From a45ac0a6c51a88b62250e02994151ea569f93c57 Mon Sep 17 00:00:00 2001 From: Titouan Rigoudy Date: Sun, 21 Dec 2014 05:43:24 -0500 Subject: [PATCH] Added consistency checks, which revealed errors. --- ya_block.c | 91 ++++++++++++++++++++++++++++++++++++++++++--------- ya_block.h | 5 +++ ya_debug.h | 5 +++ ya_freelist.c | 83 +++++++++++++++++++++++++++++++++++++++++----- ya_freelist.h | 7 ++++ yamalloc.c | 40 ++++++++++++++++------ yamalloc.h | 2 +- yatest.c | 14 ++++++++ 8 files changed, 213 insertions(+), 34 deletions(-) diff --git a/ya_block.c b/ya_block.c index 686a2bd..af645a0 100644 --- a/ya_block.c +++ b/ya_block.c @@ -61,21 +61,6 @@ static inline intptr_t inner_bytes(intptr_t *block) { /* Function definitions */ /*----------------------*/ -#ifdef YA_DEBUG -void block_print_range(intptr_t *start, intptr_t *end) { - if (!start || !end) { - return; - } - intptr_t *block; - intptr_t size; - for (block = start; block < end; block += size) { - size = block_size(block); - ya_debug("%d %p:%ld\n", - block_is_alloc(block), block, size); - } -} -#endif - /* Initializes the block's boundary tags. */ void block_init(intptr_t *block, intptr_t size) { block[-1] = size; @@ -234,3 +219,79 @@ intptr_t *heap_extend(size_t n_bytes) { ya_print_blocks(); return block; } + +#ifdef YA_DEBUG + +/* Checks one block for consistency. Does not check the free list tags. + * Returns -1 on error, 0 otherwise. */ +int block_check(intptr_t *block) { + if ((intptr_t) block & (WORD_SIZE-1)) { + ya_debug("block_check(%p): not aligned\n", block); + return -1; + } + intptr_t size = block_size(block); + if (size & 1) { + ya_debug("block_check(%p): size %ld not aligned\n", block, size); + return -1; + } + if (block[-1] != block[size-4]) { + ya_debug("block_check(%p): tags don't match %ld != %ld\n", + block, block[-1], block[size-4]); + return -1; + } + return 0; +} + +/* Checks the heap and each block for consistency. + * Does not check the free list. + * Returns -1 on error, the total number of free blocks otherwise. */ +int heap_check() { + if (!heap_start || !heap_end) { + ya_debug("heap_check: heap not initialized correctly %p-%p\n", + heap_start, heap_end); + return -1; + } + if ((intptr_t) heap_start & (WORD_SIZE-1)) { + ya_debug("heap_check: heap start %p not aligned\n", heap_start); + return -1; + } + if ((intptr_t) heap_end & (WORD_SIZE-1)) { + ya_debug("heap_check: heap end %p not aligned\n", heap_end); + return -1; + } + if ((heap_end - heap_start) & 1) { + ya_debug("heap_check: heap size %p not aligned\n", + heap_end - heap_start); + return -1; + } + int num_free = 0; + bool prev_free = false; + intptr_t *block; + for (block = heap_start; block < heap_end; block += block_size(block)) { + if (block_check(block)) { + return -1; + } + if (prev_free && !block_is_alloc(block)) { + ya_debug("heap_check: block %p and prev are both free\n", block); + return -1; + } + prev_free = !block_is_alloc(block); + num_free += prev_free; + } + return num_free; +} + +void block_print_range(intptr_t *start, intptr_t *end) { + if (!start || !end) { + return; + } + intptr_t *block; + intptr_t size; + for (block = start; block < end; block += size) { + size = block_size(block); + ya_debug("%d %p:%ld\n", + block_is_alloc(block), block, size); + } +} + +#endif //def YA_DEBUG diff --git a/ya_block.h b/ya_block.h index bcf9981..8fe7ac9 100644 --- a/ya_block.h +++ b/ya_block.h @@ -71,6 +71,11 @@ intptr_t *heap_extend(size_t n_bytes); #ifdef YA_DEBUG /* Prints each block in the range from the block at start to the one at end */ void block_print_range(intptr_t *start, intptr_t *end); + +/* Checks the heap and each block for consistency. + * Does not check the free list. + * Returns -1 on error, the total number of free blocks otherwise. */ +int heap_check(); #endif /* Initializes the block's boundary tags. */ diff --git a/ya_debug.h b/ya_debug.h index aa47843..6c73951 100644 --- a/ya_debug.h +++ b/ya_debug.h @@ -14,10 +14,15 @@ void ya_print_blocks(); +/* Checks internal state for errors. + * Returns -1 on error, 0 otherwise. */ +int ya_check(); + #else #define ya_debug(...) #define ya_print_blocks(...) +#define ya_check(...) #endif // def YA_DEBUG diff --git a/ya_freelist.c b/ya_freelist.c index e48423e..eac9e63 100644 --- a/ya_freelist.c +++ b/ya_freelist.c @@ -23,14 +23,6 @@ intptr_t *fl_end = NULL; // sorted decreasing /* Functions */ /*-----------*/ -#ifdef YA_DEBUG -void fl_debug_print() { - for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) { - ya_debug("%p:%ld\n", block, block_size(block)); - } -} -#endif - /* Splices the allocated block out of the free list. */ void fl_alloc(intptr_t *block) { intptr_t size = block_size(block); @@ -157,3 +149,78 @@ intptr_t *fl_get_start() { intptr_t *fl_get_end() { return fl_end; } + +#ifdef YA_DEBUG + +void fl_debug_print() { + for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) { + ya_debug("%p:%ld\n", block, block_size(block)); + } +} + +/* Checks that block's information is consistent. The previous pointer should + * point to correct_prev, which was the previous free block during free list + * iteration. + * Returns -1 on error, 0 otherwise. */ +int fl_check_one(intptr_t *block, intptr_t *correct_prev) { + if (block < heap_start || block >= heap_end) { + ya_debug("fl_check_one: block %p out of bounds\n", block); + return -1; + } + intptr_t *prev = fl_prev(block); + if (prev && (prev < heap_start || prev >= heap_end)) { + ya_debug("fl_check_one: previous pointer %p out of bounds [%p,%p[\n", + prev, heap_start, heap_end); + return -1; + } + if (correct_prev != prev) { + ya_debug("fl_check_one(%p): previous pointer mismatch, " + "should be %p, not %p\n", block, correct_prev, prev); + return -1; + } + intptr_t *next = fl_next(block); + if (next && (next < heap_start || next >= heap_end)) { + ya_debug("fl_check_one: next pointer %p out of bounds [%p,%p[\n", + next, heap_start, heap_end); + return -1; + } + return 0; +} + +/* Checks the free list for consistency. + * Returns -1 on error, the total number of free blocks otherwise. */ +int fl_check() { + if (!fl_start) { + if (fl_end) { + ya_debug("fl_check: fl_start == NULL but fl_end == %p\n", fl_end); + return -1; + } + return 0; + } + if (!fl_end) { + ya_debug("fl_check: fl_end == NULL but fl_start == %p\n", fl_start); + return -1; + } + if (fl_start < heap_start || fl_start >= heap_end) { + ya_debug("fl_check: fl_start %p out of bounds\n", fl_start); + return -1; + } + if (fl_end < heap_start || fl_end >= heap_end) { + ya_debug("fl_check: fl_ebd %p out of bounds\n", fl_end); + return -1; + } + int num_free = 0; + intptr_t *prev = NULL; + intptr_t *block; + for (block = fl_start; block; block = fl_next(block)) { + num_free++; + if (fl_check_one(block, prev)) { + return -1; + } + prev = block; + } + return num_free; +} + +#endif // def YA_DEBUG + diff --git a/ya_freelist.h b/ya_freelist.h index 6824d7e..ee94f3c 100644 --- a/ya_freelist.h +++ b/ya_freelist.h @@ -49,7 +49,14 @@ static inline void fl_set_next(intptr_t *block, intptr_t *next) { /*--------------*/ #ifdef YA_DEBUG + +/* Prints debug information about the free list. */ void fl_debug_print(); + +/* Checks the free list for consistency. + * Returns -1 on error, the total number of free blocks otherwise. */ +int fl_check(); + #endif /* Splices the allocated block out of the free list. */ diff --git a/yamalloc.c b/yamalloc.c index bc3a6d0..92f7643 100644 --- a/yamalloc.c +++ b/yamalloc.c @@ -16,16 +16,6 @@ /* Function definitions */ /*----------------------*/ -#ifdef YA_DEBUG -/* Print all blocks in the heap */ -void ya_print_blocks() { - ya_debug("All blocks:\n"); - block_print_range(heap_start, heap_end); - ya_debug("Free blocks:\n"); - fl_debug_print(); -} -#endif - /* Allocates enough memory to store at least size bytes. * Returns a dword-aligned pointer to the memory or NULL in case of failure. */ void *malloc(size_t n_bytes) { @@ -148,3 +138,33 @@ void *realloc(void *ptr, size_t n_bytes) { free(block); return new_block; } + +#ifdef YA_DEBUG +/* Print all blocks in the heap */ +void ya_print_blocks() { + ya_debug("All blocks:\n"); + block_print_range(heap_start, heap_end); + ya_debug("Free blocks:\n"); + fl_debug_print(); +} + +/* Checks internal state for errors. + * Returns -1 on error, 0 otherwise. */ +int ya_check() { + int heap_free = heap_check(); + if (heap_free == -1) { + return -1; + } + int fl_free = fl_check(); + if (fl_free == -1) { + return -1; + } + if (fl_free != heap_free) { + ya_debug("ya_check: heap_check reports %d free blocks, fl_check %d\n", + heap_free, fl_free); + return -1; + } + return 0; +} + +#endif diff --git a/yamalloc.h b/yamalloc.h index fd00133..ffc2bc5 100644 --- a/yamalloc.h +++ b/yamalloc.h @@ -16,4 +16,4 @@ void *calloc(size_t nmemb, size_t size); void *realloc(void *ptr, size_t size); -#endif +#endif // def YAMALLOC_H diff --git a/yatest.c b/yatest.c index d8b2586..2d77ce5 100644 --- a/yatest.c +++ b/yatest.c @@ -31,30 +31,44 @@ int main(int argc, char **argv) { ya_print_blocks(); a = print_malloc(4); ya_print_blocks(); + if (ya_check()) return -1; b = print_malloc(10); ya_print_blocks(); + if (ya_check()) return -1; c = print_malloc(10000); ya_print_blocks(); + if (ya_check()) return -1; d = print_malloc(2000); ya_print_blocks(); + if (ya_check()) return -1; print_free(a); ya_print_blocks(); + if (ya_check()) return -1; print_free(c); ya_print_blocks(); + if (ya_check()) return -1; c = print_malloc(100); ya_print_blocks(); + if (ya_check()) return -1; a = print_malloc(2); ya_print_blocks(); + if (ya_check()) return -1; print_free(d); ya_print_blocks(); + if (ya_check()) return -1; d = print_realloc(NULL, 400); ya_print_blocks(); + if (ya_check()) return -1; d = print_realloc(d, 4000); ya_print_blocks(); + if (ya_check()) return -1; d = print_realloc(d, 32000); ya_print_blocks(); + if (ya_check()) return -1; d = print_realloc(d, 1000); ya_print_blocks(); + if (ya_check()) return -1; c = print_realloc(c, 500); ya_print_blocks(); + if (ya_check()) return -1; }