As I learned the hard way not long ago, free() doesn't detect all errors because of the delay mechanism. We can make two improvements.
1. Perform the sanity checking from free_bytes before we insert something into the delay array. This detects many kinds of badness much sooner. 2. Check that the freed pointer isn't the same as the pointer in the delay array. Checking the entire array would be more complete, but slower. Randomly crashing immediately is a modest improvement. Index: malloc.c =================================================================== RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v retrieving revision 1.163 diff -u -p -r1.163 malloc.c --- malloc.c 12 May 2014 19:02:20 -0000 1.163 +++ malloc.c 14 May 2014 19:21:15 -0000 @@ -966,16 +966,11 @@ malloc_bytes(struct dir_info *d, size_t return ((char *)bp->page + k); } - -/* - * Free a chunk, and possibly the page it's on, if the page becomes empty. - */ static void -free_bytes(struct dir_info *d, struct region_info *r, void *ptr) +check_free_chunk(struct dir_info *d, struct region_info *r, void *ptr) { - struct chunk_head *mp; struct chunk_info *info; - int i, listnum; + int i; info = (struct chunk_info *)r->size; if (info->canary != d->canary1) @@ -992,6 +987,24 @@ free_bytes(struct dir_info *d, struct re wrterror("chunk is already free", ptr); return; } +} + +/* + * Free a chunk, and possibly the page it's on, if the page becomes empty. + */ +static void +free_bytes(struct dir_info *d, struct region_info *r, void *ptr) +{ + struct chunk_head *mp; + struct chunk_info *info; + int i, listnum; + + check_free_chunk(d, r, ptr); + + info = (struct chunk_info *)r->size; + + /* Find the chunk number on the page */ + i = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; info->bits[i / MALLOC_BITS] |= 1U << (i % MALLOC_BITS); info->free++; @@ -1204,9 +1217,14 @@ ofree(void *p) if (mopts.malloc_junk && sz > 0) memset(p, SOME_FREEJUNK, sz); if (!mopts.malloc_freenow) { + check_free_chunk(g_pool, r, p); i = getrbyte() & MALLOC_DELAYED_CHUNK_MASK; tmp = p; p = g_pool->delayed_chunks[i]; + if (tmp == p) { + wrterror("double free", p); + return; + } g_pool->delayed_chunks[i] = tmp; } if (p != NULL) {