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,

Reply via email to