summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortedu <>2006-10-24 04:35:30 +0000
committertedu <>2006-10-24 04:35:30 +0000
commit182ec138408d8727b97aa8a6b7faef3a1c05037b (patch)
treef4d3456af2c02712b0b326b7e2de95295cbe24a3
parente1dcd644aa891adcbd561d7688084819368c4139 (diff)
downloadopenbsd-182ec138408d8727b97aa8a6b7faef3a1c05037b.tar.gz
openbsd-182ec138408d8727b97aa8a6b7faef3a1c05037b.tar.bz2
openbsd-182ec138408d8727b97aa8a6b7faef3a1c05037b.zip
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@
-rw-r--r--src/lib/libc/stdlib/malloc.c186
1 files changed, 125 insertions, 61 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c
index 028eff2b2d..ace34c96f6 100644
--- a/src/lib/libc/stdlib/malloc.c
+++ b/src/lib/libc/stdlib/malloc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: malloc.c,v 1.83 2006/05/14 19:53:40 otto Exp $ */ 1/* $OpenBSD: malloc.c,v 1.84 2006/10/24 04:35:30 tedu Exp $ */
2 2
3/* 3/*
4 * ---------------------------------------------------------------------------- 4 * ----------------------------------------------------------------------------
@@ -75,6 +75,18 @@
75#define malloc_pageshift (PGSHIFT) 75#define malloc_pageshift (PGSHIFT)
76#endif 76#endif
77 77
78#ifndef malloc_minsize
79#define malloc_minsize 16UL
80#endif
81
82#if !defined(malloc_pagesize)
83#define malloc_pagesize (1UL<<malloc_pageshift)
84#endif
85
86/* How many bits per u_long in the bitmap */
87#define MALLOC_BITS (NBBY * sizeof(u_long))
88
89
78/* 90/*
79 * No user serviceable parts behind this point. 91 * No user serviceable parts behind this point.
80 * 92 *
@@ -87,12 +99,9 @@ struct pginfo {
87 u_short shift; /* How far to shift for this size chunks */ 99 u_short shift; /* How far to shift for this size chunks */
88 u_short free; /* How many free chunks */ 100 u_short free; /* How many free chunks */
89 u_short total; /* How many chunk */ 101 u_short total; /* How many chunk */
90 u_long bits[1];/* Which chunks are free */ 102 u_long bits[(malloc_pagesize / malloc_minsize) / MALLOC_BITS];/* Which chunks are free */
91}; 103};
92 104
93/* How many bits per u_long in the bitmap */
94#define MALLOC_BITS (NBBY * sizeof(u_long))
95
96/* 105/*
97 * This structure describes a number of free pages. 106 * This structure describes a number of free pages.
98 */ 107 */
@@ -113,14 +122,6 @@ struct pgfree {
113#define MALLOC_FOLLOW ((struct pginfo*) 3) 122#define MALLOC_FOLLOW ((struct pginfo*) 3)
114#define MALLOC_MAGIC ((struct pginfo*) 4) 123#define MALLOC_MAGIC ((struct pginfo*) 4)
115 124
116#ifndef malloc_minsize
117#define malloc_minsize 16UL
118#endif
119
120#if !defined(malloc_pagesize)
121#define malloc_pagesize (1UL<<malloc_pageshift)
122#endif
123
124#if ((1UL<<malloc_pageshift) != malloc_pagesize) 125#if ((1UL<<malloc_pageshift) != malloc_pagesize)
125#error "(1UL<<malloc_pageshift) != malloc_pagesize" 126#error "(1UL<<malloc_pageshift) != malloc_pagesize"
126#endif 127#endif
@@ -254,6 +255,66 @@ static void ifree(void *ptr);
254static void *irealloc(void *ptr, size_t size); 255static void *irealloc(void *ptr, size_t size);
255static void *malloc_bytes(size_t size); 256static void *malloc_bytes(size_t size);
256 257
258static struct pginfo *pginfo_list;
259
260static struct pgfree *pgfree_list;
261
262static struct pgfree *
263alloc_pgfree()
264{
265 struct pgfree *p;
266 int i;
267
268 if (pgfree_list == NULL) {
269 p = MMAP(malloc_pagesize);
270 if (!p)
271 return NULL;
272 for (i = 0; i < malloc_pagesize / sizeof(*p); i++) {
273 p[i].next = pgfree_list;
274 pgfree_list = &p[i];
275 }
276 }
277 p = pgfree_list;
278 pgfree_list = p->next;
279 memset(p, 0, sizeof *p);
280 return p;
281}
282
283static struct pginfo *
284alloc_pginfo()
285{
286 struct pginfo *p;
287 int i;
288
289 if (pginfo_list == NULL) {
290 p = MMAP(malloc_pagesize);
291 if (!p)
292 return NULL;
293 for (i = 0; i < malloc_pagesize / sizeof(*p); i++) {
294 p[i].next = pginfo_list;
295 pginfo_list = &p[i];
296 }
297 }
298 p = pginfo_list;
299 pginfo_list = p->next;
300 memset(p, 0, sizeof *p);
301 return p;
302}
303
304static void
305put_pgfree(struct pgfree *p)
306{
307 p->next = pgfree_list;
308 pgfree_list = p;
309}
310
311static void
312put_pginfo(struct pginfo *p)
313{
314 p->next = pginfo_list;
315 pginfo_list = p;
316}
317
257/* 318/*
258 * Function for page directory lookup. 319 * Function for page directory lookup.
259 */ 320 */
@@ -764,12 +825,12 @@ malloc_init(void)
764static void * 825static void *
765malloc_pages(size_t size) 826malloc_pages(size_t size)
766{ 827{
767 void *p, *delay_free = NULL, *tp; 828 void *p, *tp;
768 int i; 829 int i;
769 struct pginfo **pd; 830 struct pginfo **pd;
770 struct pdinfo *pi; 831 struct pdinfo *pi;
771 u_long pidx, index; 832 u_long pidx, index;
772 struct pgfree *pf; 833 struct pgfree *pf, *delay_free = NULL;
773 834
774 size = pageround(size) + malloc_guard; 835 size = pageround(size) + malloc_guard;
775 836
@@ -936,7 +997,7 @@ malloc_pages(size_t size)
936 if (px == NULL) 997 if (px == NULL)
937 px = delay_free; 998 px = delay_free;
938 else 999 else
939 ifree(delay_free); 1000 put_pgfree(delay_free);
940 } 1001 }
941 return (p); 1002 return (p);
942} 1003}
@@ -945,7 +1006,7 @@ malloc_pages(size_t size)
945 * Allocate a page of fragments 1006 * Allocate a page of fragments
946 */ 1007 */
947 1008
948static __inline__ int 1009static int
949malloc_make_chunks(int bits) 1010malloc_make_chunks(int bits)
950{ 1011{
951 struct pginfo *bp, **pd; 1012 struct pginfo *bp, **pd;
@@ -955,17 +1016,13 @@ malloc_make_chunks(int bits)
955#endif /* MALLOC_EXTRA_SANITY */ 1016#endif /* MALLOC_EXTRA_SANITY */
956 void *pp; 1017 void *pp;
957 long i, k; 1018 long i, k;
958 size_t l;
959 1019
960 /* Allocate a new bucket */ 1020 /* Allocate a new bucket */
961 pp = malloc_pages((size_t) malloc_pagesize); 1021 pp = malloc_pages((size_t)malloc_pagesize);
962 if (pp == NULL) 1022 if (pp == NULL)
963 return (0); 1023 return (0);
964 1024
965 /* Find length of admin structure */ 1025 /* Find length of admin structure */
966 l = sizeof *bp - sizeof(u_long);
967 l += sizeof(u_long) *
968 (((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS);
969 1026
970 /* Don't waste more than two chunks on this */ 1027 /* Don't waste more than two chunks on this */
971 1028
@@ -975,14 +1032,10 @@ malloc_make_chunks(int bits)
975 * pginfo page. 1032 * pginfo page.
976 * --> Treat it like the big chunk alloc, get a second data page. 1033 * --> Treat it like the big chunk alloc, get a second data page.
977 */ 1034 */
978 if (bits != 0 && (1UL << (bits)) <= l + l) { 1035 bp = alloc_pginfo();
979 bp = (struct pginfo *) pp; 1036 if (bp == NULL) {
980 } else { 1037 ifree(pp);
981 bp = (struct pginfo *) imalloc(l); 1038 return (0);
982 if (bp == NULL) {
983 ifree(pp);
984 return (0);
985 }
986 } 1039 }
987 1040
988 /* memory protect the page allocated in the malloc(0) case */ 1041 /* memory protect the page allocated in the malloc(0) case */
@@ -998,7 +1051,7 @@ malloc_make_chunks(int bits)
998 k = mprotect(pp, malloc_pagesize, PROT_NONE); 1051 k = mprotect(pp, malloc_pagesize, PROT_NONE);
999 if (k < 0) { 1052 if (k < 0) {
1000 ifree(pp); 1053 ifree(pp);
1001 ifree(bp); 1054 put_pginfo(bp);
1002 return (0); 1055 return (0);
1003 } 1056 }
1004 } else { 1057 } else {
@@ -1019,18 +1072,6 @@ malloc_make_chunks(int bits)
1019 for (; i < k; i++) 1072 for (; i < k; i++)
1020 bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS); 1073 bp->bits[i / MALLOC_BITS] |= 1UL << (i % MALLOC_BITS);
1021 1074
1022 k = (long)l;
1023 if (bp == bp->page) {
1024 /* Mark the ones we stole for ourselves */
1025 for (i = 0; k > 0; i++) {
1026 bp->bits[i / MALLOC_BITS] &= ~(1UL << (i % MALLOC_BITS));
1027 bp->free--;
1028 bp->total--;
1029 k -= (1 << bits);
1030 }
1031 }
1032 /* MALLOC_LOCK */
1033
1034 pdir_lookup(ptr2index(pp), &pi); 1075 pdir_lookup(ptr2index(pp), &pi);
1035#ifdef MALLOC_EXTRA_SANITY 1076#ifdef MALLOC_EXTRA_SANITY
1036 pidx = PI_IDX(ptr2index(pp)); 1077 pidx = PI_IDX(ptr2index(pp));
@@ -1325,7 +1366,7 @@ irealloc(void *ptr, size_t size)
1325/* 1366/*
1326 * Free a sequence of pages 1367 * Free a sequence of pages
1327 */ 1368 */
1328static __inline__ void 1369static void
1329free_pages(void *ptr, u_long index, struct pginfo * info) 1370free_pages(void *ptr, u_long index, struct pginfo * info)
1330{ 1371{
1331 u_long i, pidx, lidx; 1372 u_long i, pidx, lidx;
@@ -1409,8 +1450,8 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
1409 mprotect(ptr, l, PROT_NONE); 1450 mprotect(ptr, l, PROT_NONE);
1410 1451
1411 /* Add to free-list. */ 1452 /* Add to free-list. */
1412 if (px == NULL && (px = malloc_bytes(sizeof *px)) == NULL) 1453 if (px == NULL && (px = alloc_pgfree()) == NULL)
1413 goto not_return; 1454 goto not_return;
1414 px->page = ptr; 1455 px->page = ptr;
1415 px->pdir = spi; 1456 px->pdir = spi;
1416 px->size = l; 1457 px->size = l;
@@ -1578,7 +1619,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
1578 /* XXX: We could realloc/shrink the pagedir here I guess. */ 1619 /* XXX: We could realloc/shrink the pagedir here I guess. */
1579 if (pf->size == 0) { /* Remove from free-list as well. */ 1620 if (pf->size == 0) { /* Remove from free-list as well. */
1580 if (px) 1621 if (px)
1581 ifree(px); 1622 put_pgfree(px);
1582 if ((px = pf->prev) != &free_list) { 1623 if ((px = pf->prev) != &free_list) {
1583 if (pi == NULL && last_index == (index - 1)) { 1624 if (pi == NULL && last_index == (index - 1)) {
1584 if (spi == NULL) { 1625 if (spi == NULL) {
@@ -1592,7 +1633,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
1592 pdi_mod) - 1; 1633 pdi_mod) - 1;
1593 for (pi = spi, i = index; 1634 for (pi = spi, i = index;
1594 pd[PI_OFF(i)] == MALLOC_NOT_MINE; 1635 pd[PI_OFF(i)] == MALLOC_NOT_MINE;
1595 i--) 1636 i--) {
1596#ifdef MALLOC_EXTRA_SANITY 1637#ifdef MALLOC_EXTRA_SANITY
1597 if (!PI_OFF(i)) { 1638 if (!PI_OFF(i)) {
1598 pi = pi->prev; 1639 pi = pi->prev;
@@ -1601,10 +1642,8 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
1601 pd = pi->base; 1642 pd = pi->base;
1602 i = (PD_IDX(pi->dirnum) + 1) * pdi_mod; 1643 i = (PD_IDX(pi->dirnum) + 1) * pdi_mod;
1603 } 1644 }
1604#else /* !MALLOC_EXTRA_SANITY */
1605 {
1606 }
1607#endif /* MALLOC_EXTRA_SANITY */ 1645#endif /* MALLOC_EXTRA_SANITY */
1646 }
1608 malloc_brk = index2ptr(i + 1); 1647 malloc_brk = index2ptr(i + 1);
1609 } 1648 }
1610 last_index = i; 1649 last_index = i;
@@ -1622,7 +1661,7 @@ free_pages(void *ptr, u_long index, struct pginfo * info)
1622 } 1661 }
1623not_return: 1662not_return:
1624 if (pt != NULL) 1663 if (pt != NULL)
1625 ifree(pt); 1664 put_pgfree(pt);
1626} 1665}
1627 1666
1628/* 1667/*
@@ -1630,16 +1669,41 @@ not_return:
1630 */ 1669 */
1631 1670
1632/* ARGSUSED */ 1671/* ARGSUSED */
1633static __inline__ void 1672static void
1634free_bytes(void *ptr, u_long index, struct pginfo * info) 1673free_bytes(void *ptr)
1635{ 1674{
1636 struct pginfo **mp, **pd; 1675 struct pginfo **mp, **pd, *info;
1637 struct pdinfo *pi; 1676 struct pdinfo *pi;
1638#ifdef MALLOC_EXTRA_SANITY 1677#ifdef MALLOC_EXTRA_SANITY
1639 u_long pidx; 1678 u_long pidx;
1640#endif /* MALLOC_EXTRA_SANITY */ 1679#endif /* MALLOC_EXTRA_SANITY */
1680 u_long index;
1641 void *vp; 1681 void *vp;
1642 long i; 1682 long i;
1683 void *tmpptr;
1684 unsigned int tmpidx;
1685 /* pointers that we will want to free at some future time */
1686 static void *chunk_buffer[16];
1687
1688
1689 /* delay return, returning a random something from before instead */
1690 tmpidx = arc4random() % 16;
1691 tmpptr = chunk_buffer[tmpidx];
1692 chunk_buffer[tmpidx] = ptr;
1693 ptr = tmpptr;
1694 if (!ptr)
1695 return;
1696
1697 index = ptr2index(ptr);
1698
1699 pdir_lookup(index, &pi);
1700 if (pi != last_dir) {
1701 prev_dir = last_dir;
1702 last_dir = pi;
1703 }
1704 pd = pi->base;
1705 info = pd[PI_OFF(index)];
1706
1643 1707
1644 /* Find the chunk number on the page */ 1708 /* Find the chunk number on the page */
1645 i = ((u_long) ptr & malloc_pagemask) >> info->shift; 1709 i = ((u_long) ptr & malloc_pagemask) >> info->shift;
@@ -1711,9 +1775,8 @@ free_bytes(void *ptr, u_long index, struct pginfo * info)
1711 if (info->size == 0) 1775 if (info->size == 0)
1712 mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE); 1776 mprotect(info->page, malloc_pagesize, PROT_READ | PROT_WRITE);
1713 1777
1714 vp = info->page; /* Order is important ! */ 1778 vp = info->page;
1715 if (vp != (void *) info) 1779 put_pginfo(info);
1716 ifree(info);
1717 ifree(vp); 1780 ifree(vp);
1718} 1781}
1719 1782
@@ -1736,7 +1799,7 @@ ifree(void *ptr)
1736 return; 1799 return;
1737 1800
1738 if (malloc_ptrguard && PTR_ALIGNED(ptr)) 1801 if (malloc_ptrguard && PTR_ALIGNED(ptr))
1739 ptr = (char *) ptr - PTR_GAP; 1802 ptr = (char *)ptr - PTR_GAP;
1740 1803
1741 index = ptr2index(ptr); 1804 index = ptr2index(ptr);
1742 1805
@@ -1750,6 +1813,7 @@ ifree(void *ptr)
1750 wrtwarning("ifree: junk pointer, too high to make sense"); 1813 wrtwarning("ifree: junk pointer, too high to make sense");
1751 return; 1814 return;
1752 } 1815 }
1816
1753 pdir_lookup(index, &pi); 1817 pdir_lookup(index, &pi);
1754#ifdef MALLOC_EXTRA_SANITY 1818#ifdef MALLOC_EXTRA_SANITY
1755 pidx = PI_IDX(index); 1819 pidx = PI_IDX(index);
@@ -1769,11 +1833,11 @@ ifree(void *ptr)
1769 if (info < MALLOC_MAGIC) 1833 if (info < MALLOC_MAGIC)
1770 free_pages(ptr, index, info); 1834 free_pages(ptr, index, info);
1771 else 1835 else
1772 free_bytes(ptr, index, info); 1836 free_bytes(ptr);
1773 1837
1774 /* does not matter if malloc_bytes fails */ 1838 /* does not matter if malloc_bytes fails */
1775 if (px == NULL) 1839 if (px == NULL)
1776 px = malloc_bytes(sizeof *px); 1840 px = alloc_pgfree();
1777 1841
1778 return; 1842 return;
1779} 1843}