add more detailed block info for heap visiting

This commit is contained in:
daan 2020-04-06 23:39:09 -07:00
parent 120af372ab
commit 173978c122
5 changed files with 89 additions and 12 deletions

View file

@ -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); bool _mi_free_delayed_block(mi_block_t* block);
void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size); void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size);
size_t _mi_path_max(void); 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); 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); void* _mi_base_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero MI_SOURCE_XPARAM) mi_attr_alloc_size(2);

View file

@ -282,7 +282,17 @@ typedef struct mi_heap_area_s {
size_t block_size; // size in bytes of each block size_t block_size; // size in bytes of each block
} mi_heap_area_t; } 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); mi_decl_export bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_all_blocks, mi_block_visit_fun* visitor, void* arg);

View file

@ -18,6 +18,7 @@ terms of the MIT license. A copy of the license can be found in the file
#include "alloc-override.c" #include "alloc-override.c"
#undef MI_IN_ALLOC_C #undef MI_IN_ALLOC_C
#define MI_PADDING_MAX_VALIDATE (4*1024) // max. bytes validated for overwrites
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Allocation // 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->delta = (uint32_t)(delta);
padding->source = __mi_source; padding->source = __mi_source;
uint8_t* fill = (uint8_t*)padding - delta; 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; } for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; }
#endif #endif
return block; 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) #if defined(MI_PADDING) && defined(MI_ENCODE_FREELIST)
static void mi_check_padding(const mi_page_t* page, const mi_block_t* block); 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); *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; *delta = padding->delta;
return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize); 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); mi_assert_internal(bsize >= delta);
*size = bsize - delta; *size = bsize - delta;
uint8_t* fill = (uint8_t*)block + 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++) { for (size_t i = 0; i < maxpad; i++) {
if (fill[i] != MI_DEBUG_PADDING) { if (fill[i] != MI_DEBUG_PADDING) {
*wrong = bsize - delta + i; *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 #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 // Free
// ------------------------------------------------------ // ------------------------------------------------------

View file

@ -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) { if (page->capacity == 1) {
// optimize page with one block // optimize page with one block
mi_assert_internal(page->used == 1 && page->free == NULL); 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. // 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) { else if ((m & ((uintptr_t)1 << bit)) == 0) {
used_count++; used_count++;
uint8_t* block = pstart + (i * bsize); mi_block_info_t info;
if (!visitor(mi_page_heap(page), area, block, bsize, arg)) return false; 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); 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.reserved = page->reserved * bsize;
xarea.area.committed = page->capacity * bsize; xarea.area.committed = page->capacity * bsize;
xarea.area.blocks = _mi_page_start(_mi_page_segment(page), page, NULL); 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; xarea.area.block_size = bsize;
return fun(heap, &xarea, arg); 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) { 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; 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) { if (args->visit_blocks) {
return mi_heap_area_visit_blocks(xarea, args->visitor, args->arg); return mi_heap_area_visit_blocks(xarea, args->visitor, args->arg);
} }

View file

@ -7,6 +7,44 @@
#include <mimalloc.h> #include <mimalloc.h>
#include <mimalloc-override.h> // redefines malloc etc. #include <mimalloc-override.h> // 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_free1();
static void double_free2(); static void double_free2();
static void corrupt_free(); static void corrupt_free();
@ -22,7 +60,7 @@ int main() {
// double_free2(); // double_free2();
// corrupt_free(); // corrupt_free();
// block_overflow1(); // block_overflow1();
block_overflow2(); // block_overflow2();
// dangling_ptr_write(); // dangling_ptr_write();
void* p1 = malloc(78); void* p1 = malloc(78);
@ -52,7 +90,6 @@ static void block_overflow1() {
p[18] = 0; p[18] = 0;
free(p); free(p);
} }
static void block_overflow2() { static void block_overflow2() {
void* p[100]; void* p[100];
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {