summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/libc/stdlib/malloc.c244
1 files changed, 142 insertions, 102 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c
index 6167145669..c049b2da54 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.277 2023/02/27 06:47:54 otto Exp $ */ 1/* $OpenBSD: malloc.c,v 1.278 2023/03/25 15:22:06 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>
@@ -67,6 +67,11 @@
67#define MALLOC_CHUNK_LISTS 4 67#define MALLOC_CHUNK_LISTS 4
68#define CHUNK_CHECK_LENGTH 32 68#define CHUNK_CHECK_LENGTH 32
69 69
70#define B2SIZE(b) ((b) * MALLOC_MINSIZE)
71#define B2ALLOC(b) ((b) == 0 ? MALLOC_MINSIZE : \
72 (b) * MALLOC_MINSIZE)
73#define BUCKETS (MALLOC_MAXCHUNK / MALLOC_MINSIZE)
74
70/* 75/*
71 * We move allocations between half a page and a whole page towards the end, 76 * We move allocations between half a page and a whole page towards the end,
72 * subject to alignment constraints. This is the extra headroom we allow. 77 * subject to alignment constraints. This is the extra headroom we allow.
@@ -144,9 +149,9 @@ struct dir_info {
144 int mutex; 149 int mutex;
145 int malloc_mt; /* multi-threaded mode? */ 150 int malloc_mt; /* multi-threaded mode? */
146 /* lists of free chunk info structs */ 151 /* lists of free chunk info structs */
147 struct chunk_head chunk_info_list[MALLOC_MAXSHIFT + 1]; 152 struct chunk_head chunk_info_list[BUCKETS + 1];
148 /* lists of chunks with free slots */ 153 /* lists of chunks with free slots */
149 struct chunk_head chunk_dir[MALLOC_MAXSHIFT + 1][MALLOC_CHUNK_LISTS]; 154 struct chunk_head chunk_dir[BUCKETS + 1][MALLOC_CHUNK_LISTS];
150 /* delayed free chunk slots */ 155 /* delayed free chunk slots */
151 void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1]; 156 void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1];
152 u_char rbytes[32]; /* random bytes */ 157 u_char rbytes[32]; /* random bytes */
@@ -155,6 +160,8 @@ struct dir_info {
155 size_t bigcache_used; 160 size_t bigcache_used;
156 size_t bigcache_size; 161 size_t bigcache_size;
157 struct bigcache *bigcache; 162 struct bigcache *bigcache;
163 void *chunk_pages;
164 size_t chunk_pages_used;
158#ifdef MALLOC_STATS 165#ifdef MALLOC_STATS
159 size_t inserts; 166 size_t inserts;
160 size_t insert_collisions; 167 size_t insert_collisions;
@@ -195,8 +202,7 @@ struct chunk_info {
195 LIST_ENTRY(chunk_info) entries; 202 LIST_ENTRY(chunk_info) entries;
196 void *page; /* pointer to the page */ 203 void *page; /* pointer to the page */
197 u_short canary; 204 u_short canary;
198 u_short size; /* size of this page's chunks */ 205 u_short bucket;
199 u_short shift; /* how far to shift for this size */
200 u_short free; /* how many free chunks */ 206 u_short free; /* how many free chunks */
201 u_short total; /* how many chunks */ 207 u_short total; /* how many chunks */
202 u_short offset; /* requested size table offset */ 208 u_short offset; /* requested size table offset */
@@ -247,11 +253,11 @@ static void malloc_exit(void);
247#endif 253#endif
248 254
249/* low bits of r->p determine size: 0 means >= page size and r->size holding 255/* low bits of r->p determine size: 0 means >= page size and r->size holding
250 * real size, otherwise low bits are a shift count, or 1 for malloc(0) 256 * real size, otherwise low bits is the bucket + 1
251 */ 257 */
252#define REALSIZE(sz, r) \ 258#define REALSIZE(sz, r) \
253 (sz) = (uintptr_t)(r)->p & MALLOC_PAGEMASK, \ 259 (sz) = (uintptr_t)(r)->p & MALLOC_PAGEMASK, \
254 (sz) = ((sz) == 0 ? (r)->size : ((sz) == 1 ? 0 : (1 << ((sz)-1)))) 260 (sz) = ((sz) == 0 ? (r)->size : B2SIZE((sz) - 1))
255 261
256static inline void 262static inline void
257_MALLOC_LEAVE(struct dir_info *d) 263_MALLOC_LEAVE(struct dir_info *d)
@@ -502,7 +508,7 @@ omalloc_poolinit(struct dir_info *d, int mmap_flag)
502 d->r = NULL; 508 d->r = NULL;
503 d->rbytesused = sizeof(d->rbytes); 509 d->rbytesused = sizeof(d->rbytes);
504 d->regions_free = d->regions_total = 0; 510 d->regions_free = d->regions_total = 0;
505 for (i = 0; i <= MALLOC_MAXSHIFT; i++) { 511 for (i = 0; i <= BUCKETS; i++) {
506 LIST_INIT(&d->chunk_info_list[i]); 512 LIST_INIT(&d->chunk_info_list[i]);
507 for (j = 0; j < MALLOC_CHUNK_LISTS; j++) 513 for (j = 0; j < MALLOC_CHUNK_LISTS; j++)
508 LIST_INIT(&d->chunk_dir[i][j]); 514 LIST_INIT(&d->chunk_dir[i][j]);
@@ -720,7 +726,7 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear)
720 726
721 /* don't look through all slots */ 727 /* don't look through all slots */
722 for (j = 0; j < d->bigcache_size / 4; j++) { 728 for (j = 0; j < d->bigcache_size / 4; j++) {
723 i = (base + j) % d->bigcache_size; 729 i = (base + j) & (d->bigcache_size - 1);
724 if (d->bigcache_used < 730 if (d->bigcache_used <
725 BIGCACHE_FILL(d->bigcache_size)) { 731 BIGCACHE_FILL(d->bigcache_size)) {
726 if (d->bigcache[i].psize == 0) 732 if (d->bigcache[i].psize == 0)
@@ -764,10 +770,13 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear)
764 } 770 }
765 cache = &d->smallcache[psz - 1]; 771 cache = &d->smallcache[psz - 1];
766 if (cache->length == cache->max) { 772 if (cache->length == cache->max) {
773 int fresh;
767 /* use a random slot */ 774 /* use a random slot */
768 i = getrbyte(d) % cache->max; 775 i = getrbyte(d) & (cache->max - 1);
769 r = cache->pages[i]; 776 r = cache->pages[i];
770 if (!mopts.malloc_freeunmap) 777 fresh = (uintptr_t)r & 1;
778 *(uintptr_t*)&r &= ~1ULL;
779 if (!fresh && !mopts.malloc_freeunmap)
771 validate_junk(d, r, sz); 780 validate_junk(d, r, sz);
772 if (munmap(r, sz)) 781 if (munmap(r, sz))
773 wrterror(d, "munmap %p", r); 782 wrterror(d, "munmap %p", r);
@@ -809,7 +818,7 @@ map(struct dir_info *d, size_t sz, int zero_fill)
809 ushort j; 818 ushort j;
810 819
811 for (j = 0; j < d->bigcache_size && cached >= psz; j++) { 820 for (j = 0; j < d->bigcache_size && cached >= psz; j++) {
812 i = (j + base) % d->bigcache_size; 821 i = (j + base) & (d->bigcache_size - 1);
813 if (d->bigcache[i].psize == psz) { 822 if (d->bigcache[i].psize == psz) {
814 p = d->bigcache[i].page; 823 p = d->bigcache[i].page;
815 d->bigcache_used -= psz; 824 d->bigcache_used -= psz;
@@ -832,6 +841,7 @@ map(struct dir_info *d, size_t sz, int zero_fill)
832 if (psz <= MAX_SMALLCACHEABLE_SIZE && d->smallcache[psz - 1].max > 0) { 841 if (psz <= MAX_SMALLCACHEABLE_SIZE && d->smallcache[psz - 1].max > 0) {
833 cache = &d->smallcache[psz - 1]; 842 cache = &d->smallcache[psz - 1];
834 if (cache->length > 0) { 843 if (cache->length > 0) {
844 int fresh;
835 if (cache->length == 1) 845 if (cache->length == 1)
836 p = cache->pages[--cache->length]; 846 p = cache->pages[--cache->length];
837 else { 847 else {
@@ -839,7 +849,11 @@ map(struct dir_info *d, size_t sz, int zero_fill)
839 p = cache->pages[i]; 849 p = cache->pages[i];
840 cache->pages[i] = cache->pages[--cache->length]; 850 cache->pages[i] = cache->pages[--cache->length];
841 } 851 }
842 if (!mopts.malloc_freeunmap) 852 /* check if page was not junked, i.e. "fresh
853 we use the lsb of the pointer for that */
854 fresh = (uintptr_t)p & 1UL;
855 *(uintptr_t*)&p &= ~1UL;
856 if (!fresh && !mopts.malloc_freeunmap)
843 validate_junk(d, p, sz); 857 validate_junk(d, p, sz);
844 if (mopts.malloc_freeunmap) 858 if (mopts.malloc_freeunmap)
845 mprotect(p, sz, PROT_READ | PROT_WRITE); 859 mprotect(p, sz, PROT_READ | PROT_WRITE);
@@ -859,15 +873,14 @@ map(struct dir_info *d, size_t sz, int zero_fill)
859 for (i = 0; i < cache->max - 1; i++) { 873 for (i = 0; i < cache->max - 1; i++) {
860 void *q = (char*)p + i * sz; 874 void *q = (char*)p + i * sz;
861 cache->pages[i] = q; 875 cache->pages[i] = q;
862 if (!mopts.malloc_freeunmap) 876 /* mark pointer in slot as not junked */
863 junk_free(d->malloc_junk, q, 877 *(uintptr_t*)&cache->pages[i] |= 1UL;
864 sz);
865 } 878 }
866 if (mopts.malloc_freeunmap) 879 if (mopts.malloc_freeunmap)
867 mprotect(p, (cache->max - 1) * sz, 880 mprotect(p, (cache->max - 1) * sz,
868 PROT_NONE); 881 PROT_NONE);
869 p = (char*)p + (cache->max - 1) * sz; 882 p = (char*)p + (cache->max - 1) * sz;
870 /* zero fill not needed */ 883 /* zero fill not needed, freshly mmapped */
871 return p; 884 return p;
872 } 885 }
873 } 886 }
@@ -883,21 +896,13 @@ map(struct dir_info *d, size_t sz, int zero_fill)
883} 896}
884 897
885static void 898static void
886init_chunk_info(struct dir_info *d, struct chunk_info *p, int bits) 899init_chunk_info(struct dir_info *d, struct chunk_info *p, u_int bucket)
887{ 900{
888 int i; 901 u_int i;
889 902
890 if (bits == 0) { 903 p->bucket = bucket;
891 p->shift = MALLOC_MINSHIFT; 904 p->total = p->free = MALLOC_PAGESIZE / B2ALLOC(bucket);
892 p->total = p->free = MALLOC_PAGESIZE >> p->shift; 905 p->offset = bucket == 0 ? 0xdead : howmany(p->total, MALLOC_BITS);
893 p->size = 0;
894 p->offset = 0xdead;
895 } else {
896 p->shift = bits;
897 p->total = p->free = MALLOC_PAGESIZE >> p->shift;
898 p->size = 1U << bits;
899 p->offset = howmany(p->total, MALLOC_BITS);
900 }
901 p->canary = (u_short)d->canary1; 906 p->canary = (u_short)d->canary1;
902 907
903 /* set all valid bits in the bitmap */ 908 /* set all valid bits in the bitmap */
@@ -907,41 +912,48 @@ init_chunk_info(struct dir_info *d, struct chunk_info *p, int bits)
907} 912}
908 913
909static struct chunk_info * 914static struct chunk_info *
910alloc_chunk_info(struct dir_info *d, int bits) 915alloc_chunk_info(struct dir_info *d, u_int bucket)
911{ 916{
912 struct chunk_info *p; 917 struct chunk_info *p;
913 918
914 if (LIST_EMPTY(&d->chunk_info_list[bits])) { 919 if (LIST_EMPTY(&d->chunk_info_list[bucket])) {
920 const size_t chunk_pages = 64;
915 size_t size, count, i; 921 size_t size, count, i;
916 char *q; 922 char *q;
917 923
918 if (bits == 0) 924 count = MALLOC_PAGESIZE / B2ALLOC(bucket);
919 count = MALLOC_PAGESIZE / MALLOC_MINSIZE;
920 else
921 count = MALLOC_PAGESIZE >> bits;
922 925
923 size = howmany(count, MALLOC_BITS); 926 size = howmany(count, MALLOC_BITS);
924 size = sizeof(struct chunk_info) + (size - 1) * sizeof(u_short); 927 size = sizeof(struct chunk_info) + (size - 1) * sizeof(u_short);
925 if (mopts.chunk_canaries) 928 if (mopts.chunk_canaries)
926 size += count * sizeof(u_short); 929 size += count * sizeof(u_short);
927 size = _ALIGN(size); 930 size = _ALIGN(size);
931 count = MALLOC_PAGESIZE / size;
928 932
929 /* Don't use cache here, we don't want user uaf touch this */ 933 /* Don't use cache here, we don't want user uaf touch this */
930 q = MMAP(MALLOC_PAGESIZE, d->mmap_flag); 934 if (d->chunk_pages_used == chunk_pages ||
931 if (q == MAP_FAILED) 935 d->chunk_pages == NULL) {
932 return NULL; 936 q = MMAP(MALLOC_PAGESIZE * chunk_pages, d->mmap_flag);
933 STATS_ADD(d->malloc_used, MALLOC_PAGESIZE); 937 if (q == MAP_FAILED)
934 count = MALLOC_PAGESIZE / size; 938 return NULL;
939 d->chunk_pages = q;
940 d->chunk_pages_used = 0;
941 STATS_ADD(d->malloc_used, MALLOC_PAGESIZE *
942 chunk_pages);
943 }
944 q = (char *)d->chunk_pages + d->chunk_pages_used *
945 MALLOC_PAGESIZE;
946 d->chunk_pages_used++;
935 947
936 for (i = 0; i < count; i++, q += size) { 948 for (i = 0; i < count; i++, q += size) {
937 p = (struct chunk_info *)q; 949 p = (struct chunk_info *)q;
938 LIST_INSERT_HEAD(&d->chunk_info_list[bits], p, entries); 950 LIST_INSERT_HEAD(&d->chunk_info_list[bucket], p, entries);
939 } 951 }
940 } 952 }
941 p = LIST_FIRST(&d->chunk_info_list[bits]); 953 p = LIST_FIRST(&d->chunk_info_list[bucket]);
942 LIST_REMOVE(p, entries); 954 LIST_REMOVE(p, entries);
943 if (p->shift == 0) 955 if (p->total == 0)
944 init_chunk_info(d, p, bits); 956 init_chunk_info(d, p, bucket);
945 return p; 957 return p;
946} 958}
947 959
@@ -949,7 +961,7 @@ alloc_chunk_info(struct dir_info *d, int bits)
949 * Allocate a page of chunks 961 * Allocate a page of chunks
950 */ 962 */
951static struct chunk_info * 963static struct chunk_info *
952omalloc_make_chunks(struct dir_info *d, int bits, int listnum) 964omalloc_make_chunks(struct dir_info *d, u_int bucket, u_int listnum)
953{ 965{
954 struct chunk_info *bp; 966 struct chunk_info *bp;
955 void *pp; 967 void *pp;
@@ -960,18 +972,18 @@ omalloc_make_chunks(struct dir_info *d, int bits, int listnum)
960 return NULL; 972 return NULL;
961 973
962 /* memory protect the page allocated in the malloc(0) case */ 974 /* memory protect the page allocated in the malloc(0) case */
963 if (bits == 0 && mprotect(pp, MALLOC_PAGESIZE, PROT_NONE) == -1) 975 if (bucket == 0 && mprotect(pp, MALLOC_PAGESIZE, PROT_NONE) == -1)
964 goto err; 976 goto err;
965 977
966 bp = alloc_chunk_info(d, bits); 978 bp = alloc_chunk_info(d, bucket);
967 if (bp == NULL) 979 if (bp == NULL)
968 goto err; 980 goto err;
969 bp->page = pp; 981 bp->page = pp;
970 982
971 if (insert(d, (void *)((uintptr_t)pp | (bits + 1)), (uintptr_t)bp, 983 if (insert(d, (void *)((uintptr_t)pp | (bucket + 1)), (uintptr_t)bp,
972 NULL)) 984 NULL))
973 goto err; 985 goto err;
974 LIST_INSERT_HEAD(&d->chunk_dir[bits][listnum], bp, entries); 986 LIST_INSERT_HEAD(&d->chunk_dir[bucket][listnum], bp, entries);
975 return bp; 987 return bp;
976 988
977err: 989err:
@@ -979,23 +991,46 @@ err:
979 return NULL; 991 return NULL;
980} 992}
981 993
982static int 994static inline unsigned int
983find_chunksize(size_t size) 995lb(u_int x)
996{
997 /* I need an extension just for integer-length (: */
998 return (sizeof(int) * CHAR_BIT - 1) - __builtin_clz(x);
999}
1000
1001/* https://pvk.ca/Blog/2015/06/27/linear-log-bucketing-fast-versatile-simple/
1002 via Tony Finch */
1003static inline unsigned int
1004bin_of(unsigned int size)
984{ 1005{
985 int r; 1006 const unsigned int linear = 6;
1007 const unsigned int subbin = 2;
986 1008
1009 unsigned int mask, range, rounded, sub_index, rounded_size;
1010 unsigned int n_bits, shift;
1011
1012 n_bits = lb(size | (1U << linear));
1013 shift = n_bits - subbin;
1014 mask = (1ULL << shift) - 1;
1015 rounded = size + mask; /* XXX: overflow. */
1016 sub_index = rounded >> shift;
1017 range = n_bits - linear;
1018
1019 rounded_size = rounded & ~mask;
1020 return rounded_size;
1021}
1022
1023static inline u_short
1024find_bucket(u_short size)
1025{
987 /* malloc(0) is special */ 1026 /* malloc(0) is special */
988 if (size == 0) 1027 if (size == 0)
989 return 0; 1028 return 0;
990
991 if (size < MALLOC_MINSIZE) 1029 if (size < MALLOC_MINSIZE)
992 size = MALLOC_MINSIZE; 1030 size = MALLOC_MINSIZE;
993 size--; 1031 if (mopts.def_maxcache != 0)
994 1032 size = bin_of(size);
995 r = MALLOC_MINSHIFT; 1033 return howmany(size, MALLOC_MINSIZE);
996 while (size >> r)
997 r++;
998 return r;
999} 1034}
1000 1035
1001static void 1036static void
@@ -1014,8 +1049,7 @@ fill_canary(char *ptr, size_t sz, size_t allocated)
1014static void * 1049static void *
1015malloc_bytes(struct dir_info *d, size_t size, void *f) 1050malloc_bytes(struct dir_info *d, size_t size, void *f)
1016{ 1051{
1017 u_int i, r; 1052 u_int i, r, bucket, listnum;
1018 int j, listnum;
1019 size_t k; 1053 size_t k;
1020 u_short *lp; 1054 u_short *lp;
1021 struct chunk_info *bp; 1055 struct chunk_info *bp;
@@ -1025,13 +1059,14 @@ malloc_bytes(struct dir_info *d, size_t size, void *f)
1025 d->canary1 != ~d->canary2) 1059 d->canary1 != ~d->canary2)
1026 wrterror(d, "internal struct corrupt"); 1060 wrterror(d, "internal struct corrupt");
1027 1061
1028 j = find_chunksize(size); 1062 bucket = find_bucket(size);
1029 1063
1030 r = ((u_int)getrbyte(d) << 8) | getrbyte(d); 1064 r = ((u_int)getrbyte(d) << 8) | getrbyte(d);
1031 listnum = r % MALLOC_CHUNK_LISTS; 1065 listnum = r % MALLOC_CHUNK_LISTS;
1066
1032 /* If it's empty, make a page more of that size chunks */ 1067 /* If it's empty, make a page more of that size chunks */
1033 if ((bp = LIST_FIRST(&d->chunk_dir[j][listnum])) == NULL) { 1068 if ((bp = LIST_FIRST(&d->chunk_dir[bucket][listnum])) == NULL) {
1034 bp = omalloc_make_chunks(d, j, listnum); 1069 bp = omalloc_make_chunks(d, bucket, listnum);
1035 if (bp == NULL) 1070 if (bp == NULL)
1036 return NULL; 1071 return NULL;
1037 } 1072 }
@@ -1039,12 +1074,13 @@ malloc_bytes(struct dir_info *d, size_t size, void *f)
1039 if (bp->canary != (u_short)d->canary1) 1074 if (bp->canary != (u_short)d->canary1)
1040 wrterror(d, "chunk info corrupted"); 1075 wrterror(d, "chunk info corrupted");
1041 1076
1042 i = (r / MALLOC_CHUNK_LISTS) & (bp->total - 1); 1077 /* bias, as bp->total is not a power of 2 */
1078 i = (r / MALLOC_CHUNK_LISTS) % bp->total;
1043 1079
1044 /* start somewhere in a short */ 1080 /* potentially start somewhere in a short */
1045 lp = &bp->bits[i / MALLOC_BITS]; 1081 lp = &bp->bits[i / MALLOC_BITS];
1046 if (*lp) { 1082 if (*lp) {
1047 j = i % MALLOC_BITS; 1083 int j = i % MALLOC_BITS; /* j must be signed */
1048 k = ffs(*lp >> j); 1084 k = ffs(*lp >> j);
1049 if (k != 0) { 1085 if (k != 0) {
1050 k += j - 1; 1086 k += j - 1;
@@ -1054,7 +1090,7 @@ malloc_bytes(struct dir_info *d, size_t size, void *f)
1054 /* no bit halfway, go to next full short */ 1090 /* no bit halfway, go to next full short */
1055 i /= MALLOC_BITS; 1091 i /= MALLOC_BITS;
1056 for (;;) { 1092 for (;;) {
1057 if (++i >= bp->total / MALLOC_BITS) 1093 if (++i >= howmany(bp->total, MALLOC_BITS))
1058 i = 0; 1094 i = 0;
1059 lp = &bp->bits[i]; 1095 lp = &bp->bits[i];
1060 if (*lp) { 1096 if (*lp) {
@@ -1082,14 +1118,14 @@ found:
1082 if (mopts.chunk_canaries && size > 0) 1118 if (mopts.chunk_canaries && size > 0)
1083 bp->bits[bp->offset + k] = size; 1119 bp->bits[bp->offset + k] = size;
1084 1120
1085 k <<= bp->shift; 1121 k *= B2ALLOC(bp->bucket);
1086 1122
1087 p = (char *)bp->page + k; 1123 p = (char *)bp->page + k;
1088 if (bp->size > 0) { 1124 if (bp->bucket > 0) {
1089 if (d->malloc_junk == 2) 1125 if (d->malloc_junk == 2)
1090 memset(p, SOME_JUNK, bp->size); 1126 memset(p, SOME_JUNK, B2SIZE(bp->bucket));
1091 else if (mopts.chunk_canaries) 1127 else if (mopts.chunk_canaries)
1092 fill_canary(p, size, bp->size); 1128 fill_canary(p, size, B2SIZE(bp->bucket));
1093 } 1129 }
1094 return p; 1130 return p;
1095} 1131}
@@ -1124,16 +1160,16 @@ find_chunknum(struct dir_info *d, struct chunk_info *info, void *ptr, int check)
1124 wrterror(d, "chunk info corrupted"); 1160 wrterror(d, "chunk info corrupted");
1125 1161
1126 /* Find the chunk number on the page */ 1162 /* Find the chunk number on the page */
1127 chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) >> info->shift; 1163 chunknum = ((uintptr_t)ptr & MALLOC_PAGEMASK) / B2ALLOC(info->bucket);
1128 1164
1129 if ((uintptr_t)ptr & ((1U << (info->shift)) - 1)) 1165 if ((uintptr_t)ptr & (MALLOC_MINSIZE - 1))
1130 wrterror(d, "modified chunk-pointer %p", ptr); 1166 wrterror(d, "modified chunk-pointer %p", ptr);
1131 if (info->bits[chunknum / MALLOC_BITS] & 1167 if (info->bits[chunknum / MALLOC_BITS] &
1132 (1U << (chunknum % MALLOC_BITS))) 1168 (1U << (chunknum % MALLOC_BITS)))
1133 wrterror(d, "chunk is already free %p", ptr); 1169 wrterror(d, "chunk is already free %p", ptr);
1134 if (check && info->size > 0) { 1170 if (check && info->bucket > 0) {
1135 validate_canary(d, ptr, info->bits[info->offset + chunknum], 1171 validate_canary(d, ptr, info->bits[info->offset + chunknum],
1136 info->size); 1172 B2SIZE(info->bucket));
1137 } 1173 }
1138 return chunknum; 1174 return chunknum;
1139} 1175}
@@ -1147,7 +1183,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr)
1147 struct chunk_head *mp; 1183 struct chunk_head *mp;
1148 struct chunk_info *info; 1184 struct chunk_info *info;
1149 uint32_t chunknum; 1185 uint32_t chunknum;
1150 int listnum; 1186 uint32_t listnum;
1151 1187
1152 info = (struct chunk_info *)r->size; 1188 info = (struct chunk_info *)r->size;
1153 chunknum = find_chunknum(d, info, ptr, 0); 1189 chunknum = find_chunknum(d, info, ptr, 0);
@@ -1158,11 +1194,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr)
1158 if (info->free == 1) { 1194 if (info->free == 1) {
1159 /* Page became non-full */ 1195 /* Page became non-full */
1160 listnum = getrbyte(d) % MALLOC_CHUNK_LISTS; 1196 listnum = getrbyte(d) % MALLOC_CHUNK_LISTS;
1161 if (info->size != 0) 1197 mp = &d->chunk_dir[info->bucket][listnum];
1162 mp = &d->chunk_dir[info->shift][listnum];
1163 else
1164 mp = &d->chunk_dir[0][listnum];
1165
1166 LIST_INSERT_HEAD(mp, info, entries); 1198 LIST_INSERT_HEAD(mp, info, entries);
1167 return; 1199 return;
1168 } 1200 }
@@ -1172,15 +1204,12 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr)
1172 1204
1173 LIST_REMOVE(info, entries); 1205 LIST_REMOVE(info, entries);
1174 1206
1175 if (info->size == 0 && !mopts.malloc_freeunmap) 1207 if (info->bucket == 0 && !mopts.malloc_freeunmap)
1176 mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE); 1208 mprotect(info->page, MALLOC_PAGESIZE, PROT_READ | PROT_WRITE);
1177 unmap(d, info->page, MALLOC_PAGESIZE, 0); 1209 unmap(d, info->page, MALLOC_PAGESIZE, 0);
1178 1210
1179 delete(d, r); 1211 delete(d, r);
1180 if (info->size != 0) 1212 mp = &d->chunk_info_list[info->bucket];
1181 mp = &d->chunk_info_list[info->shift];
1182 else
1183 mp = &d->chunk_info_list[0];
1184 LIST_INSERT_HEAD(mp, info, entries); 1213 LIST_INSERT_HEAD(mp, info, entries);
1185} 1214}
1186 1215
@@ -1520,7 +1549,7 @@ ofree(struct dir_info **argpool, void *p, int clear, int check, size_t argsz)
1520 1549
1521 /* Validate and optionally canary check */ 1550 /* Validate and optionally canary check */
1522 struct chunk_info *info = (struct chunk_info *)r->size; 1551 struct chunk_info *info = (struct chunk_info *)r->size;
1523 if (info->size != sz) 1552 if (B2SIZE(info->bucket) != sz)
1524 wrterror(pool, "internal struct corrupt"); 1553 wrterror(pool, "internal struct corrupt");
1525 find_chunknum(pool, info, p, mopts.chunk_canaries); 1554 find_chunknum(pool, info, p, mopts.chunk_canaries);
1526 1555
@@ -1750,13 +1779,13 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f)
1750 } 1779 }
1751 if (oldsz <= MALLOC_MAXCHUNK && oldsz > 0 && 1780 if (oldsz <= MALLOC_MAXCHUNK && oldsz > 0 &&
1752 newsz <= MALLOC_MAXCHUNK && newsz > 0 && 1781 newsz <= MALLOC_MAXCHUNK && newsz > 0 &&
1753 1 << find_chunksize(newsz) == oldsz && !forced) { 1782 !forced && find_bucket(newsz) == find_bucket(oldsz)) {
1754 /* do not reallocate if new size fits good in existing chunk */ 1783 /* do not reallocate if new size fits good in existing chunk */
1755 if (pool->malloc_junk == 2) 1784 if (pool->malloc_junk == 2)
1756 memset((char *)p + newsz, SOME_JUNK, oldsz - newsz); 1785 memset((char *)p + newsz, SOME_JUNK, oldsz - newsz);
1757 if (mopts.chunk_canaries) { 1786 if (mopts.chunk_canaries) {
1758 info->bits[info->offset + chunknum] = newsz; 1787 info->bits[info->offset + chunknum] = newsz;
1759 fill_canary(p, newsz, info->size); 1788 fill_canary(p, newsz, B2SIZE(info->bucket));
1760 } 1789 }
1761 STATS_SETF(r, f); 1790 STATS_SETF(r, f);
1762 ret = p; 1791 ret = p;
@@ -2048,14 +2077,21 @@ omemalign(struct dir_info *pool, size_t alignment, size_t sz, int zero_fill,
2048 if (sz > MALLOC_MAXCHUNK && sz < MALLOC_PAGESIZE) 2077 if (sz > MALLOC_MAXCHUNK && sz < MALLOC_PAGESIZE)
2049 sz = MALLOC_PAGESIZE; 2078 sz = MALLOC_PAGESIZE;
2050 if (alignment <= MALLOC_PAGESIZE) { 2079 if (alignment <= MALLOC_PAGESIZE) {
2080 size_t pof2;
2051 /* 2081 /*
2052 * max(size, alignment) is enough to assure the requested 2082 * max(size, alignment) rounded up to power of 2 is enough
2053 * alignment, since the allocator always allocates 2083 * to assure the requested alignment. Large regions are
2054 * power-of-two blocks. 2084 * always page aligned.
2055 */ 2085 */
2056 if (sz < alignment) 2086 if (sz < alignment)
2057 sz = alignment; 2087 sz = alignment;
2058 return omalloc(pool, sz, zero_fill, f); 2088 if (sz < MALLOC_PAGESIZE) {
2089 pof2 = MALLOC_MINSIZE;
2090 while (pof2 < sz)
2091 pof2 <<= 1;
2092 } else
2093 pof2 = sz;
2094 return omalloc(pool, pof2, zero_fill, f);
2059 } 2095 }
2060 2096
2061 if (sz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) { 2097 if (sz >= SIZE_MAX - mopts.malloc_guard - MALLOC_PAGESIZE) {
@@ -2252,15 +2288,16 @@ static void
2252dump_chunk(int fd, struct chunk_info *p, void *f, int fromfreelist) 2288dump_chunk(int fd, struct chunk_info *p, void *f, int fromfreelist)
2253{ 2289{
2254 while (p != NULL) { 2290 while (p != NULL) {
2255 dprintf(fd, "chunk %18p %18p %4d %d/%d\n", 2291 dprintf(fd, "chunk %18p %18p %4zu %d/%d\n",
2256 p->page, ((p->bits[0] & 1) ? NULL : f), 2292 p->page, ((p->bits[0] & 1) ? NULL : f),
2257 p->size, p->free, p->total); 2293 B2SIZE(p->bucket), p->free, p->total);
2258 if (!fromfreelist) { 2294 if (!fromfreelist) {
2295 size_t sz = B2SIZE(p->bucket);
2259 if (p->bits[0] & 1) 2296 if (p->bits[0] & 1)
2260 putleakinfo(NULL, p->size, p->total - p->free); 2297 putleakinfo(NULL, sz, p->total - p->free);
2261 else { 2298 else {
2262 putleakinfo(f, p->size, 1); 2299 putleakinfo(f, sz, 1);
2263 putleakinfo(NULL, p->size, 2300 putleakinfo(NULL, sz,
2264 p->total - p->free - 1); 2301 p->total - p->free - 1);
2265 } 2302 }
2266 break; 2303 break;
@@ -2278,7 +2315,7 @@ dump_free_chunk_info(int fd, struct dir_info *d)
2278 struct chunk_info *p; 2315 struct chunk_info *p;
2279 2316
2280 dprintf(fd, "Free chunk structs:\n"); 2317 dprintf(fd, "Free chunk structs:\n");
2281 for (i = 0; i <= MALLOC_MAXSHIFT; i++) { 2318 for (i = 0; i <= BUCKETS; i++) {
2282 count = 0; 2319 count = 0;
2283 LIST_FOREACH(p, &d->chunk_info_list[i], entries) 2320 LIST_FOREACH(p, &d->chunk_info_list[i], entries)
2284 count++; 2321 count++;
@@ -2286,7 +2323,10 @@ dump_free_chunk_info(int fd, struct dir_info *d)
2286 p = LIST_FIRST(&d->chunk_dir[i][j]); 2323 p = LIST_FIRST(&d->chunk_dir[i][j]);
2287 if (p == NULL && count == 0) 2324 if (p == NULL && count == 0)
2288 continue; 2325 continue;
2289 dprintf(fd, "%2d) %3d ", i, count); 2326 if (j == 0)
2327 dprintf(fd, "%3d) %3d ", i, count);
2328 else
2329 dprintf(fd, " ");
2290 if (p != NULL) 2330 if (p != NULL)
2291 dump_chunk(fd, p, NULL, 1); 2331 dump_chunk(fd, p, NULL, 1);
2292 else 2332 else