Hi,
Malloc maintains a list if 16 slots of chunks to be freed. On free a
chunk is put in a random slot and the existing chunk in that slot is
actually freed. Currently, the code only checks the slot selected for
a double free.
This diff adds code to check all slots. It also removes the option to
disable delayed free.
For now, this extra check is only enabled with F. F is also added to S.
As a bonus, the chunk canary corruption check wil also hint at a
double free if applicable.
Please review and test. malloc.conf diff will come later.
-Otto
Index: malloc.c
===================================================================
RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v
retrieving revision 1.231
diff -u -p -r1.231 malloc.c
--- malloc.c 12 Sep 2017 18:36:30 -0000 1.231
+++ malloc.c 19 Sep 2017 13:04:47 -0000
@@ -179,7 +179,7 @@ struct chunk_info {
struct malloc_readonly {
struct dir_info *malloc_pool[_MALLOC_MUTEXES]; /* Main bookkeeping
information */
int malloc_mt; /* multi-threaded mode? */
- int malloc_freenow; /* Free quickly - disable chunk rnd */
+ int malloc_freecheck; /* Extensive double free check */
int malloc_freeunmap; /* mprotect free pages PROT_NONE? */
int malloc_junk; /* junk fill? */
int malloc_realloc; /* always realloc? */
@@ -520,11 +520,11 @@ omalloc_parseopt(char opt)
break;
#endif /* MALLOC_STATS */
case 'f':
- mopts.malloc_freenow = 0;
+ mopts.malloc_freecheck = 0;
mopts.malloc_freeunmap = 0;
break;
case 'F':
- mopts.malloc_freenow = 1;
+ mopts.malloc_freecheck = 1;
mopts.malloc_freeunmap = 1;
break;
case 'g':
@@ -605,7 +605,7 @@ omalloc_init(void)
for (; p != NULL && *p != '\0'; p++) {
switch (*p) {
case 'S':
- for (q = "CGJ"; *q != '\0'; q++)
+ for (q = "CFGJ"; *q != '\0'; q++)
omalloc_parseopt(*q);
mopts.malloc_cache = 0;
break;
@@ -1046,10 +1046,12 @@ validate_canary(struct dir_info *d, u_ch
q = p + check_sz;
while (p < q) {
- if (*p++ != SOME_JUNK) {
- wrterror(d, "chunk canary corrupted %p %#tx@%#zx",
- ptr, p - ptr - 1, sz);
+ if (*p != SOME_JUNK) {
+ wrterror(d, "chunk canary corrupted %p %#tx@%#zx%s",
+ ptr, p - ptr, sz, *p == SOME_FREEJUNK ?
+ " (double free?)" : "");
}
+ p++;
}
}
@@ -1381,13 +1383,18 @@ ofree(struct dir_info *argpool, void *p,
unmap(pool, p, PAGEROUND(sz), clear);
delete(pool, r);
} else {
- void *tmp;
- int i;
-
- /* Delayed free or canaries? Extra check */
- if (!mopts.malloc_freenow || mopts.chunk_canaries)
- find_chunknum(pool, r, p, mopts.chunk_canaries);
- if (!clear && !mopts.malloc_freenow) {
+ /* Validate and optionally canary check */
+ find_chunknum(pool, r, p, mopts.chunk_canaries);
+ if (!clear) {
+ void *tmp;
+ int i;
+
+ if (mopts.malloc_freecheck) {
+ for (i = 0; i <= MALLOC_DELAYED_CHUNK_MASK; i++)
+ if (p == pool->delayed_chunks[i])
+ wrterror(pool,
+ "double free %p", p);
+ }
if (mopts.malloc_junk && sz > 0)
memset(p, SOME_FREEJUNK, sz);
i = getrbyte(pool) & MALLOC_DELAYED_CHUNK_MASK;
@@ -1395,13 +1402,11 @@ ofree(struct dir_info *argpool, void *p,
p = pool->delayed_chunks[i];
if (tmp == p)
wrterror(pool, "double free %p", tmp);
+ pool->delayed_chunks[i] = tmp;
if (mopts.malloc_junk)
validate_junk(pool, p);
- pool->delayed_chunks[i] = tmp;
- } else {
- if ((clear || mopts.malloc_junk) && sz > 0)
- memset(p, clear ? 0 : SOME_FREEJUNK, sz);
- }
+ } else if (sz > 0)
+ memset(p, 0, sz);
if (p != NULL) {
r = find(pool, p);
if (r == NULL)
@@ -2348,7 +2353,7 @@ malloc_exit(void)
snprintf(buf, sizeof(buf),
"MT=%d I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u G=%zu\n",
mopts.malloc_mt, mopts.internal_funcs,
- mopts.malloc_freenow,
+ mopts.malloc_freecheck,
mopts.malloc_freeunmap, mopts.malloc_junk,
mopts.malloc_realloc, mopts.malloc_xmalloc,
mopts.chunk_canaries, mopts.malloc_cache,