Browse Source

respond to ben hawkes's ruxcon presentation.

create special allocators for pginfo and pgfree structs instead of imalloc.
this keeps them separated from application memory.
for chunks, to prevent deterministic reuse, keep a small array
and swizzle the to be freed chunk with a random previously freed chunk.
this last bit only for chunks because keeping arbitrarily large regions
of pages around may cause out of memory issues (and pages are, to some
extent, returned in random order).
all changes enabled by default.
thanks to ben for pointing out these issues.
ok tech@
OPENBSD_4_1
tedu 18 years ago
parent
commit
dcff59fbc7
1 changed files with 125 additions and 61 deletions
  1. +125
    -61
      src/lib/libc/stdlib/malloc.c

+ 125
- 61
src/lib/libc/stdlib/malloc.c View File

@ -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) #define malloc_pageshift (PGSHIFT)
#endif #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. * 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 shift; /* How far to shift for this size chunks */
u_short free; /* How many free chunks */ u_short free; /* How many free chunks */
u_short total; /* How many chunk */ 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. * This structure describes a number of free pages.
*/ */
@ -113,14 +122,6 @@ struct pgfree {
#define MALLOC_FOLLOW ((struct pginfo*) 3) #define MALLOC_FOLLOW ((struct pginfo*) 3)
#define MALLOC_MAGIC ((struct pginfo*) 4) #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) #if ((1UL<<malloc_pageshift) != malloc_pagesize)
#error "(1UL<<malloc_pageshift) != malloc_pagesize" #error "(1UL<<malloc_pageshift) != malloc_pagesize"
#endif #endif
@ -254,6 +255,66 @@ static void ifree(void *ptr);
static void *irealloc(void *ptr, size_t size); static void *irealloc(void *ptr, size_t size);
static void *malloc_bytes(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. * Function for page directory lookup.
*/ */
@ -764,12 +825,12 @@ malloc_init(void)
static void * static void *
malloc_pages(size_t size) malloc_pages(size_t size)
{ {
void *p, *delay_free = NULL, *tp;
void *p, *tp;
int i; int i;
struct pginfo **pd; struct pginfo **pd;
struct pdinfo *pi; struct pdinfo *pi;
u_long pidx, index; u_long pidx, index;
struct pgfree *pf;
struct pgfree *pf, *delay_free = NULL;
size = pageround(size) + malloc_guard; size = pageround(size) + malloc_guard;
@ -936,7 +997,7 @@ malloc_pages(size_t size)
if (px == NULL) if (px == NULL)
px = delay_free; px = delay_free;
else else
ifree(delay_free);
put_pgfree(delay_free);
} }
return (p); return (p);
} }
@ -945,7 +1006,7 @@ malloc_pages(size_t size)
* Allocate a page of fragments * Allocate a page of fragments
*/ */
static __inline__ int
static int
malloc_make_chunks(int bits) malloc_make_chunks(int bits)
{ {
struct pginfo *bp, **pd; struct pginfo *bp, **pd;
@ -955,17 +1016,13 @@ malloc_make_chunks(int bits)
#endif /* MALLOC_EXTRA_SANITY */ #endif /* MALLOC_EXTRA_SANITY */
void *pp; void *pp;
long i, k; long i, k;
size_t l;
/* Allocate a new bucket */ /* Allocate a new bucket */
pp = malloc_pages((size_t) malloc_pagesize);
pp = malloc_pages((size_t)malloc_pagesize);
if (pp == NULL) if (pp == NULL)
return (0); return (0);
/* Find length of admin structure */ /* 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 */ /* Don't waste more than two chunks on this */
@ -975,14 +1032,10 @@ malloc_make_chunks(int bits)
* pginfo page. * pginfo page.
* --> Treat it like the big chunk alloc, get a second data 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 */ /* 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); k = mprotect(pp, malloc_pagesize, PROT_NONE);
if (k < 0) { if (k < 0) {
ifree(pp); ifree(pp);
ifree(bp);
put_pginfo(bp);
return (0); return (0);
} }
} else { } else {
@ -1019,18 +1072,6 @@ malloc_make_chunks(int bits)
for (; i < k; i++) for (; i < k; i++)
bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); 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); pdir_lookup(ptr2index(pp), &pi);
#ifdef MALLOC_EXTRA_SANITY #ifdef MALLOC_EXTRA_SANITY
pidx = PI_IDX(ptr2index(pp)); pidx = PI_IDX(ptr2index(pp));
@ -1325,7 +1366,7 @@ irealloc(void *ptr, size_t size)
/* /*
* Free a sequence of pages * Free a sequence of pages
*/ */
static __inline__ void
static void
free_pages(void *ptr, u_long index, struct pginfo * info) free_pages(void *ptr, u_long index, struct pginfo * info)
{ {
u_long i, pidx, lidx; u_long i, pidx, lidx;
@ -1409,8 +1450,8 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
mprotect(ptr, l, PROT_NONE); mprotect(ptr, l, PROT_NONE);
/* Add to free-list. */ /* 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->page = ptr;
px->pdir = spi; px->pdir = spi;
px->size = l; 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. */ /* XXX: We could realloc/shrink the pagedir here I guess. */
if (pf->size == 0) { /* Remove from free-list as well. */ if (pf->size == 0) { /* Remove from free-list as well. */
if (px) if (px)
ifree(px);
put_pgfree(px);
if ((px = pf->prev) != &free_list) { if ((px = pf->prev) != &free_list) {
if (pi == NULL && last_index == (index - 1)) { if (pi == NULL && last_index == (index - 1)) {
if (spi == NULL) { if (spi == NULL) {
@ -1592,7 +1633,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
pdi_mod) - 1; pdi_mod) - 1;
for (pi = spi, i = index; for (pi = spi, i = index;
pd[PI_OFF(i)] == MALLOC_NOT_MINE; pd[PI_OFF(i)] == MALLOC_NOT_MINE;
i--)
i--) {
#ifdef MALLOC_EXTRA_SANITY #ifdef MALLOC_EXTRA_SANITY
if (!PI_OFF(i)) { if (!PI_OFF(i)) {
pi = pi->prev; pi = pi->prev;
@ -1601,10 +1642,8 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
pd = pi->base; pd = pi->base;
i = (PD_IDX(pi->dirnum) + 1) * pdi_mod; i = (PD_IDX(pi->dirnum) + 1) * pdi_mod;
} }
#else /* !MALLOC_EXTRA_SANITY */
{
}
#endif /* MALLOC_EXTRA_SANITY */ #endif /* MALLOC_EXTRA_SANITY */
}
malloc_brk = index2ptr(i + 1); malloc_brk = index2ptr(i + 1);
} }
last_index = i; last_index = i;
@ -1622,7 +1661,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
} }
not_return: not_return:
if (pt != NULL) if (pt != NULL)
ifree(pt);
put_pgfree(pt);
} }
/* /*
@ -1630,16 +1669,41 @@ not_return:
*/ */
/* ARGSUSED */ /* 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; struct pdinfo *pi;
#ifdef MALLOC_EXTRA_SANITY #ifdef MALLOC_EXTRA_SANITY
u_long pidx; u_long pidx;
#endif /* MALLOC_EXTRA_SANITY */ #endif /* MALLOC_EXTRA_SANITY */
u_long index;
void *vp; void *vp;
long i; 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 */ /* Find the chunk number on the page */
i = ((u_long) ptr & malloc_pagemask) >> info->shift; 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) if (info->size == 0)
mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE); 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); ifree(vp);
} }
@ -1736,7 +1799,7 @@ ifree(void *ptr)
return; return;
if (malloc_ptrguard && PTR_ALIGNED(ptr)) if (malloc_ptrguard && PTR_ALIGNED(ptr))
ptr = (char *) ptr - PTR_GAP;
ptr = (char *)ptr - PTR_GAP;
index = ptr2index(ptr); index = ptr2index(ptr);
@ -1750,6 +1813,7 @@ ifree(void *ptr)
wrtwarning("ifree: junk pointer, too high to make sense"); wrtwarning("ifree: junk pointer, too high to make sense");
return; return;
} }
pdir_lookup(index, &pi); pdir_lookup(index, &pi);
#ifdef MALLOC_EXTRA_SANITY #ifdef MALLOC_EXTRA_SANITY
pidx = PI_IDX(index); pidx = PI_IDX(index);
@ -1769,11 +1833,11 @@ ifree(void *ptr)
if (info < MALLOC_MAGIC) if (info < MALLOC_MAGIC)
free_pages(ptr, index, info); free_pages(ptr, index, info);
else else
free_bytes(ptr, index, info);
free_bytes(ptr);
/* does not matter if malloc_bytes fails */ /* does not matter if malloc_bytes fails */
if (px == NULL) if (px == NULL)
px = malloc_bytes(sizeof *px);
px = alloc_pgfree();
return; return;
} }


Loading…
Cancel
Save