summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorotto <>2022-02-26 16:14:42 +0000
committerotto <>2022-02-26 16:14:42 +0000
commitc5273921395aa49c913ac5a1325c7eb78a50292c (patch)
treeeedb761db3c5aebae973ef73ef1f63a888027635
parent241f81f05560b5b5be14de78e45d18c5a5899b06 (diff)
downloadopenbsd-c5273921395aa49c913ac5a1325c7eb78a50292c.tar.gz
openbsd-c5273921395aa49c913ac5a1325c7eb78a50292c.tar.bz2
openbsd-c5273921395aa49c913ac5a1325c7eb78a50292c.zip
Currently malloc caches a number of free'ed regions up to 128k
in size. This cache is indexed by size (in # of pages), so it is very quick to check. Some programs allocate and deallocate larger allocations in a frantic way. Accomodate those programs by also keeping a cache of regions between 128k and 2M, in a cache of variable sized regions. Tested by many in snaps; ok deraadt@
-rw-r--r--src/lib/libc/stdlib/malloc.c193
1 files changed, 160 insertions, 33 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c
index 64273b49fe..a700087534 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.272 2021/09/19 09:15:22 tb Exp $ */ 1/* $OpenBSD: malloc.c,v 1.273 2022/02/26 16:14:42 otto Exp $ */
2/* 2/*
3 * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> 3 * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net>
4 * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> 4 * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org>
@@ -113,13 +113,27 @@ struct region_info {
113 113
114LIST_HEAD(chunk_head, chunk_info); 114LIST_HEAD(chunk_head, chunk_info);
115 115
116#define MAX_CACHEABLE_SIZE 32 116/*
117struct cache { 117 * Two caches, one for "small" regions, one for "big".
118 void *pages[MALLOC_MAXCACHE]; 118 * Small cache is an array per size, big cache is one array with different
119 * sized regions
120 */
121#define MAX_SMALLCACHEABLE_SIZE 32
122#define MAX_BIGCACHEABLE_SIZE 512
123/* If the total # of pages is larger than this, evict before inserting */
124#define BIGCACHE_FILL(sz) (MAX_BIGCACHEABLE_SIZE * (sz) / 4)
125
126struct smallcache {
127 void **pages;
119 ushort length; 128 ushort length;
120 ushort max; 129 ushort max;
121}; 130};
122 131
132struct bigcache {
133 void *page;
134 size_t psize;
135};
136
123struct dir_info { 137struct dir_info {
124 u_int32_t canary1; 138 u_int32_t canary1;
125 int active; /* status of malloc */ 139 int active; /* status of malloc */
@@ -139,7 +153,10 @@ struct dir_info {
139 void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1]; 153 void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1];
140 u_char rbytes[32]; /* random bytes */ 154 u_char rbytes[32]; /* random bytes */
141 /* free pages cache */ 155 /* free pages cache */
142 struct cache cache[MAX_CACHEABLE_SIZE]; 156 struct smallcache smallcache[MAX_SMALLCACHEABLE_SIZE];
157 size_t bigcache_used;
158 size_t bigcache_size;
159 struct bigcache *bigcache;
143#ifdef MALLOC_STATS 160#ifdef MALLOC_STATS
144 size_t inserts; 161 size_t inserts;
145 size_t insert_collisions; 162 size_t insert_collisions;
@@ -207,7 +224,7 @@ struct malloc_readonly {
207#ifdef MALLOC_STATS 224#ifdef MALLOC_STATS
208 int malloc_stats; /* dump statistics at end */ 225 int malloc_stats; /* dump statistics at end */
209#endif 226#endif
210 u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ 227 u_int32_t malloc_canary; /* Matched against ones in pool */
211}; 228};
212 229
213/* This object is mapped PROT_READ after initialisation to prevent tampering */ 230/* This object is mapped PROT_READ after initialisation to prevent tampering */
@@ -714,18 +731,61 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear)
714 size_t psz = sz >> MALLOC_PAGESHIFT; 731 size_t psz = sz >> MALLOC_PAGESHIFT;
715 void *r; 732 void *r;
716 u_short i; 733 u_short i;
717 struct cache *cache; 734 struct smallcache *cache;
718 735
719 if (sz != PAGEROUND(sz) || psz == 0) 736 if (sz != PAGEROUND(sz) || psz == 0)
720 wrterror(d, "munmap round"); 737 wrterror(d, "munmap round");
721 738
722 if (psz > MAX_CACHEABLE_SIZE || d->cache[psz - 1].max == 0) { 739 if (d->bigcache_size > 0 && psz > MAX_SMALLCACHEABLE_SIZE &&
740 psz <= MAX_BIGCACHEABLE_SIZE) {
741 u_short base = getrbyte(d);
742 u_short j;
743
744 /* don't look through all slots */
745 for (j = 0; j < d->bigcache_size / 4; j++) {
746 i = (base + j) % d->bigcache_size;
747 if (d->bigcache_used <
748 BIGCACHE_FILL(d->bigcache_size)) {
749 if (d->bigcache[i].psize == 0)
750 break;
751 } else {
752 if (d->bigcache[i].psize != 0)
753 break;
754 }
755 }
756 /* if we didn't find a preferred slot, use random one */
757 if (d->bigcache[i].psize != 0) {
758 size_t tmp;
759
760 r = d->bigcache[i].page;
761 d->bigcache_used -= d->bigcache[i].psize;
762 tmp = d->bigcache[i].psize << MALLOC_PAGESHIFT;
763 if (!mopts.malloc_freeunmap)
764 validate_junk(d, r, tmp);
765 if (munmap(r, tmp))
766 wrterror(d, "munmap %p", r);
767 STATS_SUB(d->malloc_used, tmp);
768 }
769
770 if (clear > 0)
771 explicit_bzero(p, clear);
772 if (mopts.malloc_freeunmap) {
773 if (mprotect(p, sz, PROT_NONE))
774 wrterror(d, "mprotect %p", r);
775 } else
776 junk_free(d->malloc_junk, p, sz);
777 d->bigcache[i].page = p;
778 d->bigcache[i].psize = psz;
779 d->bigcache_used += psz;
780 return;
781 }
782 if (psz > MAX_SMALLCACHEABLE_SIZE || d->smallcache[psz - 1].max == 0) {
723 if (munmap(p, sz)) 783 if (munmap(p, sz))
724 wrterror(d, "munmap %p", p); 784 wrterror(d, "munmap %p", p);
725 STATS_SUB(d->malloc_used, sz); 785 STATS_SUB(d->malloc_used, sz);
726 return; 786 return;
727 } 787 }
728 cache = &d->cache[psz - 1]; 788 cache = &d->smallcache[psz - 1];
729 if (cache->length == cache->max) { 789 if (cache->length == cache->max) {
730 /* use a random slot */ 790 /* use a random slot */
731 i = getrbyte(d) % cache->max; 791 i = getrbyte(d) % cache->max;
@@ -753,9 +813,10 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear)
753static void * 813static void *
754map(struct dir_info *d, size_t sz, int zero_fill) 814map(struct dir_info *d, size_t sz, int zero_fill)
755{ 815{
756 size_t i, psz = sz >> MALLOC_PAGESHIFT; 816 size_t psz = sz >> MALLOC_PAGESHIFT;
817 u_short i;
757 void *p; 818 void *p;
758 struct cache *cache; 819 struct smallcache *cache;
759 820
760 if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || 821 if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) ||
761 d->canary1 != ~d->canary2) 822 d->canary1 != ~d->canary2)
@@ -764,8 +825,35 @@ map(struct dir_info *d, size_t sz, int zero_fill)
764 wrterror(d, "map round"); 825 wrterror(d, "map round");
765 826
766 827
767 if (psz <= MAX_CACHEABLE_SIZE && d->cache[psz - 1].max > 0) { 828 if (d->bigcache_size > 0 && psz > MAX_SMALLCACHEABLE_SIZE &&
768 cache = &d->cache[psz - 1]; 829 psz <= MAX_BIGCACHEABLE_SIZE) {
830 size_t base = getrbyte(d);
831 size_t cached = d->bigcache_used;
832 ushort j;
833
834 for (j = 0; j < d->bigcache_size && cached >= psz; j++) {
835 i = (j + base) % d->bigcache_size;
836 if (d->bigcache[i].psize == psz) {
837 p = d->bigcache[i].page;
838 d->bigcache_used -= psz;
839 d->bigcache[i].page = NULL;
840 d->bigcache[i].psize = 0;
841
842 if (!mopts.malloc_freeunmap)
843 validate_junk(d, p, sz);
844 if (mopts.malloc_freeunmap)
845 mprotect(p, sz, PROT_READ | PROT_WRITE);
846 if (zero_fill)
847 memset(p, 0, sz);
848 else if (mopts.malloc_freeunmap)
849 junk_free(d->malloc_junk, p, sz);
850 return p;
851 }
852 cached -= d->bigcache[i].psize;
853 }
854 }
855 if (psz <= MAX_SMALLCACHEABLE_SIZE && d->smallcache[psz - 1].max > 0) {
856 cache = &d->smallcache[psz - 1];
769 if (cache->length > 0) { 857 if (cache->length > 0) {
770 if (cache->length == 1) 858 if (cache->length == 1)
771 p = cache->pages[--cache->length]; 859 p = cache->pages[--cache->length];
@@ -795,10 +883,12 @@ map(struct dir_info *d, size_t sz, int zero_fill)
795 void *q = (char*)p + i * sz; 883 void *q = (char*)p + i * sz;
796 cache->pages[i] = q; 884 cache->pages[i] = q;
797 if (!mopts.malloc_freeunmap) 885 if (!mopts.malloc_freeunmap)
798 junk_free(d->malloc_junk, q, sz); 886 junk_free(d->malloc_junk, q,
887 sz);
799 } 888 }
800 if (mopts.malloc_freeunmap) 889 if (mopts.malloc_freeunmap)
801 mprotect(p, (cache->max - 1) * sz, PROT_NONE); 890 mprotect(p, (cache->max - 1) * sz,
891 PROT_NONE);
802 p = (char*)p + (cache->max - 1) * sz; 892 p = (char*)p + (cache->max - 1) * sz;
803 /* zero fill not needed */ 893 /* zero fill not needed */
804 return p; 894 return p;
@@ -1161,8 +1251,9 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f)
1161 } else { 1251 } else {
1162 if (pool->malloc_junk == 2) { 1252 if (pool->malloc_junk == 2) {
1163 if (zero_fill) 1253 if (zero_fill)
1164 memset((char *)p + sz - mopts.malloc_guard, 1254 memset((char *)p + sz -
1165 SOME_JUNK, psz - sz); 1255 mopts.malloc_guard, SOME_JUNK,
1256 psz - sz);
1166 else 1257 else
1167 memset(p, SOME_JUNK, 1258 memset(p, SOME_JUNK,
1168 psz - mopts.malloc_guard); 1259 psz - mopts.malloc_guard);
@@ -1224,13 +1315,33 @@ _malloc_init(int from_rthreads)
1224 if (i == 0) { 1315 if (i == 0) {
1225 omalloc_poolinit(&d, MAP_CONCEAL); 1316 omalloc_poolinit(&d, MAP_CONCEAL);
1226 d->malloc_junk = 2; 1317 d->malloc_junk = 2;
1227 for (j = 0; j < MAX_CACHEABLE_SIZE; j++) 1318 d->bigcache_size = 0;
1228 d->cache[j].max = 0; 1319 for (j = 0; j < MAX_SMALLCACHEABLE_SIZE; j++)
1320 d->smallcache[j].max = 0;
1229 } else { 1321 } else {
1322 size_t sz = 0;
1323
1230 omalloc_poolinit(&d, 0); 1324 omalloc_poolinit(&d, 0);
1231 d->malloc_junk = mopts.def_malloc_junk; 1325 d->malloc_junk = mopts.def_malloc_junk;
1232 for (j = 0; j < MAX_CACHEABLE_SIZE; j++) 1326 d->bigcache_size = mopts.def_maxcache;
1233 d->cache[j].max = mopts.def_maxcache >> (j / 8); 1327 for (j = 0; j < MAX_SMALLCACHEABLE_SIZE; j++) {
1328 d->smallcache[j].max =
1329 mopts.def_maxcache >> (j / 8);
1330 sz += d->smallcache[j].max * sizeof(void *);
1331 }
1332 sz += d->bigcache_size * sizeof(struct bigcache);
1333 if (sz > 0) {
1334 void *p = MMAP(sz, 0);
1335 if (p == MAP_FAILED)
1336 wrterror(NULL,
1337 "malloc init mmap failed");
1338 for (j = 0; j < MAX_SMALLCACHEABLE_SIZE; j++) {
1339 d->smallcache[j].pages = p;
1340 p = (char *)p + d->smallcache[j].max *
1341 sizeof(void *);
1342 }
1343 d->bigcache = p;
1344 }
1234 } 1345 }
1235 d->mutex = i; 1346 d->mutex = i;
1236 mopts.malloc_pool[i] = d; 1347 mopts.malloc_pool[i] = d;
@@ -1348,8 +1459,11 @@ ofree(struct dir_info **argpool, void *p, int clear, int check, size_t argsz)
1348 REALSIZE(sz, r); 1459 REALSIZE(sz, r);
1349 if (pool->mmap_flag) { 1460 if (pool->mmap_flag) {
1350 clear = 1; 1461 clear = 1;
1351 if (!check) 1462 if (!check) {
1352 argsz = sz; 1463 argsz = sz;
1464 if (sz > MALLOC_MAXCHUNK)
1465 argsz -= mopts.malloc_guard;
1466 }
1353 } 1467 }
1354 if (check) { 1468 if (check) {
1355 if (sz <= MALLOC_MAXCHUNK) { 1469 if (sz <= MALLOC_MAXCHUNK) {
@@ -1609,7 +1723,8 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f)
1609 wrterror(pool, "mprotect"); 1723 wrterror(pool, "mprotect");
1610 } 1724 }
1611 if (munmap((char *)r->p + rnewsz, roldsz - rnewsz)) 1725 if (munmap((char *)r->p + rnewsz, roldsz - rnewsz))
1612 wrterror(pool, "munmap %p", (char *)r->p + rnewsz); 1726 wrterror(pool, "munmap %p", (char *)r->p +
1727 rnewsz);
1613 STATS_SUB(pool->malloc_used, roldsz - rnewsz); 1728 STATS_SUB(pool->malloc_used, roldsz - rnewsz);
1614 r->size = gnewsz; 1729 r->size = gnewsz;
1615 if (MALLOC_MOVE_COND(gnewsz)) { 1730 if (MALLOC_MOVE_COND(gnewsz)) {
@@ -2091,7 +2206,7 @@ putleakinfo(void *f, size_t sz, int cnt)
2091{ 2206{
2092 struct leaknode key, *p; 2207 struct leaknode key, *p;
2093 static struct leaknode *page; 2208 static struct leaknode *page;
2094 static int used; 2209 static unsigned int used;
2095 2210
2096 if (cnt == 0 || page == MAP_FAILED) 2211 if (cnt == 0 || page == MAP_FAILED)
2097 return; 2212 return;
@@ -2123,7 +2238,7 @@ static void
2123dump_leaks(int fd) 2238dump_leaks(int fd)
2124{ 2239{
2125 struct leaknode *p; 2240 struct leaknode *p;
2126 int i = 0; 2241 unsigned int i = 0;
2127 2242
2128 dprintf(fd, "Leak report\n"); 2243 dprintf(fd, "Leak report\n");
2129 dprintf(fd, " f sum # avg\n"); 2244 dprintf(fd, " f sum # avg\n");
@@ -2196,16 +2311,25 @@ dump_free_chunk_info(int fd, struct dir_info *d)
2196static void 2311static void
2197dump_free_page_info(int fd, struct dir_info *d) 2312dump_free_page_info(int fd, struct dir_info *d)
2198{ 2313{
2199 struct cache *cache; 2314 struct smallcache *cache;
2200 size_t i, total = 0; 2315 size_t i, total = 0;
2201 2316
2202 dprintf(fd, "Cached:\n"); 2317 dprintf(fd, "Cached in small cache:\n");
2203 for (i = 0; i < MAX_CACHEABLE_SIZE; i++) { 2318 for (i = 0; i < MAX_SMALLCACHEABLE_SIZE; i++) {
2204 cache = &d->cache[i]; 2319 cache = &d->smallcache[i];
2205 if (cache->length != 0) 2320 if (cache->length != 0)
2206 dprintf(fd, "%zu(%u): %u = %zu\n", i + 1, cache->max, cache->length, cache->length * (i + 1)); 2321 dprintf(fd, "%zu(%u): %u = %zu\n", i + 1, cache->max,
2322 cache->length, cache->length * (i + 1));
2207 total += cache->length * (i + 1); 2323 total += cache->length * (i + 1);
2208 } 2324 }
2325
2326 dprintf(fd, "Cached in big cache: %zu/%zu\n", d->bigcache_used,
2327 d->bigcache_size);
2328 for (i = 0; i < d->bigcache_size; i++) {
2329 if (d->bigcache[i].psize != 0)
2330 dprintf(fd, "%zu: %zu\n", i, d->bigcache[i].psize);
2331 total += d->bigcache[i].psize;
2332 }
2209 dprintf(fd, "Free pages cached: %zu\n", total); 2333 dprintf(fd, "Free pages cached: %zu\n", total);
2210} 2334}
2211 2335
@@ -2232,7 +2356,8 @@ malloc_dump1(int fd, int poolno, struct dir_info *d)
2232 dump_free_chunk_info(fd, d); 2356 dump_free_chunk_info(fd, d);
2233 dump_free_page_info(fd, d); 2357 dump_free_page_info(fd, d);
2234 dprintf(fd, 2358 dprintf(fd,
2235 "slot) hash d type page f size [free/n]\n"); 2359 "slot) hash d type page f "
2360 "size [free/n]\n");
2236 for (i = 0; i < d->regions_total; i++) { 2361 for (i = 0; i < d->regions_total; i++) {
2237 if (d->r[i].p != NULL) { 2362 if (d->r[i].p != NULL) {
2238 size_t h = hash(d->r[i].p) & 2363 size_t h = hash(d->r[i].p) &
@@ -2298,13 +2423,15 @@ DEF_WEAK(malloc_gdump);
2298static void 2423static void
2299malloc_exit(void) 2424malloc_exit(void)
2300{ 2425{
2301 int save_errno = errno, fd, i; 2426 int save_errno = errno, fd;
2427 unsigned i;
2302 2428
2303 fd = open("malloc.out", O_RDWR|O_APPEND); 2429 fd = open("malloc.out", O_RDWR|O_APPEND);
2304 if (fd != -1) { 2430 if (fd != -1) {
2305 dprintf(fd, "******** Start dump %s *******\n", __progname); 2431 dprintf(fd, "******** Start dump %s *******\n", __progname);
2306 dprintf(fd, 2432 dprintf(fd,
2307 "MT=%d M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u G=%zu\n", 2433 "MT=%d M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u "
2434 "G=%zu\n",
2308 mopts.malloc_mt, mopts.malloc_mutexes, 2435 mopts.malloc_mt, mopts.malloc_mutexes,
2309 mopts.internal_funcs, mopts.malloc_freecheck, 2436 mopts.internal_funcs, mopts.malloc_freecheck,
2310 mopts.malloc_freeunmap, mopts.def_malloc_junk, 2437 mopts.malloc_freeunmap, mopts.def_malloc_junk,