diff options
-rw-r--r-- | src/lib/libc/stdlib/malloc.c | 244 |
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 | ||
256 | static inline void | 262 | static 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 | ||
885 | static void | 898 | static void |
886 | init_chunk_info(struct dir_info *d, struct chunk_info *p, int bits) | 899 | init_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 | ||
909 | static struct chunk_info * | 914 | static struct chunk_info * |
910 | alloc_chunk_info(struct dir_info *d, int bits) | 915 | alloc_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 | */ |
951 | static struct chunk_info * | 963 | static struct chunk_info * |
952 | omalloc_make_chunks(struct dir_info *d, int bits, int listnum) | 964 | omalloc_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 | ||
977 | err: | 989 | err: |
@@ -979,23 +991,46 @@ err: | |||
979 | return NULL; | 991 | return NULL; |
980 | } | 992 | } |
981 | 993 | ||
982 | static int | 994 | static inline unsigned int |
983 | find_chunksize(size_t size) | 995 | lb(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 */ | ||
1003 | static inline unsigned int | ||
1004 | bin_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 | |||
1023 | static inline u_short | ||
1024 | find_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 | ||
1001 | static void | 1036 | static void |
@@ -1014,8 +1049,7 @@ fill_canary(char *ptr, size_t sz, size_t allocated) | |||
1014 | static void * | 1049 | static void * |
1015 | malloc_bytes(struct dir_info *d, size_t size, void *f) | 1050 | malloc_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 | |||
2252 | dump_chunk(int fd, struct chunk_info *p, void *f, int fromfreelist) | 2288 | dump_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 |