diff options
Diffstat (limited to 'src/lib/libc/stdlib/malloc.c')
| -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 | 
