1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if defined(LIBC_SCCS) && !defined(lint) 33 /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/ 34 static char *rcsid = "$FreeBSD$"; 35 #endif /* LIBC_SCCS and not lint */ 36 37 /* 38 * malloc.c (Caltech) 2/21/82 39 * Chris Kingsley, kingsley@cit-20. 40 * 41 * This is a very fast storage allocator. It allocates blocks of a small 42 * number of different sizes, and keeps free lists of each size. Blocks that 43 * don't exactly fit are passed up to the next larger size. In this 44 * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. 45 * This is designed for use in a virtual memory environment. 46 */ 47 48 #include <sys/param.h> 49 #include <sys/sysctl.h> 50 #include <sys/mman.h> 51 #include <errno.h> 52 #include <stdarg.h> 53 #include <stddef.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include "rtld.h" 57 #include "rtld_printf.h" 58 #include "paths.h" 59 60 /* 61 * Pre-allocate mmap'ed pages 62 */ 63 #define NPOOLPAGES (128*1024/pagesz) 64 static caddr_t pagepool_start, pagepool_end; 65 66 /* 67 * The overhead on a block is at least 4 bytes. When free, this space 68 * contains a pointer to the next free block, and the bottom two bits must 69 * be zero. When in use, the first byte is set to MAGIC, and the second 70 * byte is the size index. The remaining bytes are for alignment. 71 * If range checking is enabled then a second word holds the size of the 72 * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC). 73 * The order of elements is critical: ov_magic must overlay the low order 74 * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. 75 */ 76 union overhead { 77 union overhead *ov_next; /* when free */ 78 struct { 79 u_char ovu_magic; /* magic number */ 80 u_char ovu_index; /* bucket # */ 81 } ovu; 82 #define ov_magic ovu.ovu_magic 83 #define ov_index ovu.ovu_index 84 }; 85 86 static void morecore(int bucket); 87 static int morepages(int n); 88 89 #define MAGIC 0xef /* magic # on accounting info */ 90 91 /* 92 * nextf[i] is the pointer to the next free block of size 93 * (FIRST_BUCKET_SIZE << i). The overhead information precedes the data 94 * area returned to the user. 95 */ 96 #define FIRST_BUCKET_SIZE 8 97 #define NBUCKETS 30 98 static union overhead *nextf[NBUCKETS]; 99 100 static int pagesz; /* page size */ 101 102 /* 103 * The array of supported page sizes is provided by the user, i.e., the 104 * program that calls this storage allocator. That program must initialize 105 * the array before making its first call to allocate storage. The array 106 * must contain at least one page size. The page sizes must be stored in 107 * increasing order. 108 */ 109 110 void * 111 __crt_malloc(size_t nbytes) 112 { 113 union overhead *op; 114 int bucket; 115 size_t amt; 116 117 /* 118 * First time malloc is called, setup page size. 119 */ 120 if (pagesz == 0) 121 pagesz = pagesizes[0]; 122 /* 123 * Convert amount of memory requested into closest block size 124 * stored in hash buckets which satisfies request. 125 * Account for space used per block for accounting. 126 */ 127 amt = FIRST_BUCKET_SIZE; 128 bucket = 0; 129 while (nbytes > amt - sizeof(*op)) { 130 amt <<= 1; 131 bucket++; 132 if (amt == 0 || bucket >= NBUCKETS) 133 return (NULL); 134 } 135 /* 136 * If nothing in hash bucket right now, 137 * request more memory from the system. 138 */ 139 if ((op = nextf[bucket]) == NULL) { 140 morecore(bucket); 141 if ((op = nextf[bucket]) == NULL) 142 return (NULL); 143 } 144 /* remove from linked list */ 145 nextf[bucket] = op->ov_next; 146 op->ov_magic = MAGIC; 147 op->ov_index = bucket; 148 return ((char *)(op + 1)); 149 } 150 151 void * 152 __crt_calloc(size_t num, size_t size) 153 { 154 void *ret; 155 156 if (size != 0 && (num * size) / size != num) { 157 /* size_t overflow. */ 158 return (NULL); 159 } 160 161 if ((ret = __crt_malloc(num * size)) != NULL) 162 memset(ret, 0, num * size); 163 164 return (ret); 165 } 166 167 /* 168 * Allocate more memory to the indicated bucket. 169 */ 170 static void 171 morecore(int bucket) 172 { 173 union overhead *op; 174 int sz; /* size of desired block */ 175 int amt; /* amount to allocate */ 176 int nblks; /* how many blocks we get */ 177 178 sz = FIRST_BUCKET_SIZE << bucket; 179 if (sz < pagesz) { 180 amt = pagesz; 181 nblks = amt / sz; 182 } else { 183 amt = sz; 184 nblks = 1; 185 } 186 if (amt > pagepool_end - pagepool_start) 187 if (morepages(amt/pagesz + NPOOLPAGES) == 0) 188 return; 189 op = (union overhead *)pagepool_start; 190 pagepool_start += amt; 191 192 /* 193 * Add new memory allocated to that on 194 * free list for this hash bucket. 195 */ 196 nextf[bucket] = op; 197 while (--nblks > 0) { 198 op->ov_next = (union overhead *)((caddr_t)op + sz); 199 op = (union overhead *)((caddr_t)op + sz); 200 } 201 } 202 203 void 204 __crt_free(void *cp) 205 { 206 int size; 207 union overhead *op; 208 209 if (cp == NULL) 210 return; 211 op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 212 if (op->ov_magic != MAGIC) 213 return; /* sanity */ 214 size = op->ov_index; 215 op->ov_next = nextf[size]; /* also clobbers ov_magic */ 216 nextf[size] = op; 217 } 218 219 void * 220 __crt_realloc(void *cp, size_t nbytes) 221 { 222 u_int onb; 223 int i; 224 union overhead *op; 225 char *res; 226 227 if (cp == NULL) 228 return (__crt_malloc(nbytes)); 229 op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 230 if (op->ov_magic != MAGIC) 231 return (NULL); /* Double-free or bad argument */ 232 i = op->ov_index; 233 onb = 1 << (i + 3); 234 if (onb < (u_int)pagesz) 235 onb -= sizeof(*op); 236 else 237 onb += pagesz - sizeof(*op); 238 /* avoid the copy if same size block */ 239 if (i != 0) { 240 i = 1 << (i + 2); 241 if (i < pagesz) 242 i -= sizeof(*op); 243 else 244 i += pagesz - sizeof(*op); 245 } 246 if (nbytes <= onb && nbytes > (size_t)i) 247 return (cp); 248 if ((res = __crt_malloc(nbytes)) == NULL) 249 return (NULL); 250 bcopy(cp, res, (nbytes < onb) ? nbytes : onb); 251 __crt_free(cp); 252 return (res); 253 } 254 255 static int 256 morepages(int n) 257 { 258 caddr_t addr; 259 int offset; 260 261 if (pagepool_end - pagepool_start > pagesz) { 262 addr = (caddr_t)roundup2((long)pagepool_start, pagesz); 263 if (munmap(addr, pagepool_end - addr) != 0) { 264 #ifdef IN_RTLD 265 rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": " 266 "morepages: cannot munmap %p: %s\n", 267 addr, rtld_strerror(errno)); 268 #endif 269 } 270 } 271 272 offset = (long)pagepool_start - rounddown2((long)pagepool_start, 273 pagesz); 274 275 pagepool_start = mmap(0, n * pagesz, PROT_READ | PROT_WRITE, 276 MAP_ANON | MAP_PRIVATE, -1, 0); 277 if (pagepool_start == MAP_FAILED) { 278 #ifdef IN_RTLD 279 rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": morepages: " 280 "cannot mmap anonymous memory: %s\n", 281 rtld_strerror(errno)); 282 #endif 283 return (0); 284 } 285 pagepool_end = pagepool_start + n * pagesz; 286 pagepool_start += offset; 287 288 return (n); 289 } 290