factor out a bit of the chunk index code and use it to make sure that a
authortedu <tedu@openbsd.org>
Sun, 18 May 2014 17:49:47 +0000 (17:49 +0000)
committertedu <tedu@openbsd.org>
Sun, 18 May 2014 17:49:47 +0000 (17:49 +0000)
freed chunk is actually freeable immediately. catch more errors.
hints/ok otto

lib/libc/stdlib/malloc.c

index c0542fc..3f54400 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: malloc.c,v 1.163 2014/05/12 19:02:20 tedu Exp $       */
+/*     $OpenBSD: malloc.c,v 1.164 2014/05/18 17:49:47 tedu Exp $       */
 /*
  * Copyright (c) 2008, 2010, 2011 Otto Moerbeek <otto@drijf.net>
  * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org>
@@ -966,34 +966,47 @@ malloc_bytes(struct dir_info *d, size_t size, void *f)
        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)
+static uint32_t
+find_chunknum(struct dir_info *d, struct region_info *r, void *ptr)
 {
-       struct chunk_head *mp;
        struct chunk_info *info;
-       int i, listnum;
+       uint32_t chunknum;
 
        info = (struct chunk_info *)r->size;
        if (info->canary != d->canary1)
                wrterror("chunk info corrupted", NULL);
 
        /* Find the chunk number on the page */
-       i = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift;
+       chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift;
 
        if ((uintptr_t)ptr & ((1U << (info->shift)) - 1)) {
                wrterror("modified chunk-pointer", ptr);
-               return;
+               return -1;
        }
-       if (info->bits[i / MALLOC_BITS] & (1U << (i % MALLOC_BITS))) {
+       if (info->bits[chunknum / MALLOC_BITS] &
+           (1U << (chunknum % MALLOC_BITS))) {
                wrterror("chunk is already free", ptr);
-               return;
+               return -1;
        }
+       return chunknum;
+}
 
-       info->bits[i / MALLOC_BITS] |= 1U << (i % MALLOC_BITS);
+/*
+ * 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;
+       uint32_t chunknum;
+       int listnum;
+
+       info = (struct chunk_info *)r->size;
+       if ((chunknum = find_chunknum(d, r, ptr)) == -1)
+               return;
+
+       info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS);
        info->free++;
 
        if (info->free == 1) {
@@ -1204,9 +1217,15 @@ ofree(void *p)
                if (mopts.malloc_junk && sz > 0)
                        memset(p, SOME_FREEJUNK, sz);
                if (!mopts.malloc_freenow) {
+                       if (find_chunknum(g_pool, r, p) == -1)
+                               return;
                        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) {