diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 186 |
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); | |||
254 | static void *irealloc(void *ptr, size_t size); | 255 | static void *irealloc(void *ptr, size_t size); |
255 | static void *malloc_bytes(size_t size); | 256 | static void *malloc_bytes(size_t size); |
256 | 257 | ||
258 | static struct pginfo *pginfo_list; | ||
259 | |||
260 | static struct pgfree *pgfree_list; | ||
261 | |||
262 | static struct pgfree * | ||
263 | alloc_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 | |||
283 | static struct pginfo * | ||
284 | alloc_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 | |||
304 | static void | ||
305 | put_pgfree(struct pgfree *p) | ||
306 | { | ||
307 | p->next = pgfree_list; | ||
308 | pgfree_list = p; | ||
309 | } | ||
310 | |||
311 | static void | ||
312 | put_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) | |||
764 | static void * | 825 | static void * |
765 | malloc_pages(size_t size) | 826 | malloc_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 | ||
948 | static __inline__ int | 1009 | static int |
949 | malloc_make_chunks(int bits) | 1010 | malloc_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 | */ |
1328 | static __inline__ void | 1369 | static void |
1329 | free_pages(void *ptr, u_long index, struct pginfo * info) | 1370 | free_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 | } |
1623 | not_return: | 1662 | not_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 */ |
1633 | static __inline__ void | 1672 | static void |
1634 | free_bytes(void *ptr, u_long index, struct pginfo * info) | 1673 | free_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 | } |