Hi, I wanted an extension to malloc() that would report the caller of all memory leaks. It works fine for me!
ok asou@ -- ASOU Masato From: Otto Moerbeek <o...@drijf.net> Date: Tue, 10 Oct 2023 12:39:00 +0200 > Hi, > > This diff adds better error reporting for write-after-free or the more > general write of free memory if malloc option D is active. Knowing the > place where allocations were done often helps to find out where the > overwrite happened. > > If option D is active malloc now saves caller info in a separate page > instead of only doing that for chunk index 0. It also reports about > the preceding chunk if applicable. A report looks like this: > > X(12489) in calloc(): write to free chunk 0x851ec6066d0[0..7]@16 > allocated at /usr/X11R6/lib/modules/dri/radeonsi_dri.so 0x88c949 > (preceding chunk 0x851ec6066c0 allocated at /usr/X11R6/bin/X 0x177374 (now > free)) > > You can use addr2line -e to get the file and linenumber the allocation > was done (if the object was compiled with debug info). > > If D is not used only the first part is printed, as no caller info is > saved. So no extra overhead if D is not useed. > > Also: the leak report now do not contain unknown locations anymore, as > each allocation gets its caller recorded if D is active. > > -Otto > > Index: stdlib/malloc.3 > =================================================================== > RCS file: /home/cvs/src/lib/libc/stdlib/malloc.3,v > retrieving revision 1.137 > diff -u -p -r1.137 malloc.3 > --- stdlib/malloc.3 1 Jul 2023 18:35:14 -0000 1.137 > +++ stdlib/malloc.3 10 Oct 2023 10:23:19 -0000 > @@ -307,7 +307,7 @@ These malloc options imply > .Cm D . > .It Cm F > .Dq Freecheck . > -Enable more extensive double free and use after free detection. > +Enable more extensive double free and write after free detection. > All chunks in the delayed free list will be checked for double frees and > write after frees. > Unused pages on the freelist are read and write protected to > @@ -641,18 +641,34 @@ or > reallocate an unallocated pointer was made. > .It Dq double free > There was an attempt to free an allocation that had already been freed. > -.It Dq write after free > -An allocation has been modified after it was freed. > +.It Dq write of free mem Va address Ns [ Va start Ns .. Ns Va end Ns ]@ Ns > Va size > +An allocation has been modified after it was freed, > +or a chunk that was never allocated was written to. > +The > +.Va range > +at which corruption was detected is printed between [ and ]. > +.Pp > +Enabling option > +.Cm D > +allows malloc to print information about where the allocation > +was done. > .It Dq modified chunk-pointer > The pointer passed to > .Fn free > or a reallocation function has been modified. > -.It Dq canary corrupted address offset@length > -A byte after the requested size has been overwritten, > +.It Dq canary corrupted Va address Ns [ Va offset Ns ]@ Ns Va length Ns / Ns > Va size > +A byte after the requested > +.Va length has been overwritten, > indicating a heap overflow. > -The offset at which corruption was detected is printed before the @, > -and the requested length of the allocation after the @. > -.It Dq recorded size oldsize inconsistent with size > +The > +.Va offset > +at which corruption was detected is printed between [ and ], > +the requested > +.Va length > +of the allocation is printed before the / and the > +.Va size > +of the allocation after the /. > +.It Dq recorded size Va oldsize No inconsistent with Va size > .Fn recallocarray > or > .Fn freezero > @@ -676,7 +692,7 @@ functions nor utilize any other function > (e.g., > .Xr stdio 3 > routines). > -.It Dq unknown char in MALLOC_OPTIONS > +.It Dq unknown char in Ev MALLOC_OPTIONS > We found something we didn't understand. > .It any other error > .Fn malloc > Index: stdlib/malloc.c > =================================================================== > RCS file: /home/cvs/src/lib/libc/stdlib/malloc.c,v > retrieving revision 1.290 > diff -u -p -r1.290 malloc.c > --- stdlib/malloc.c 9 Sep 2023 06:52:40 -0000 1.290 > +++ stdlib/malloc.c 10 Oct 2023 10:23:19 -0000 > @@ -112,7 +112,7 @@ struct region_info { > void *p; /* page; low bits used to mark chunks */ > uintptr_t size; /* size for pages, or chunk_info pointer */ > #ifdef MALLOC_STATS > - void *f; /* where allocated from */ > + void **f; /* where allocated from */ > #endif > }; > > @@ -146,7 +146,7 @@ struct dir_info { > size_t regions_total; /* number of region slots */ > size_t regions_free; /* number of free slots */ > size_t rbytesused; /* random bytes used */ > - char *func; /* current function */ > + const char *func; /* current function */ > int malloc_junk; /* junk fill? */ > int mmap_flag; /* extra flag for mmap */ > int mutex; > @@ -166,6 +166,7 @@ struct dir_info { > void *chunk_pages; > size_t chunk_pages_used; > #ifdef MALLOC_STATS > + void *caller; > size_t inserts; > size_t insert_collisions; > size_t finds; > @@ -183,12 +184,16 @@ struct dir_info { > #define STATS_INC(x) ((x)++) > #define STATS_ZERO(x) ((x) = 0) > #define STATS_SETF(x,y) ((x)->f = (y)) > +#define STATS_SETFN(x,k,y) ((x)->f[k] = (y)) > +#define SET_CALLER(x,y) if (DO_STATS) ((x)->caller = (y)) > #else > #define STATS_ADD(x,y) /* nothing */ > #define STATS_SUB(x,y) /* nothing */ > #define STATS_INC(x) /* nothing */ > #define STATS_ZERO(x) /* nothing */ > #define STATS_SETF(x,y) /* nothing */ > +#define STATS_SETFN(x,k,y) /* nothing */ > +#define SET_CALLER(x,y) /* nothing */ > #endif /* MALLOC_STATS */ > u_int32_t canary2; > }; > @@ -212,6 +217,8 @@ struct chunk_info { > u_short bits[1]; /* which chunks are free */ > }; > > +#define CHUNK_FREE(i, n) ((i)->bits[(n) / MALLOC_BITS] & (1U << ((n) % > MALLOC_BITS))) > + > struct malloc_readonly { > /* Main bookkeeping information */ > struct dir_info *malloc_pool[_MALLOC_MUTEXES]; > @@ -227,7 +234,7 @@ struct malloc_readonly { > u_int junk_loc; /* variation in location of junk */ > size_t malloc_guard; /* use guard pages after allocations? */ > #ifdef MALLOC_STATS > - int malloc_stats; /* dump leak report at end */ > + int malloc_stats; /* save callers, dump leak report at > end */ > int malloc_verbose; /* dump verbose statistics at end */ > #define DO_STATS mopts.malloc_stats > #else > @@ -254,6 +261,7 @@ static __dead void wrterror(struct dir_i > void malloc_dump(void); > PROTO_NORMAL(malloc_dump); > static void malloc_exit(void); > +static void print_chunk_details(struct dir_info *, void *, size_t, size_t); > #endif > > #if defined(__aarch64__) || \ > @@ -714,14 +722,14 @@ junk_free(int junk, void *p, size_t sz) > } > > static inline void > -validate_junk(struct dir_info *pool, void *p, size_t sz) > +validate_junk(struct dir_info *pool, void *p, size_t argsz) > { > - size_t i, step = 1; > + size_t i, sz, step = 1; > uint64_t *lp = p; > > - if (pool->malloc_junk == 0 || sz == 0) > + if (pool->malloc_junk == 0 || argsz == 0) > return; > - sz /= sizeof(uint64_t); > + sz = argsz / sizeof(uint64_t); > if (pool->malloc_junk == 1) { > if (sz > MALLOC_PAGESIZE / sizeof(uint64_t)) > sz = MALLOC_PAGESIZE / sizeof(uint64_t); > @@ -731,8 +739,17 @@ validate_junk(struct dir_info *pool, voi > } > /* see junk_free */ > for (i = mopts.junk_loc % step; i < sz; i += step) { > - if (lp[i] != SOME_FREEJUNK_ULL) > - wrterror(pool, "write after free %p", p); > + if (lp[i] != SOME_FREEJUNK_ULL) { > +#ifdef MALLOC_STATS > + if (DO_STATS && argsz <= MALLOC_MAXCHUNK) > + print_chunk_details(pool, lp, argsz, i); > + else > +#endif > + wrterror(pool, > + "write to free mem %p[%zu..%zu]@%zu", > + lp, i * sizeof(uint64_t), > + (i + 1) * sizeof(uint64_t) - 1, argsz); > + } > } > } > > @@ -809,7 +826,7 @@ unmap(struct dir_info *d, void *p, size_ > i = getrbyte(d) & (cache->max - 1); > r = cache->pages[i]; > fresh = (uintptr_t)r & 1; > - *(uintptr_t*)&r &= ~1ULL; > + *(uintptr_t*)&r &= ~1UL; > if (!fresh && !mopts.malloc_freeunmap) > validate_junk(d, r, sz); > if (munmap(r, sz)) > @@ -995,11 +1012,18 @@ omalloc_make_chunks(struct dir_info *d, > { > struct chunk_info *bp; > void *pp; > + void *ff = NULL; > > /* Allocate a new bucket */ > pp = map(d, MALLOC_PAGESIZE, 0); > if (pp == MAP_FAILED) > return NULL; > + if (DO_STATS) { > + ff = map(d, MALLOC_PAGESIZE, 0); > + if (ff == MAP_FAILED) > + goto err; > + memset(ff, 0, sizeof(void *) * MALLOC_PAGESIZE / > B2ALLOC(bucket)); > + } > > /* memory protect the page allocated in the malloc(0) case */ > if (bucket == 0 && mprotect(pp, MALLOC_PAGESIZE, PROT_NONE) == -1) > @@ -1011,7 +1035,7 @@ omalloc_make_chunks(struct dir_info *d, > bp->page = pp; > > if (insert(d, (void *)((uintptr_t)pp | (bucket + 1)), (uintptr_t)bp, > - NULL)) > + ff)) > goto err; > LIST_INSERT_HEAD(&d->chunk_dir[bucket][listnum], bp, entries); > > @@ -1022,6 +1046,8 @@ omalloc_make_chunks(struct dir_info *d, > > err: > unmap(d, pp, MALLOC_PAGESIZE, 0); > + if (ff != NULL && ff != MAP_FAILED) > + unmap(d, ff, MALLOC_PAGESIZE, 0); > return NULL; > } > > @@ -1101,7 +1127,7 @@ fill_canary(char *ptr, size_t sz, size_t > * Allocate a chunk > */ > static void * > -malloc_bytes(struct dir_info *d, size_t size, void *f) > +malloc_bytes(struct dir_info *d, size_t size) > { > u_int i, r, bucket, listnum; > size_t k; > @@ -1153,11 +1179,6 @@ malloc_bytes(struct dir_info *d, size_t > } > } > found: > - if (i == 0 && k == 0 && DO_STATS) { > - struct region_info *r = find(d, bp->page); > - STATS_SETF(r, f); > - } > - > *lp ^= 1 << k; > > /* If there are no more free, remove from free-list */ > @@ -1170,6 +1191,11 @@ found: > if (mopts.chunk_canaries && size > 0) > bp->bits[bp->offset + k] = size; > > + if (DO_STATS) { > + struct region_info *r = find(d, bp->page); > + STATS_SETFN(r, k, d->caller); > + } > + > k *= B2ALLOC(bp->bucket); > > p = (char *)bp->page + k; > @@ -1194,8 +1220,8 @@ validate_canary(struct dir_info *d, u_ch > > while (p < q) { > if (*p != (u_char)mopts.chunk_canaries && *p != SOME_JUNK) { > - wrterror(d, "canary corrupted %p %#tx@%#zx%s", > - ptr, p - ptr, sz, > + wrterror(d, "canary corrupted %p[%tu]@%zu/%zu%s", > + ptr, p - ptr, sz, allocated, > *p == SOME_FREEJUNK ? " (double free?)" : ""); > } > p++; > @@ -1215,8 +1241,7 @@ find_chunknum(struct dir_info *d, struct > > if ((uintptr_t)ptr & (MALLOC_MINSIZE - 1)) > wrterror(d, "modified chunk-pointer %p", ptr); > - if (info->bits[chunknum / MALLOC_BITS] & > - (1U << (chunknum % MALLOC_BITS))) > + if (CHUNK_FREE(info, chunknum)) > wrterror(d, "double free %p", ptr); > if (check && info->bucket > 0) { > validate_canary(d, ptr, info->bits[info->offset + chunknum], > @@ -1239,9 +1264,6 @@ free_bytes(struct dir_info *d, struct re > info = (struct chunk_info *)r->size; > chunknum = find_chunknum(d, info, ptr, 0); > > - if (chunknum == 0) > - STATS_SETF(r, NULL); > - > info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); > info->free++; > > @@ -1261,18 +1283,22 @@ free_bytes(struct dir_info *d, struct re > if (info->bucket == 0 && !mopts.malloc_freeunmap) > mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); > unmap(d, info->page, MALLOC_PAGESIZE, 0); > +#ifdef MALLOC_STATS > + if (r->f != NULL) { > + unmap(d, r->f, MALLOC_PAGESIZE, MALLOC_PAGESIZE); > + r->f = NULL; > + } > +#endif > > delete(d, r); > mp = &d->chunk_info_list[info->bucket]; > LIST_INSERT_HEAD(mp, info, entries); > } > > - > - > static void * > -omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) > +omalloc(struct dir_info *pool, size_t sz, int zero_fill) > { > - void *p; > + void *p, *caller = NULL; > size_t psz; > > if (sz > MALLOC_MAXCHUNK) { > @@ -1287,7 +1313,11 @@ omalloc(struct dir_info *pool, size_t sz > errno = ENOMEM; > return NULL; > } > - if (insert(pool, p, sz, f)) { > +#ifdef MALLOC_STATS > + if (DO_STATS) > + caller = pool->caller; > +#endif > + if (insert(pool, p, sz, caller)) { > unmap(pool, p, psz, 0); > errno = ENOMEM; > return NULL; > @@ -1324,7 +1354,7 @@ omalloc(struct dir_info *pool, size_t sz > > } else { > /* takes care of SOME_JUNK */ > - p = malloc_bytes(pool, sz, f); > + p = malloc_bytes(pool, sz); > if (zero_fill && p != NULL && sz > 0) > memset(p, 0, sz); > } > @@ -1473,7 +1503,8 @@ malloc(size_t size) > int saved_errno = errno; > > PROLOGUE(getpool(), "malloc") > - r = omalloc(d, size, 0, caller()); > + SET_CALLER(d, caller()); > + r = omalloc(d, size, 0); > EPILOGUE() > return r; > } > @@ -1487,7 +1518,8 @@ malloc_conceal(size_t size) > int saved_errno = errno; > > PROLOGUE(mopts.malloc_pool[0], "malloc_conceal") > - r = omalloc(d, size, 0, caller()); > + SET_CALLER(d, caller()); > + r = omalloc(d, size, 0); > EPILOGUE() > return r; > } > @@ -1495,7 +1527,7 @@ DEF_WEAK(malloc_conceal); > > static struct region_info * > findpool(void *p, struct dir_info *argpool, struct dir_info **foundpool, > - char **saved_function) > + const char ** saved_function) > { > struct dir_info *pool = argpool; > struct region_info *r = find(pool, p); > @@ -1533,7 +1565,7 @@ ofree(struct dir_info **argpool, void *p > { > struct region_info *r; > struct dir_info *pool; > - char *saved_function; > + const char *saved_function; > size_t sz; > > r = findpool(p, *argpool, &pool, &saved_function); > @@ -1721,11 +1753,11 @@ freezero(void *ptr, size_t sz) > DEF_WEAK(freezero); > > static void * > -orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) > +orealloc(struct dir_info **argpool, void *p, size_t newsz) > { > struct region_info *r; > struct dir_info *pool; > - char *saved_function; > + const char *saved_function; > struct chunk_info *info; > size_t oldsz, goldsz, gnewsz; > void *q, *ret; > @@ -1733,7 +1765,7 @@ orealloc(struct dir_info **argpool, void > int forced; > > if (p == NULL) > - return omalloc(*argpool, newsz, 0, f); > + return omalloc(*argpool, newsz, 0); > > if (newsz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { > errno = ENOMEM; > @@ -1797,7 +1829,7 @@ orealloc(struct dir_info **argpool, void > if (mopts.chunk_canaries) > fill_canary(p, newsz, > PAGEROUND(newsz)); > - STATS_SETF(r, f); > + STATS_SETF(r, (*argpool)->caller); > STATS_INC(pool->cheap_reallocs); > ret = p; > goto done; > @@ -1822,7 +1854,7 @@ orealloc(struct dir_info **argpool, void > p = pp; > } else if (mopts.chunk_canaries) > fill_canary(p, newsz, PAGEROUND(newsz)); > - STATS_SETF(r, f); > + STATS_SETF(r, (*argpool)->caller); > ret = p; > goto done; > } else { > @@ -1844,7 +1876,7 @@ orealloc(struct dir_info **argpool, void > if (mopts.chunk_canaries) > fill_canary(p, newsz, PAGEROUND(newsz)); > } > - STATS_SETF(r, f); > + STATS_SETF(r, (*argpool)->caller); > ret = p; > goto done; > } > @@ -1859,12 +1891,12 @@ orealloc(struct dir_info **argpool, void > info->bits[info->offset + chunknum] = newsz; > fill_canary(p, newsz, B2SIZE(info->bucket)); > } > - if (DO_STATS && chunknum == 0) > - STATS_SETF(r, f); > + if (DO_STATS) > + STATS_SETFN(r, chunknum, (*argpool)->caller); > ret = p; > } else if (newsz != oldsz || forced) { > /* create new allocation */ > - q = omalloc(pool, newsz, 0, f); > + q = omalloc(pool, newsz, 0); > if (q == NULL) { > ret = NULL; > goto done; > @@ -1877,8 +1909,8 @@ orealloc(struct dir_info **argpool, void > /* oldsz == newsz */ > if (newsz != 0) > wrterror(pool, "realloc internal inconsistency"); > - if (DO_STATS && chunknum == 0) > - STATS_SETF(r, f); > + if (DO_STATS) > + STATS_SETFN(r, chunknum, (*argpool)->caller); > ret = p; > } > done: > @@ -1897,7 +1929,8 @@ realloc(void *ptr, size_t size) > int saved_errno = errno; > > PROLOGUE(getpool(), "realloc") > - r = orealloc(&d, ptr, size, caller()); > + SET_CALLER(d, caller()); > + r = orealloc(&d, ptr, size); > EPILOGUE() > return r; > } > @@ -1917,6 +1950,7 @@ calloc(size_t nmemb, size_t size) > int saved_errno = errno; > > PROLOGUE(getpool(), "calloc") > + SET_CALLER(d, caller()); > if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && > nmemb > 0 && SIZE_MAX / nmemb < size) { > d->active--; > @@ -1928,7 +1962,7 @@ calloc(size_t nmemb, size_t size) > } > > size *= nmemb; > - r = omalloc(d, size, 1, caller()); > + r = omalloc(d, size, 1); > EPILOGUE() > return r; > } > @@ -1942,6 +1976,7 @@ calloc_conceal(size_t nmemb, size_t size > int saved_errno = errno; > > PROLOGUE(mopts.malloc_pool[0], "calloc_conceal") > + SET_CALLER(d, caller()); > if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && > nmemb > 0 && SIZE_MAX / nmemb < size) { > d->active--; > @@ -1953,7 +1988,7 @@ calloc_conceal(size_t nmemb, size_t size > } > > size *= nmemb; > - r = omalloc(d, size, 1, caller()); > + r = omalloc(d, size, 1); > EPILOGUE() > return r; > } > @@ -1961,16 +1996,16 @@ DEF_WEAK(calloc_conceal); > > static void * > orecallocarray(struct dir_info **argpool, void *p, size_t oldsize, > - size_t newsize, void *f) > + size_t newsize) > { > struct region_info *r; > struct dir_info *pool; > - char *saved_function; > + const char *saved_function; > void *newptr; > size_t sz; > > if (p == NULL) > - return omalloc(*argpool, newsize, 1, f); > + return omalloc(*argpool, newsize, 1); > > if (oldsize == newsize) > return p; > @@ -2001,7 +2036,7 @@ orecallocarray(struct dir_info **argpool > sz - mopts.malloc_guard, oldsize); > } > > - newptr = omalloc(pool, newsize, 0, f); > + newptr = omalloc(pool, newsize, 0); > if (newptr == NULL) > goto done; > > @@ -2086,6 +2121,7 @@ recallocarray(void *ptr, size_t oldnmemb > return recallocarray_p(ptr, oldnmemb, newnmemb, size); > > PROLOGUE(getpool(), "recallocarray") > + SET_CALLER(d, caller()); > > if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && > newnmemb > 0 && SIZE_MAX / newnmemb < size) { > @@ -2109,7 +2145,7 @@ recallocarray(void *ptr, size_t oldnmemb > oldsize = oldnmemb * size; > } > > - r = orecallocarray(&d, ptr, oldsize, newsize, caller()); > + r = orecallocarray(&d, ptr, oldsize, newsize); > EPILOGUE() > return r; > } > @@ -2150,11 +2186,10 @@ mapalign(struct dir_info *d, size_t alig > } > > static void * > -omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill, > - void *f) > +omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill) > { > size_t psz; > - void *p; > + void *p, *caller = NULL; > > /* If between half a page and a page, avoid MALLOC_MOVE. */ > if (sz > MALLOC_MAXCHUNK && sz < MALLOC_PAGESIZE) > @@ -2174,7 +2209,7 @@ omemalign(struct dir_info *pool, size_t > pof2 <<= 1; > } else > pof2 = sz; > - return omalloc(pool, pof2, zero_fill, f); > + return omalloc(pool, pof2, zero_fill); > } > > if (sz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { > @@ -2193,7 +2228,11 @@ omemalign(struct dir_info *pool, size_t > return NULL; > } > > - if (insert(pool, p, sz, f)) { > +#ifdef MALLOC_STATS > + if (DO_STATS) > + caller = pool->caller; > +#endif > + if (insert(pool, p, sz, caller)) { > unmap(pool, p, psz, 0); > errno = ENOMEM; > return NULL; > @@ -2241,7 +2280,8 @@ posix_memalign(void **memptr, size_t ali > malloc_recurse(d); > goto err; > } > - r = omemalign(d, alignment, size, 0, caller()); > + SET_CALLER(d, caller()); > + r = omemalign(d, alignment, size, 0); > d->active--; > _MALLOC_UNLOCK(d->mutex); > if (r == NULL) { > @@ -2279,7 +2319,8 @@ aligned_alloc(size_t alignment, size_t s > } > > PROLOGUE(getpool(), "aligned_alloc") > - r = omemalign(d, alignment, size, 0, caller()); > + SET_CALLER(d, caller()); > + r = omemalign(d, alignment, size, 0); > EPILOGUE() > return r; > } > @@ -2288,6 +2329,45 @@ DEF_STRONG(aligned_alloc); > #ifdef MALLOC_STATS > > static void > +print_chunk_details(struct dir_info *pool, void *p, size_t sz, size_t i) > +{ > + struct region_info *r; > + struct chunk_info *chunkinfo; > + uint32_t chunknum; > + Dl_info info; > + const char *caller, *pcaller = NULL; > + const char *object = "."; > + const char *pobject = "."; > + const char *msg = ""; > + > + r = find(pool, p); > + chunkinfo = (struct chunk_info *)r->size; > + chunknum = find_chunknum(pool, chunkinfo, p, 0); > + caller = r->f[chunknum]; > + if (dladdr(caller, &info) != 0) { > + caller -= (uintptr_t)info.dli_fbase; > + object = info.dli_fname; > + } > + if (chunknum > 0) { > + chunknum--; > + pcaller = r->f[chunknum]; > + if (dladdr(pcaller, &info) != 0) { > + pcaller -= (uintptr_t)info.dli_fbase; > + pobject = info.dli_fname; > + } > + if (CHUNK_FREE(chunkinfo, chunknum)) > + msg = " (now free)"; > + } > + > + wrterror(pool, > + "write to free chunk %p[%zu..%zu]@%zu allocated at %s %p " > + "(preceding chunk %p allocated at %s %p%s)", > + p, i * sizeof(uint64_t), > + (i + 1) * sizeof(uint64_t) - 1, sz, object, caller, p - sz, > + pobject, pcaller, msg); > +} > + > +static void > ulog(const char *format, ...) > { > va_list ap; > @@ -2413,23 +2493,19 @@ dump_leaks(struct leaktree *leaks) > } > > static void > -dump_chunk(struct leaktree* leaks, struct chunk_info *p, void *f, > +dump_chunk(struct leaktree* leaks, struct chunk_info *p, void **f, > int fromfreelist) > { > while (p != NULL) { > if (mopts.malloc_verbose) > ulog("chunk %18p %18p %4zu %d/%d\n", > - p->page, ((p->bits[0] & 1) ? NULL : f), > + p->page, NULL, > B2SIZE(p->bucket), p->free, p->total); > if (!fromfreelist) { > - size_t sz = B2SIZE(p->bucket); > - if (p->bits[0] & 1) > - putleakinfo(leaks, NULL, sz, p->total - > - p->free); > - else { > - putleakinfo(leaks, f, sz, 1); > - putleakinfo(leaks, NULL, sz, > - p->total - p->free - 1); > + size_t i, sz = B2SIZE(p->bucket); > + for (i = 0; i < p->total; i++) { > + if (!CHUNK_FREE(p, i)) > + putleakinfo(leaks, f[i], sz, 1); > } > break; > } > @@ -2501,7 +2577,7 @@ malloc_dump1(int poolno, struct dir_info > > if (mopts.malloc_verbose) { > ulog("Malloc dir of %s pool %d at %p\n", __progname, poolno, d); > - ulog("MT=%d J=%d Fl=%x\n", d->malloc_mt, d->malloc_junk, > + ulog("MT=%d J=%d Fl=%#x\n", d->malloc_mt, d->malloc_junk, > d->mmap_flag); > ulog("Region slots free %zu/%zu\n", > d->regions_free, d->regions_total); > @@ -2589,7 +2665,7 @@ malloc_exit(void) > int save_errno = errno; > > ulog("******** Start dump %s *******\n", __progname); > - ulog("M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " > + ulog("M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%#x cache=%u " > "G=%zu\n", > mopts.malloc_mutexes, > mopts.internal_funcs, mopts.malloc_freecheck, > > >