summaryrefslogtreecommitdiff
path: root/lib/libc/stdlib/malloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/stdlib/malloc.c')
-rw-r--r--lib/libc/stdlib/malloc.c186
1 files changed, 125 insertions, 61 deletions
diff --git a/lib/libc/stdlib/malloc.c b/lib/libc/stdlib/malloc.c
index 028eff2b2d5..ace34c96f67 100644
--- a/lib/libc/stdlib/malloc.c
+++ b/lib/libc/stdlib/malloc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: malloc.c,v 1.83 2006/05/14 19:53:40 otto Exp $ */
+/* $OpenBSD: malloc.c,v 1.84 2006/10/24 04:35:30 tedu Exp $ */
/*
* ----------------------------------------------------------------------------
@@ -75,6 +75,18 @@
#define malloc_pageshift (PGSHIFT)
#endif
+#ifndef malloc_minsize
+#define malloc_minsize 16UL
+#endif
+
+#if !defined(malloc_pagesize)
+#define malloc_pagesize (1UL<<malloc_pageshift)
+#endif
+
+/* How many bits per u_long in the bitmap */
+#define MALLOC_BITS (NBBY * sizeof(u_long))
+
+
/*
* No user serviceable parts behind this point.
*
@@ -87,12 +99,9 @@ struct pginfo {
u_short shift; /* How far to shift for this size chunks */
u_short free; /* How many free chunks */
u_short total; /* How many chunk */
- u_long bits[1];/* Which chunks are free */
+ u_long bits[(malloc_pagesize / malloc_minsize) / MALLOC_BITS];/* Which chunks are free */
};
-/* How many bits per u_long in the bitmap */
-#define MALLOC_BITS (NBBY * sizeof(u_long))
-
/*
* This structure describes a number of free pages.
*/
@@ -113,14 +122,6 @@ struct pgfree {
#define MALLOC_FOLLOW ((struct pginfo*) 3)
#define MALLOC_MAGIC ((struct pginfo*) 4)
-#ifndef malloc_minsize
-#define malloc_minsize 16UL
-#endif
-
-#if !defined(malloc_pagesize)
-#define malloc_pagesize (1UL<<malloc_pageshift)
-#endif
-
#if ((1UL<<malloc_pageshift) != malloc_pagesize)
#error "(1UL<<malloc_pageshift) != malloc_pagesize"
#endif
@@ -254,6 +255,66 @@ static void ifree(void *ptr);
static void *irealloc(void *ptr, size_t size);
static void *malloc_bytes(size_t size);
+static struct pginfo *pginfo_list;
+
+static struct pgfree *pgfree_list;
+
+static struct pgfree *
+alloc_pgfree()
+{
+ struct pgfree *p;
+ int i;
+
+ if (pgfree_list == NULL) {
+ p = MMAP(malloc_pagesize);
+ if (!p)
+ return NULL;
+ for (i = 0; i < malloc_pagesize / sizeof(*p); i++) {
+ p[i].next = pgfree_list;
+ pgfree_list = &p[i];
+ }
+ }
+ p = pgfree_list;
+ pgfree_list = p->next;
+ memset(p, 0, sizeof *p);
+ return p;
+}
+
+static struct pginfo *
+alloc_pginfo()
+{
+ struct pginfo *p;
+ int i;
+
+ if (pginfo_list == NULL) {
+ p = MMAP(malloc_pagesize);
+ if (!p)
+ return NULL;
+ for (i = 0; i < malloc_pagesize / sizeof(*p); i++) {
+ p[i].next = pginfo_list;
+ pginfo_list = &p[i];
+ }
+ }
+ p = pginfo_list;
+ pginfo_list = p->next;
+ memset(p, 0, sizeof *p);
+ return p;
+}
+
+static void
+put_pgfree(struct pgfree *p)
+{
+ p->next = pgfree_list;
+ pgfree_list = p;
+}
+
+static void
+put_pginfo(struct pginfo *p)
+{
+ p->next = pginfo_list;
+ pginfo_list = p;
+}
+
/*
* Function for page directory lookup.
*/
@@ -764,12 +825,12 @@ malloc_init(void)
static void *
malloc_pages(size_t size)
{
- void *p, *delay_free = NULL, *tp;
+ void *p, *tp;
int i;
struct pginfo **pd;
struct pdinfo *pi;
u_long pidx, index;
- struct pgfree *pf;
+ struct pgfree *pf, *delay_free = NULL;
size = pageround(size) + malloc_guard;
@@ -936,7 +997,7 @@ malloc_pages(size_t size)
if (px == NULL)
px = delay_free;
else
- ifree(delay_free);
+ put_pgfree(delay_free);
}
return (p);
}
@@ -945,7 +1006,7 @@ malloc_pages(size_t size)
* Allocate a page of fragments
*/
-static __inline__ int
+static int
malloc_make_chunks(int bits)
{
struct pginfo *bp, **pd;
@@ -955,17 +1016,13 @@ malloc_make_chunks(int bits)
#endif /* MALLOC_EXTRA_SANITY */
void *pp;
long i, k;
- size_t l;
/* Allocate a new bucket */
- pp = malloc_pages((size_t) malloc_pagesize);
+ pp = malloc_pages((size_t)malloc_pagesize);
if (pp == NULL)
return (0);
/* Find length of admin structure */
- l = sizeof *bp - sizeof(u_long);
- l += sizeof(u_long) *
- (((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS);
/* Don't waste more than two chunks on this */
@@ -975,14 +1032,10 @@ malloc_make_chunks(int bits)
* pginfo page.
* --> Treat it like the big chunk alloc, get a second data page.
*/
- if (bits != 0 && (1UL << (bits)) <= l + l) {
- bp = (struct pginfo *) pp;
- } else {
- bp = (struct pginfo *) imalloc(l);
- if (bp == NULL) {
- ifree(pp);
- return (0);
- }
+ bp = alloc_pginfo();
+ if (bp == NULL) {
+ ifree(pp);
+ return (0);
}
/* memory protect the page allocated in the malloc(0) case */
@@ -998,7 +1051,7 @@ malloc_make_chunks(int bits)
k = mprotect(pp, malloc_pagesize, PROT_NONE);
if (k < 0) {
ifree(pp);
- ifree(bp);
+ put_pginfo(bp);
return (0);
}
} else {
@@ -1019,18 +1072,6 @@ malloc_make_chunks(int bits)
for (; i < k; i++)
bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS);
- k = (long)l;
- if (bp == bp->page) {
- /* Mark the ones we stole for ourselves */
- for (i = 0; k > 0; i++) {
- bp->bits[i / MALLOC_BITS] &= ~(1UL << (i % MALLOC_BITS));
- bp->free--;
- bp->total--;
- k -= (1 << bits);
- }
- }
- /* MALLOC_LOCK */
-
pdir_lookup(ptr2index(pp), &pi);
#ifdef MALLOC_EXTRA_SANITY
pidx = PI_IDX(ptr2index(pp));
@@ -1325,7 +1366,7 @@ irealloc(void *ptr, size_t size)
/*
* Free a sequence of pages
*/
-static __inline__ void
+static void
free_pages(void *ptr, u_long index, struct pginfo * info)
{
u_long i, pidx, lidx;
@@ -1409,8 +1450,8 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
mprotect(ptr, l, PROT_NONE);
/* Add to free-list. */
- if (px == NULL && (px = malloc_bytes(sizeof *px)) == NULL)
- goto not_return;
+ if (px == NULL && (px = alloc_pgfree()) == NULL)
+ goto not_return;
px->page = ptr;
px->pdir = spi;
px->size = l;
@@ -1578,7 +1619,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
/* XXX: We could realloc/shrink the pagedir here I guess. */
if (pf->size == 0) { /* Remove from free-list as well. */
if (px)
- ifree(px);
+ put_pgfree(px);
if ((px = pf->prev) != &free_list) {
if (pi == NULL && last_index == (index - 1)) {
if (spi == NULL) {
@@ -1592,7 +1633,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
pdi_mod) - 1;
for (pi = spi, i = index;
pd[PI_OFF(i)] == MALLOC_NOT_MINE;
- i--)
+ i--) {
#ifdef MALLOC_EXTRA_SANITY
if (!PI_OFF(i)) {
pi = pi->prev;
@@ -1601,10 +1642,8 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
pd = pi->base;
i = (PD_IDX(pi->dirnum) + 1) * pdi_mod;
}
-#else /* !MALLOC_EXTRA_SANITY */
- {
- }
#endif /* MALLOC_EXTRA_SANITY */
+ }
malloc_brk = index2ptr(i + 1);
}
last_index = i;
@@ -1622,7 +1661,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
}
not_return:
if (pt != NULL)
- ifree(pt);
+ put_pgfree(pt);
}
/*
@@ -1630,16 +1669,41 @@ not_return:
*/
/* ARGSUSED */
-static __inline__ void
-free_bytes(void *ptr, u_long index, struct pginfo * info)
+static void
+free_bytes(void *ptr)
{
- struct pginfo **mp, **pd;
+ struct pginfo **mp, **pd, *info;
struct pdinfo *pi;
#ifdef MALLOC_EXTRA_SANITY
u_long pidx;
#endif /* MALLOC_EXTRA_SANITY */
+ u_long index;
void *vp;
long i;
+ void *tmpptr;
+ unsigned int tmpidx;
+ /* pointers that we will want to free at some future time */
+ static void *chunk_buffer[16];
+
+
+ /* delay return, returning a random something from before instead */
+ tmpidx = arc4random() % 16;
+ tmpptr = chunk_buffer[tmpidx];
+ chunk_buffer[tmpidx] = ptr;
+ ptr = tmpptr;
+ if (!ptr)
+ return;
+
+ index = ptr2index(ptr);
+
+ pdir_lookup(index, &pi);
+ if (pi != last_dir) {
+ prev_dir = last_dir;
+ last_dir = pi;
+ }
+ pd = pi->base;
+ info = pd[PI_OFF(index)];
+
/* Find the chunk number on the page */
i = ((u_long) ptr & malloc_pagemask) >> info->shift;
@@ -1711,9 +1775,8 @@ free_bytes(void *ptr, u_long index, struct pginfo * info)
if (info->size == 0)
mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE);
- vp = info->page; /* Order is important ! */
- if (vp != (void *) info)
- ifree(info);
+ vp = info->page;
+ put_pginfo(info);
ifree(vp);
}
@@ -1736,7 +1799,7 @@ ifree(void *ptr)
return;
if (malloc_ptrguard && PTR_ALIGNED(ptr))
- ptr = (char *) ptr - PTR_GAP;
+ ptr = (char *)ptr - PTR_GAP;
index = ptr2index(ptr);
@@ -1750,6 +1813,7 @@ ifree(void *ptr)
wrtwarning("ifree: junk pointer, too high to make sense");
return;
}
+
pdir_lookup(index, &pi);
#ifdef MALLOC_EXTRA_SANITY
pidx = PI_IDX(index);
@@ -1769,11 +1833,11 @@ ifree(void *ptr)
if (info < MALLOC_MAGIC)
free_pages(ptr, index, info);
else
- free_bytes(ptr, index, info);
+ free_bytes(ptr);
/* does not matter if malloc_bytes fails */
if (px == NULL)
- px = malloc_bytes(sizeof *px);
+ px = alloc_pgfree();
return;
}