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) {


Reply via email to