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 2^(i+3). The 93 * smallest allocatable block is 8 bytes. The overhead information 94 * precedes the data area returned to the user. 95 */ 96 #define NBUCKETS 30 97 static union overhead *nextf[NBUCKETS]; 98 99 static int pagesz; /* page size */ 100 static int pagebucket; /* page size bucket */ 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 ssize_t n; 116 size_t amt; 117 118 /* 119 * First time malloc is called, setup page size and 120 * align break pointer so all data will be page aligned. 121 */ 122 if (pagesz == 0) { 123 pagesz = n = pagesizes[0]; 124 if (morepages(NPOOLPAGES) == 0) 125 return NULL; 126 op = (union overhead *)(pagepool_start); 127 n = n - sizeof (*op) - ((long)op & (n - 1)); 128 if (n < 0) 129 n += pagesz; 130 if (n) { 131 pagepool_start += n; 132 } 133 bucket = 0; 134 amt = 8; 135 while ((unsigned)pagesz > amt) { 136 amt <<= 1; 137 bucket++; 138 } 139 pagebucket = bucket; 140 } 141 /* 142 * Convert amount of memory requested into closest block size 143 * stored in hash buckets which satisfies request. 144 * Account for space used per block for accounting. 145 */ 146 if (nbytes <= (unsigned long)(n = pagesz - sizeof(*op))) { 147 amt = 8; /* size of first bucket */ 148 bucket = 0; 149 n = -sizeof(*op); 150 } else { 151 amt = pagesz; 152 bucket = pagebucket; 153 } 154 while (nbytes > amt + n) { 155 amt <<= 1; 156 if (amt == 0) 157 return (NULL); 158 bucket++; 159 } 160 /* 161 * If nothing in hash bucket right now, 162 * request more memory from the system. 163 */ 164 if ((op = nextf[bucket]) == NULL) { 165 morecore(bucket); 166 if ((op = nextf[bucket]) == NULL) 167 return (NULL); 168 } 169 /* remove from linked list */ 170 nextf[bucket] = op->ov_next; 171 op->ov_magic = MAGIC; 172 op->ov_index = bucket; 173 return ((char *)(op + 1)); 174 } 175 176 void * 177 __crt_calloc(size_t num, size_t size) 178 { 179 void *ret; 180 181 if (size != 0 && (num * size) / size != num) { 182 /* size_t overflow. */ 183 return (NULL); 184 } 185 186 if ((ret = __crt_malloc(num * size)) != NULL) 187 memset(ret, 0, num * size); 188 189 return (ret); 190 } 191 192 /* 193 * Allocate more memory to the indicated bucket. 194 */ 195 static void 196 morecore(int bucket) 197 { 198 union overhead *op; 199 int sz; /* size of desired block */ 200 int amt; /* amount to allocate */ 201 int nblks; /* how many blocks we get */ 202 203 /* 204 * sbrk_size <= 0 only for big, FLUFFY, requests (about 205 * 2^30 bytes on a VAX, I think) or for a negative arg. 206 */ 207 if ((unsigned)bucket >= NBBY * sizeof(int) - 4) 208 return; 209 sz = 1 << (bucket + 3); 210 if (sz < pagesz) { 211 amt = pagesz; 212 nblks = amt / sz; 213 } else { 214 amt = sz + pagesz; 215 nblks = 1; 216 } 217 if (amt > pagepool_end - pagepool_start) 218 if (morepages(amt/pagesz + NPOOLPAGES) == 0) 219 return; 220 op = (union overhead *)pagepool_start; 221 pagepool_start += amt; 222 223 /* 224 * Add new memory allocated to that on 225 * free list for this hash bucket. 226 */ 227 nextf[bucket] = op; 228 while (--nblks > 0) { 229 op->ov_next = (union overhead *)((caddr_t)op + sz); 230 op = (union overhead *)((caddr_t)op + sz); 231 } 232 } 233 234 void 235 __crt_free(void *cp) 236 { 237 int size; 238 union overhead *op; 239 240 if (cp == NULL) 241 return; 242 op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 243 if (op->ov_magic != MAGIC) 244 return; /* sanity */ 245 size = op->ov_index; 246 op->ov_next = nextf[size]; /* also clobbers ov_magic */ 247 nextf[size] = op; 248 } 249 250 void * 251 __crt_realloc(void *cp, size_t nbytes) 252 { 253 u_int onb; 254 int i; 255 union overhead *op; 256 char *res; 257 258 if (cp == NULL) 259 return (__crt_malloc(nbytes)); 260 op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 261 if (op->ov_magic != MAGIC) 262 return (NULL); /* Double-free or bad argument */ 263 i = op->ov_index; 264 onb = 1 << (i + 3); 265 if (onb < (u_int)pagesz) 266 onb -= sizeof(*op); 267 else 268 onb += pagesz - sizeof(*op); 269 /* avoid the copy if same size block */ 270 if (i != 0) { 271 i = 1 << (i + 2); 272 if (i < pagesz) 273 i -= sizeof(*op); 274 else 275 i += pagesz - sizeof(*op); 276 } 277 if (nbytes <= onb && nbytes > (size_t)i) 278 return (cp); 279 if ((res = __crt_malloc(nbytes)) == NULL) 280 return (NULL); 281 bcopy(cp, res, (nbytes < onb) ? nbytes : onb); 282 __crt_free(cp); 283 return (res); 284 } 285 286 static int 287 morepages(int n) 288 { 289 caddr_t addr; 290 int offset; 291 292 if (pagepool_end - pagepool_start > pagesz) { 293 addr = (caddr_t)roundup2((long)pagepool_start, pagesz); 294 if (munmap(addr, pagepool_end - addr) != 0) { 295 #ifdef IN_RTLD 296 rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": " 297 "morepages: cannot munmap %p: %s\n", 298 addr, rtld_strerror(errno)); 299 #endif 300 } 301 } 302 303 offset = (long)pagepool_start - rounddown2((long)pagepool_start, 304 pagesz); 305 306 pagepool_start = mmap(0, n * pagesz, PROT_READ | PROT_WRITE, 307 MAP_ANON | MAP_PRIVATE, -1, 0); 308 if (pagepool_start == MAP_FAILED) { 309 #ifdef IN_RTLD 310 rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": morepages: " 311 "cannot mmap anonymous memory: %s\n", 312 rtld_strerror(errno)); 313 #endif 314 return (0); 315 } 316 pagepool_end = pagepool_start + n * pagesz; 317 pagepool_start += offset; 318 319 return (n); 320 } 321