diff --git a/include/mimalloc-internal.h b/include/mimalloc-internal.h index 90bf6f82..feb3abae 100644 --- a/include/mimalloc-internal.h +++ b/include/mimalloc-internal.h @@ -148,6 +148,7 @@ mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* p bool _mi_free_delayed_block(mi_block_t* block); void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size); size_t _mi_path_max(void); +void _mi_page_block_info(const mi_page_t* page, const mi_block_t* block, mi_block_info_t* info); mi_decl_restrict void* _mi_base_malloc_zero(mi_heap_t* heap, size_t size, bool zero MI_SOURCE_XPARAM) mi_attr_malloc mi_attr_alloc_size(2); void* _mi_base_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero MI_SOURCE_XPARAM) mi_attr_alloc_size(2); diff --git a/include/mimalloc.h b/include/mimalloc.h index f79a4ff6..876ee89b 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -282,7 +282,17 @@ typedef struct mi_heap_area_s { size_t block_size; // size in bytes of each block } mi_heap_area_t; -typedef bool (mi_cdecl mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg); +// Information about a block +typedef struct mi_block_info_s { + void* block; // start of the block + size_t size; // full size including padding etc. + size_t usable_size; // usable size (available for in-place realloc) + size_t allocated_size; // actual allocated size (only precise in debug mode with padding) + bool valid; // is the block valid? (only detects corrupt blocks with padding enabled) + mi_source_t source; // the source location that allocated this block (only valid in debug mode with padding) +} mi_block_info_t; + +typedef bool (mi_cdecl mi_block_visit_fun)(const mi_heap_t* heap, const mi_heap_area_t* area, const mi_block_info_t* block_info, void* arg); mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg); diff --git a/src/alloc.c b/src/alloc.c index ba0a5b5d..15c6e12f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -18,6 +18,7 @@ terms of the MIT license. A copy of the license can be found in the file #include "alloc-override.c" #undef MI_IN_ALLOC_C +#define MI_PADDING_MAX_VALIDATE (4*1024) // max. bytes validated for overwrites // ---------------------------------------------------------------------------------------- // Allocation @@ -58,7 +59,7 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz padding->delta = (uint32_t)(delta); padding->source = __mi_source; uint8_t* fill = (uint8_t*)padding - delta; - const size_t maxpad = (delta > 4096 ? 4096 : delta); // set at most N initial padding bytes + const size_t maxpad = (delta > MI_PADDING_MAX_VALIDATE ? MI_PADDING_MAX_VALIDATE : delta); // set at most N initial padding bytes for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; } #endif return block; @@ -211,9 +212,13 @@ static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block #if defined(MI_PADDING) && defined(MI_ENCODE_FREELIST) static void mi_check_padding(const mi_page_t* page, const mi_block_t* block); -static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) { +static const mi_padding_t* mi_page_get_padding(const mi_page_t* page, const mi_block_t* block, size_t* bsize) { *bsize = mi_page_usable_block_size(page); - const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize); + return (mi_padding_t*)((uint8_t*)block + *bsize); +} + +static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) { + const mi_padding_t* const padding = mi_page_get_padding(page, block, bsize); *delta = padding->delta; return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize); } @@ -237,7 +242,7 @@ static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, si mi_assert_internal(bsize >= delta); *size = bsize - delta; uint8_t* fill = (uint8_t*)block + bsize - delta; - const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes + const size_t maxpad = (delta > MI_PADDING_MAX_VALIDATE ? MI_PADDING_MAX_VALIDATE : delta); // check at most the first N padding bytes for (size_t i = 0; i < maxpad; i++) { if (fill[i] != MI_DEBUG_PADDING) { *wrong = bsize - delta + i; @@ -323,6 +328,25 @@ static void mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, co } #endif +void _mi_page_block_info(const mi_page_t* page, const mi_block_t* block, mi_block_info_t* info) { + mi_assert_internal(page!=NULL); + mi_assert_internal(block!=NULL); + mi_assert_internal(info!=NULL); + memset(info, 0, sizeof(*info)); + info->block = (void*)block; + info->size = mi_page_block_size(page); +#if MI_PADDING + const mi_padding_t* padding = mi_page_get_padding(page, block, &info->usable_size); + info->source = padding->source; + size_t wrong; + info->valid = mi_verify_padding(page, block, &info->allocated_size, &wrong); +#else + info->usable_size = mi_usable_size(block); + info->allocated_size = info->usable_size; +#endif +} + + // ------------------------------------------------------ // Free // ------------------------------------------------------ diff --git a/src/heap.c b/src/heap.c index 5d0d4b8a..ec22889b 100644 --- a/src/heap.c +++ b/src/heap.c @@ -468,7 +468,10 @@ static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_v if (page->capacity == 1) { // optimize page with one block mi_assert_internal(page->used == 1 && page->free == NULL); - return visitor(mi_page_heap(page), area, pstart, bsize, arg); + mi_block_info_t info; + mi_block_t* block = (mi_block_t*)(pstart); + _mi_page_block_info(page, block, &info); + return visitor(mi_page_heap(page), area, &info, arg); } // create a bitmap of free blocks. @@ -501,8 +504,10 @@ static bool mi_heap_area_visit_blocks(const mi_heap_area_ex_t* xarea, mi_block_v } else if ((m & ((uintptr_t)1 << bit)) == 0) { used_count++; - uint8_t* block = pstart + (i * bsize); - if (!visitor(mi_page_heap(page), area, block, bsize, arg)) return false; + mi_block_info_t info; + mi_block_t* block = (mi_block_t*)(pstart + (i * bsize)); + _mi_page_block_info(page, block, &info); + if (!visitor(mi_page_heap(page), area, &info, arg)) return false; } } mi_assert_internal(page->used == used_count); @@ -522,7 +527,7 @@ static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_pa xarea.area.reserved = page->reserved * bsize; xarea.area.committed = page->capacity * bsize; xarea.area.blocks = _mi_page_start(_mi_page_segment(page), page, NULL); - xarea.area.used = page->used; + xarea.area.used = page->used * bsize; xarea.area.block_size = bsize; return fun(heap, &xarea, arg); } @@ -542,7 +547,7 @@ typedef struct mi_visit_blocks_args_s { static bool mi_heap_area_visitor(const mi_heap_t* heap, const mi_heap_area_ex_t* xarea, void* arg) { mi_visit_blocks_args_t* args = (mi_visit_blocks_args_t*)arg; - if (!args->visitor(heap, &xarea->area, NULL, xarea->area.block_size, args->arg)) return false; + if (!args->visitor(heap, &xarea->area, NULL, args->arg)) return false; if (args->visit_blocks) { return mi_heap_area_visit_blocks(xarea, args->visitor, args->arg); } diff --git a/test/main-override-static.c b/test/main-override-static.c index 2082c024..0b5b9bf2 100644 --- a/test/main-override-static.c +++ b/test/main-override-static.c @@ -7,6 +7,44 @@ #include #include // redefines malloc etc. + +typedef struct mi_visit_info_s { + size_t area_count; + size_t block_count; +} mi_visit_info_t; + +static bool visit(const mi_heap_t* heap, const mi_heap_area_t* area, const mi_block_info_t* info, void* arg) { + mi_visit_info_t* varg = (mi_visit_info_t*)(arg); + if (info==NULL) { + printf(varg->area_count==0 ? " {" : " ]\n}\n,{"); + varg->area_count++; + varg->block_count = 0; + printf("\"area\": %zu, \"start\": 0x%p, \"block_size\": %zu, \"used_size\": %zu,\n \"reserved\": %zu, \"committed\": %zu,", varg->area_count, area->blocks, area->block_size, area->used, area->reserved, area->committed); + printf(" \"blocks\": [\n"); + } + else { + printf(varg->block_count==0 ? " {" : " ,{"); + varg->block_count++; + printf("\"block\": 0x%p, \"valid\": %s, \"size\": %zu, \"usable_size\": %zu, \"allocated_size\": %zu,\n ", info->block, info->valid ? "true" : "false", info->size, info->usable_size, info->allocated_size); + int lineno; + const char* fname; + void* ret = mi_source_unpack(info->source, &fname, &lineno); + if (fname!=NULL) printf("\"source\": \"%s:%i\" }\n", fname, lineno); + else if (ret != NULL) printf("\"source\": \"(%p)\" }\n", ret); + else printf("\"source\": \"\" }\n"); + } +} + +static void mi_heap_to_json(mi_heap_t* heap) { + if (heap==NULL) heap = mi_heap_get_default(); + mi_visit_info_t info = { 0, 0 }; + printf("[\n"); + mi_heap_visit_blocks(heap, true, &visit, &info); + printf(info.area_count==0 ? "]\n" : " ] }\n]\n"); +} + + + static void double_free1(); static void double_free2(); static void corrupt_free(); @@ -22,7 +60,7 @@ int main() { // double_free2(); // corrupt_free(); // block_overflow1(); - block_overflow2(); + // block_overflow2(); // dangling_ptr_write(); void* p1 = malloc(78); @@ -52,7 +90,6 @@ static void block_overflow1() { p[18] = 0; free(p); } - static void block_overflow2() { void* p[100]; for (int i = 0; i < 100; i++) {