diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2014-01-09 21:37:07 +0100 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2014-01-16 10:17:39 +0100 |
commit | 7ab237b025cbb4c25d345604da32e894379c1721 (patch) | |
tree | 92d147029519c55d57db4273066d57b960ad7158 /firmware | |
parent | 7f5dce4116bf4e62e0cd3ef16730157e17625e1c (diff) | |
download | rockbox-7ab237b025cbb4c25d345604da32e894379c1721.tar.gz rockbox-7ab237b025cbb4c25d345604da32e894379c1721.zip |
buflib: Add crc field protecting buflib cookie integrity
This should catch the case of buffer misuse which results
in corrupted cookie of next allocation. The check is performed
on move_block() so it may be a bit late.
There is buflib_check_valid() provided which checks the
integrity of all cookies for given context.
On DEBUG build with --sdl-thread this check is carried out
for core_ctx on every context switch to catch problems earlier.
Change-Id: I999d4576084592394e3dbd3bdf0f32935ff5f601
Reviewed-on: http://gerrit.rockbox.org/711
Reviewed-by: Thomas Martitz <kugel@rockbox.org>
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/buflib.c | 72 | ||||
-rw-r--r-- | firmware/common/crc32.c | 4 | ||||
-rw-r--r-- | firmware/core_alloc.c | 7 | ||||
-rw-r--r-- | firmware/include/buflib.h | 6 | ||||
-rw-r--r-- | firmware/include/core_alloc.h | 3 | ||||
-rw-r--r-- | firmware/include/crc32.h | 4 | ||||
-rw-r--r-- | firmware/target/hosted/sdl/thread-sdl.c | 4 | ||||
-rw-r--r-- | firmware/thread.c | 6 |
8 files changed, 94 insertions, 12 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c index db09d3efc9..0a87a4c4d8 100644 --- a/firmware/buflib.c +++ b/firmware/buflib.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include "buflib.h" | 31 | #include "buflib.h" |
32 | #include "string-extra.h" /* strlcpy() */ | 32 | #include "string-extra.h" /* strlcpy() */ |
33 | #include "debug.h" | 33 | #include "debug.h" |
34 | #include "panic.h" | ||
35 | #include "crc32.h" | ||
34 | #include "system.h" /* for ALIGN_*() */ | 36 | #include "system.h" /* for ALIGN_*() */ |
35 | 37 | ||
36 | /* The main goal of this design is fast fetching of the pointer for a handle. | 38 | /* The main goal of this design is fast fetching of the pointer for a handle. |
@@ -54,13 +56,14 @@ | |||
54 | * | 56 | * |
55 | * Example: | 57 | * Example: |
56 | * |<- alloc block #1 ->|<- unalloc block ->|<- alloc block #2 ->|<-handle table->| | 58 | * |<- alloc block #1 ->|<- unalloc block ->|<- alloc block #2 ->|<-handle table->| |
57 | * |L|H|C|cccc|L2|XXXXXX|-L|YYYYYYYYYYYYYYYY|L|H|C|cc|L2|XXXXXXXXXXXXX|AAA| | 59 | * |L|H|C|cccc|L2|crc|XXXXXX|-L|YYYYYYYYYYYYYYYY|L|H|C|cc|L2|crc|XXXXXXXXXXXXX|AAA| |
58 | * | 60 | * |
59 | * L - length marker (negative if block unallocated) | 61 | * L - length marker (negative if block unallocated) |
60 | * H - handle table enry pointer | 62 | * H - handle table enry pointer |
61 | * C - pointer to struct buflib_callbacks | 63 | * C - pointer to struct buflib_callbacks |
62 | * c - variable sized string identifier | 64 | * c - variable sized string identifier |
63 | * L2 - second length marker for string identifier | 65 | * L2 - second length marker for string identifier |
66 | * crc - crc32 protecting buflib cookie integrity | ||
64 | * X - actual payload | 67 | * X - actual payload |
65 | * Y - unallocated space | 68 | * Y - unallocated space |
66 | * | 69 | * |
@@ -224,8 +227,17 @@ static bool | |||
224 | move_block(struct buflib_context* ctx, union buflib_data* block, int shift) | 227 | move_block(struct buflib_context* ctx, union buflib_data* block, int shift) |
225 | { | 228 | { |
226 | char* new_start; | 229 | char* new_start; |
227 | union buflib_data *new_block, *tmp = block[1].handle; | 230 | union buflib_data *new_block, *tmp = block[1].handle, *crc_slot; |
228 | struct buflib_callbacks *ops = block[2].ops; | 231 | struct buflib_callbacks *ops = block[2].ops; |
232 | crc_slot = (union buflib_data*)tmp->alloc - 1; | ||
233 | int cookie_size = (crc_slot - block)*sizeof(union buflib_data); | ||
234 | uint32_t crc = crc_32((void *)block, cookie_size, 0xffffffff); | ||
235 | |||
236 | /* check for cookie validity */ | ||
237 | if (crc != crc_slot->crc) | ||
238 | panicf("buflib cookie corrupted, crc: 0x%08x, expected: 0x%08x", | ||
239 | (unsigned int)crc, (unsigned int)crc_slot->crc); | ||
240 | |||
229 | if (!IS_MOVABLE(block)) | 241 | if (!IS_MOVABLE(block)) |
230 | return false; | 242 | return false; |
231 | 243 | ||
@@ -299,6 +311,7 @@ buflib_compact(struct buflib_context *ctx) | |||
299 | ret = true; | 311 | ret = true; |
300 | /* Move was successful. The memory at block is now free */ | 312 | /* Move was successful. The memory at block is now free */ |
301 | block->val = -len; | 313 | block->val = -len; |
314 | |||
302 | /* add its length to shift */ | 315 | /* add its length to shift */ |
303 | shift += -len; | 316 | shift += -len; |
304 | /* Reduce the size of the hole accordingly | 317 | /* Reduce the size of the hole accordingly |
@@ -474,9 +487,9 @@ buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name, | |||
474 | size += name_len; | 487 | size += name_len; |
475 | size = (size + sizeof(union buflib_data) - 1) / | 488 | size = (size + sizeof(union buflib_data) - 1) / |
476 | sizeof(union buflib_data) | 489 | sizeof(union buflib_data) |
477 | /* add 4 objects for alloc len, pointer to handle table entry and | 490 | /* add 5 objects for alloc len, pointer to handle table entry and |
478 | * name length, and the ops pointer */ | 491 | * name length, the ops pointer and crc */ |
479 | + 4; | 492 | + 5; |
480 | handle_alloc: | 493 | handle_alloc: |
481 | handle = handle_alloc(ctx); | 494 | handle = handle_alloc(ctx); |
482 | if (!handle) | 495 | if (!handle) |
@@ -555,14 +568,22 @@ buffer_alloc: | |||
555 | /* Set up the allocated block, by marking the size allocated, and storing | 568 | /* Set up the allocated block, by marking the size allocated, and storing |
556 | * a pointer to the handle. | 569 | * a pointer to the handle. |
557 | */ | 570 | */ |
558 | union buflib_data *name_len_slot; | 571 | union buflib_data *name_len_slot, *crc_slot; |
559 | block->val = size; | 572 | block->val = size; |
560 | block[1].handle = handle; | 573 | block[1].handle = handle; |
561 | block[2].ops = ops; | 574 | block[2].ops = ops; |
562 | strcpy(block[3].name, name); | 575 | strcpy(block[3].name, name); |
563 | name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len); | 576 | name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len); |
564 | name_len_slot->val = 1 + name_len/sizeof(union buflib_data); | 577 | name_len_slot->val = 1 + name_len/sizeof(union buflib_data); |
565 | handle->alloc = (char*)(name_len_slot + 1); | 578 | crc_slot = (union buflib_data*)(name_len_slot + 1); |
579 | crc_slot->crc = crc_32((void *)block, | ||
580 | (crc_slot - block)*sizeof(union buflib_data), | ||
581 | 0xffffffff); | ||
582 | handle->alloc = (char*)(crc_slot + 1); | ||
583 | |||
584 | BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n", | ||
585 | (unsigned int)size, (void *)handle, (void *)ops, | ||
586 | (unsigned int)crc_slot->crc, block[3].name); | ||
566 | 587 | ||
567 | block += size; | 588 | block += size; |
568 | /* alloc_end must be kept current if we're taking the last block. */ | 589 | /* alloc_end must be kept current if we're taking the last block. */ |
@@ -778,6 +799,8 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size, | |||
778 | bool | 799 | bool |
779 | buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) | 800 | buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) |
780 | { | 801 | { |
802 | union buflib_data *crc_slot; | ||
803 | int cookie_size; | ||
781 | char* oldstart = buflib_get_data(ctx, handle); | 804 | char* oldstart = buflib_get_data(ctx, handle); |
782 | char* newstart = new_start; | 805 | char* newstart = new_start; |
783 | char* newend = newstart + new_size; | 806 | char* newend = newstart + new_size; |
@@ -823,6 +846,11 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne | |||
823 | block = new_block; | 846 | block = new_block; |
824 | } | 847 | } |
825 | 848 | ||
849 | /* update crc of the cookie */ | ||
850 | crc_slot = (union buflib_data*)new_block[1].handle->alloc - 1; | ||
851 | cookie_size = (crc_slot - new_block)*sizeof(union buflib_data); | ||
852 | crc_slot->crc = crc_32((void *)new_block, cookie_size, 0xffffffff); | ||
853 | |||
826 | /* Now deal with size changes that create free blocks after the allocation */ | 854 | /* Now deal with size changes that create free blocks after the allocation */ |
827 | if (old_next_block != new_next_block) | 855 | if (old_next_block != new_next_block) |
828 | { | 856 | { |
@@ -847,11 +875,37 @@ const char* buflib_get_name(struct buflib_context *ctx, int handle) | |||
847 | union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data)); | 875 | union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data)); |
848 | if (!data) | 876 | if (!data) |
849 | return NULL; | 877 | return NULL; |
850 | size_t len = data[-1].val; | 878 | size_t len = data[-2].val; |
851 | if (len <= 1) | 879 | if (len <= 1) |
852 | return NULL; | 880 | return NULL; |
853 | return data[-len].name; | 881 | return data[-len-1].name; |
882 | } | ||
883 | |||
884 | #ifdef DEBUG | ||
885 | void buflib_check_valid(struct buflib_context *ctx) | ||
886 | { | ||
887 | union buflib_data *crc_slot; | ||
888 | int cookie_size; | ||
889 | uint32_t crc; | ||
890 | |||
891 | for(union buflib_data* this = ctx->buf_start; | ||
892 | this < ctx->alloc_end; | ||
893 | this += abs(this->val)) | ||
894 | { | ||
895 | if (this->val < 0) | ||
896 | continue; | ||
897 | |||
898 | crc_slot = (union buflib_data*) | ||
899 | ((union buflib_data*)this[1].handle)->alloc - 1; | ||
900 | cookie_size = (crc_slot - this)*sizeof(union buflib_data); | ||
901 | crc = crc_32((void *)this, cookie_size, 0xffffffff); | ||
902 | |||
903 | if (crc != crc_slot->crc) | ||
904 | panicf("buflib check crc: 0x%08x, expected: 0x%08x", | ||
905 | (unsigned int)crc, (unsigned int)crc_slot->crc); | ||
906 | } | ||
854 | } | 907 | } |
908 | #endif | ||
855 | 909 | ||
856 | #ifdef BUFLIB_DEBUG_BLOCKS | 910 | #ifdef BUFLIB_DEBUG_BLOCKS |
857 | void buflib_print_allocs(struct buflib_context *ctx, | 911 | void buflib_print_allocs(struct buflib_context *ctx, |
diff --git a/firmware/common/crc32.c b/firmware/common/crc32.c index 1cd0ca0bd5..c8c70e415c 100644 --- a/firmware/common/crc32.c +++ b/firmware/common/crc32.c | |||
@@ -25,7 +25,7 @@ | |||
25 | 25 | ||
26 | /* Tool function to calculate a CRC32 across some buffer */ | 26 | /* Tool function to calculate a CRC32 across some buffer */ |
27 | /* third argument is either 0xFFFFFFFF to start or value from last piece */ | 27 | /* third argument is either 0xFFFFFFFF to start or value from last piece */ |
28 | unsigned crc_32(const void *src, unsigned len, unsigned crc32) | 28 | uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32) |
29 | { | 29 | { |
30 | const unsigned char *buf = (const unsigned char *)src; | 30 | const unsigned char *buf = (const unsigned char *)src; |
31 | 31 | ||
@@ -39,7 +39,7 @@ unsigned crc_32(const void *src, unsigned len, unsigned crc32) | |||
39 | }; | 39 | }; |
40 | 40 | ||
41 | unsigned char byte; | 41 | unsigned char byte; |
42 | unsigned t; | 42 | uint32_t t; |
43 | 43 | ||
44 | while (len--) | 44 | while (len--) |
45 | { | 45 | { |
diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c index aa662fbee5..e9f9795917 100644 --- a/firmware/core_alloc.c +++ b/firmware/core_alloc.c | |||
@@ -96,3 +96,10 @@ void core_print_block_at(int block_num, char* buf, size_t bufsize) | |||
96 | { | 96 | { |
97 | buflib_print_block_at(&core_ctx, block_num, buf, bufsize); | 97 | buflib_print_block_at(&core_ctx, block_num, buf, bufsize); |
98 | } | 98 | } |
99 | |||
100 | #ifdef DEBUG | ||
101 | void core_check_valid(void) | ||
102 | { | ||
103 | buflib_check_valid(&core_ctx); | ||
104 | } | ||
105 | #endif | ||
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h index 0b26c04bcd..171ab5bcd7 100644 --- a/firmware/include/buflib.h +++ b/firmware/include/buflib.h | |||
@@ -40,6 +40,7 @@ union buflib_data | |||
40 | struct buflib_callbacks* ops; | 40 | struct buflib_callbacks* ops; |
41 | char* alloc; | 41 | char* alloc; |
42 | union buflib_data *handle; | 42 | union buflib_data *handle; |
43 | uint32_t crc; | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | struct buflib_context | 46 | struct buflib_context |
@@ -346,4 +347,9 @@ int buflib_get_num_blocks(struct buflib_context *ctx); | |||
346 | */ | 347 | */ |
347 | void buflib_print_block_at(struct buflib_context *ctx, int block_num, | 348 | void buflib_print_block_at(struct buflib_context *ctx, int block_num, |
348 | char* buf, size_t bufsize); | 349 | char* buf, size_t bufsize); |
350 | |||
351 | /** | ||
352 | * Check integrity of given buflib context | ||
353 | */ | ||
354 | void buflib_check_valid(struct buflib_context *ctx); | ||
349 | #endif | 355 | #endif |
diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h index a100b7cc6c..095cb5da11 100644 --- a/firmware/include/core_alloc.h +++ b/firmware/include/core_alloc.h | |||
@@ -17,6 +17,9 @@ bool core_shrink(int handle, void* new_start, size_t new_size); | |||
17 | int core_free(int handle); | 17 | int core_free(int handle); |
18 | size_t core_available(void); | 18 | size_t core_available(void); |
19 | size_t core_allocatable(void); | 19 | size_t core_allocatable(void); |
20 | #ifdef DEBUG | ||
21 | void core_check_valid(void); | ||
22 | #endif | ||
20 | 23 | ||
21 | /* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call | 24 | /* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call |
22 | * the move callbacks and are therefore unsafe in the core */ | 25 | * the move callbacks and are therefore unsafe in the core */ |
diff --git a/firmware/include/crc32.h b/firmware/include/crc32.h index 034c3984ab..8e1f868988 100644 --- a/firmware/include/crc32.h +++ b/firmware/include/crc32.h | |||
@@ -18,10 +18,12 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include <stdint.h> | ||
22 | |||
21 | #ifndef _CRC32_H | 23 | #ifndef _CRC32_H |
22 | #define _CRC32_H | 24 | #define _CRC32_H |
23 | 25 | ||
24 | unsigned crc_32(const void *src, unsigned len, unsigned crc32); | 26 | uint32_t crc_32(const void *src, uint32_t len, uint32_t crc32); |
25 | 27 | ||
26 | #endif | 28 | #endif |
27 | 29 | ||
diff --git a/firmware/target/hosted/sdl/thread-sdl.c b/firmware/target/hosted/sdl/thread-sdl.c index eaffa86aee..fbc26c8a9f 100644 --- a/firmware/target/hosted/sdl/thread-sdl.c +++ b/firmware/target/hosted/sdl/thread-sdl.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "kernel.h" | 32 | #include "kernel.h" |
33 | #include "thread.h" | 33 | #include "thread.h" |
34 | #include "debug.h" | 34 | #include "debug.h" |
35 | #include "core_alloc.h" | ||
35 | 36 | ||
36 | /* Define this as 1 to show informational messages that are not errors. */ | 37 | /* Define this as 1 to show informational messages that are not errors. */ |
37 | #define THREAD_SDL_DEBUGF_ENABLED 0 | 38 | #define THREAD_SDL_DEBUGF_ENABLED 0 |
@@ -382,6 +383,9 @@ void switch_thread(void) | |||
382 | } /* STATE_SLEEPING: */ | 383 | } /* STATE_SLEEPING: */ |
383 | } | 384 | } |
384 | 385 | ||
386 | #ifdef DEBUG | ||
387 | core_check_valid(); | ||
388 | #endif | ||
385 | cores[CURRENT_CORE].running = current; | 389 | cores[CURRENT_CORE].running = current; |
386 | 390 | ||
387 | if (threads_status != THREADS_RUN) | 391 | if (threads_status != THREADS_RUN) |
diff --git a/firmware/thread.c b/firmware/thread.c index ce9252ccc6..b687144f4f 100644 --- a/firmware/thread.c +++ b/firmware/thread.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #ifdef RB_PROFILE | 39 | #ifdef RB_PROFILE |
40 | #include <profile.h> | 40 | #include <profile.h> |
41 | #endif | 41 | #endif |
42 | #include "core_alloc.h" | ||
42 | #include "gcc_extensions.h" | 43 | #include "gcc_extensions.h" |
43 | 44 | ||
44 | /**************************************************************************** | 45 | /**************************************************************************** |
@@ -1161,6 +1162,11 @@ void switch_thread(void) | |||
1161 | * to this call. */ | 1162 | * to this call. */ |
1162 | store_context(&thread->context); | 1163 | store_context(&thread->context); |
1163 | 1164 | ||
1165 | #ifdef DEBUG | ||
1166 | /* Check core_ctx buflib integrity */ | ||
1167 | core_check_valid(); | ||
1168 | #endif | ||
1169 | |||
1164 | /* Check if the current thread stack is overflown */ | 1170 | /* Check if the current thread stack is overflown */ |
1165 | if (UNLIKELY(thread->stack[0] != DEADBEEF) && thread->stack_size > 0) | 1171 | if (UNLIKELY(thread->stack[0] != DEADBEEF) && thread->stack_size > 0) |
1166 | thread_stkov(thread); | 1172 | thread_stkov(thread); |