summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2014-05-18 17:49:48 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2014-05-18 17:49:48 +0000
commit0ee767acec5ac14aada0673c011486d1b84c2e8a (patch)
tree0c048cbbb192c52db27f831ba63a065fde1ab952
parent1e415c18717f1b7716f404d580bb7d2fe6abfd76 (diff)
factor out a bit of the chunk index code and use it to make sure that a
freed chunk is actually freeable immediately. catch more errors. hints/ok otto
-rw-r--r--lib/libc/stdlib/malloc.c47
1 files changed, 33 insertions, 14 deletions
diff --git a/lib/libc/stdlib/malloc.c b/lib/libc/stdlib/malloc.c
index c0542fc9c86..3f544006fc7 100644
--- a/lib/libc/stdlib/malloc.c
+++ b/lib/libc/stdlib/malloc.c
@@ -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) {