1 /* $NetBSD: xmalloc.c,v 1.28 2026/07/20 18:46:37 riastradh Exp $ */ 2 3 /* 4 * Copyright 1996 John D. Polstra. 5 * Copyright 1996 Matt Thomas <matt (at) 3am-software.com> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by John Polstra. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Copyright (c) 1983 Regents of the University of California. 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63 #if defined(LIBC_SCCS) && !defined(lint) 64 /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/ 65 #endif /* LIBC_SCCS and not lint */ 66 67 /* 68 * malloc.c (Caltech) 2/21/82 69 * Chris Kingsley, kingsley@cit-20. 70 * 71 * This is a very fast storage allocator. It allocates blocks of a small 72 * number of different sizes, and keeps free lists of each size. Blocks that 73 * don't exactly fit are passed up to the next larger size. In this 74 * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. 75 * This is designed for use in a virtual memory environment. 76 */ 77 78 #include <sys/cdefs.h> 79 #ifndef lint 80 __RCSID("$NetBSD: xmalloc.c,v 1.28 2026/07/20 18:46:37 riastradh Exp $"); 81 #endif /* not lint */ 82 83 #include <stdlib.h> 84 #include <string.h> 85 #include <unistd.h> 86 #include <errno.h> 87 88 #include <sys/types.h> 89 #include <sys/param.h> 90 #include <sys/mman.h> 91 #include <sys/stat.h> 92 93 #include "rtld.h" 94 95 /* 96 * Pre-allocate mmap'ed pages 97 */ 98 #define NPOOLPAGES (32 * 1024 / pagesz) 99 static char *pagepool_start, *pagepool_end; 100 #define PAGEPOOL_SIZE (size_t)(pagepool_end - pagepool_start) 101 102 /* 103 * The overhead on a block is at least 4 bytes. When free, this space 104 * contains a pointer to the next free block, and the bottom two bits must 105 * be zero. When in use, the first byte is set to MAGIC, and the second 106 * byte is the size index. The remaining bytes are for alignment. 107 */ 108 union overhead { 109 union overhead *ov_next; /* when free */ 110 struct { 111 uint16_t ovu_index; /* bucket # */ 112 uint8_t ovu_magic; /* magic number */ 113 } ovu; 114 #define ov_magic ovu.ovu_magic 115 #define ov_index ovu.ovu_index 116 }; 117 118 static void morecore(size_t); 119 static int morepages(int); 120 static void *imalloc(size_t); 121 122 #define MAGIC 0xef /* magic # on accounting info */ 123 #define AMAGIC 0xdf /* magic # for aligned alloc */ 124 125 126 /* 127 * nextf[i] is the pointer to the next free block of size 128 * (FIRST_BUCKET_SIZE << i). The overhead information precedes the data 129 * area returned to the user. 130 */ 131 #define FIRST_BUCKET_SHIFT 3 132 #define FIRST_BUCKET_SIZE (1U << FIRST_BUCKET_SHIFT) 133 #define NBUCKETS 30 134 static union overhead *nextf[NBUCKETS]; 135 136 static size_t pagesz; /* page size */ 137 static size_t pageshift; /* page size shift */ 138 139 #ifdef MSTATS 140 /* 141 * nmalloc[i] is the difference between the number of mallocs and frees 142 * for a given block size. 143 */ 144 static u_int nmalloc[NBUCKETS]; 145 #endif 146 147 #if defined(MALLOC_DEBUG) 148 #define ASSERT(p) (__predict_false(!(p)) ? botch(#p) : (void)0) 149 static void 150 botch(const char *s) 151 { 152 xwarnx("\r\nassertion botched: %s\r\n", s); 153 abort(); 154 } 155 #else 156 #define ASSERT(p) __nothing 157 #endif 158 159 #define TRACE() xprintf("TRACE %s:%d\n", __FILE__, __LINE__) 160 161 static void * 162 cp2op(void *cp) 163 { 164 return (((caddr_t)cp - sizeof(union overhead))); 165 } 166 167 static void * 168 imalloc(size_t nbytes) 169 { 170 union overhead *op; 171 size_t bucket; 172 size_t amt; 173 174 /* 175 * First time malloc is called, setup page size. 176 */ 177 if (pagesz == 0) { 178 size_t n, m; 179 pagesz = n = _rtld_pagesz; 180 pageshift = ffs(pagesz) - 1; 181 if (morepages(NPOOLPAGES) == 0) 182 return NULL; 183 op = (union overhead *)(pagepool_start); 184 m = sizeof (*op) - (((char *)op - (char *)NULL) & (n - 1)); 185 if (n < m) 186 n += pagesz - m; 187 else 188 n -= m; 189 if (n) { 190 pagepool_start += n; 191 } 192 } 193 194 /* 195 * Convert amount of memory requested into closest block size 196 * stored in hash buckets which satisfies request. 197 * Account for space used per block for accounting. 198 */ 199 amt = FIRST_BUCKET_SIZE; 200 bucket = 0; 201 while (nbytes > amt - sizeof(*op)) { 202 amt <<= 1; 203 bucket++; 204 if (amt == 0 || bucket >= NBUCKETS) 205 return (NULL); 206 } 207 /* 208 * If nothing in hash bucket right now, 209 * request more memory from the system. 210 */ 211 if ((op = nextf[bucket]) == NULL) { 212 morecore(bucket); 213 if ((op = nextf[bucket]) == NULL) 214 return (NULL); 215 } 216 /* remove from linked list */ 217 nextf[bucket] = op->ov_next; 218 op->ov_magic = MAGIC; 219 op->ov_index = bucket; 220 #ifdef MSTATS 221 nmalloc[bucket]++; 222 #endif 223 return ((char *)(op + 1)); 224 } 225 226 void * 227 xmalloc_aligned(size_t size, size_t align, size_t offset) 228 { 229 void *mem; 230 union overhead *ov; 231 uintptr_t x; 232 233 ASSERT((align & (align - 1)) == 0); 234 235 if (align < FIRST_BUCKET_SIZE) 236 align = FIRST_BUCKET_SIZE; 237 offset &= align - 1; 238 mem = imalloc(size + align + offset + sizeof(union overhead)); 239 if (mem == NULL) 240 return (NULL); 241 x = roundup2((uintptr_t)mem + sizeof(union overhead), align); 242 x += offset; 243 ov = cp2op((void *)x); 244 ov->ov_magic = AMAGIC; 245 ov->ov_index = x - (uintptr_t)mem + sizeof(union overhead); 246 247 return ((void *)x); 248 } 249 250 /* 251 * Allocate more memory to the indicated bucket. 252 */ 253 static void 254 morecore(size_t bucket) 255 { 256 union overhead *op; 257 size_t sz; /* size of desired block */ 258 size_t amt; /* amount to allocate */ 259 size_t nblks; /* how many blocks we get */ 260 261 sz = FIRST_BUCKET_SIZE << bucket; 262 if (sz < pagesz) { 263 amt = pagesz; 264 nblks = amt >> (bucket + FIRST_BUCKET_SHIFT); 265 } else { 266 amt = sz; 267 nblks = 1; 268 } 269 if (amt > PAGEPOOL_SIZE) 270 if (morepages((amt >> pageshift) + NPOOLPAGES) == 0) 271 return; 272 op = (union overhead *)pagepool_start; 273 pagepool_start += amt; 274 275 /* 276 * Add new memory allocated to that on 277 * free list for this hash bucket. 278 */ 279 nextf[bucket] = op; 280 while (--nblks > 0) { 281 op->ov_next = (union overhead *)((caddr_t)op + sz); 282 op = (union overhead *)((caddr_t)op + sz); 283 } 284 } 285 286 void 287 xfree(void *cp) 288 { 289 union overhead *op, *opx; 290 size_t size; 291 292 if (cp == NULL) 293 return; 294 opx = cp2op(cp); 295 296 op = opx->ov_magic == AMAGIC ? (void *)((caddr_t)cp - opx->ov_index) : 297 opx; 298 ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ 299 if (op->ov_magic != MAGIC) 300 return; /* sanity */ 301 size = op->ov_index; 302 ASSERT(size < NBUCKETS); 303 op->ov_next = nextf[size]; /* also clobbers ov_magic */ 304 nextf[size] = op; 305 #ifdef MSTATS 306 nmalloc[size]--; 307 #endif 308 } 309 310 static void * 311 irealloc(void *cp, size_t nbytes) 312 { 313 size_t onb; 314 size_t i; 315 union overhead *op; 316 char *res; 317 318 if (cp == NULL) 319 return (imalloc(nbytes)); 320 op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 321 if (op->ov_magic != MAGIC) { 322 static const char *err_str = 323 "memory corruption or double free in realloc\n"; 324 extern char *__progname; 325 write(STDERR_FILENO, __progname, strlen(__progname)); 326 write(STDERR_FILENO, err_str, strlen(err_str)); 327 abort(); 328 } 329 330 i = op->ov_index; 331 onb = 1 << (i + FIRST_BUCKET_SHIFT); 332 if (onb < pagesz) 333 onb -= sizeof (*op); 334 else 335 onb += pagesz - sizeof (*op); 336 /* avoid the copy if same size block */ 337 if (i) { 338 i = 1 << (i + 2); 339 if (i < pagesz) 340 i -= sizeof (*op); 341 else 342 i += pagesz - sizeof (*op); 343 } 344 if (nbytes <= onb && nbytes > i) { 345 return(cp); 346 } 347 if ((res = imalloc(nbytes)) == NULL) 348 return (NULL); 349 if (cp != res) { /* common optimization if "compacting" */ 350 memcpy(res, cp, (nbytes < onb) ? nbytes : onb); 351 xfree(cp); 352 } 353 return (res); 354 } 355 356 #ifdef MSTATS 357 /* 358 * mstats - print out statistics about malloc 359 * 360 * Prints two lines of numbers, one showing the length of the free list 361 * for each size category, the second showing the number of mallocs - 362 * frees for each size category. 363 */ 364 void 365 mstats(char *s) 366 { 367 int i, j; 368 union overhead *p; 369 int totfree = 0, 370 totused = 0; 371 372 xprintf("Memory allocation statistics %s\nfree:\t", s); 373 for (i = 0; i < NBUCKETS; i++) { 374 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) 375 ; 376 xprintf(" %d", j); 377 totfree += j * (1 << (i + FIRST_BUCKET_SHIFT)); 378 } 379 xprintf("\nused:\t"); 380 for (i = 0; i < NBUCKETS; i++) { 381 xprintf(" %d", nmalloc[i]); 382 totused += nmalloc[i] * (1 << (i + FIRST_BUCKET_SHIFT)); 383 } 384 xprintf("\n\tTotal in use: %d, total free: %d\n", 385 totused, totfree); 386 } 387 #endif 388 389 390 static int 391 morepages(int n) 392 { 393 int fd = -1; 394 int offset; 395 396 #ifdef NEED_DEV_ZERO 397 fd = open("/dev/zero", O_RDWR, 0); 398 if (fd == -1) 399 xerr(1, "/dev/zero"); 400 #endif 401 402 if (PAGEPOOL_SIZE > pagesz) { 403 caddr_t addr = (caddr_t) 404 (((long)pagepool_start + pagesz - 1) & ~(pagesz - 1)); 405 if (munmap(addr, pagepool_end - addr) != 0) 406 xwarn("morepages: munmap %p", addr); 407 } 408 409 offset = (long)pagepool_start - ((long)pagepool_start & ~(pagesz - 1)); 410 411 if ((pagepool_start = mmap(0, n * pagesz, 412 PROT_READ|PROT_WRITE, 413 MAP_ANON|MAP_PRIVATE, fd, 0)) == (caddr_t)-1) { 414 xprintf("Cannot map anonymous memory"); 415 return 0; 416 } 417 pagepool_end = pagepool_start + n * pagesz; 418 pagepool_start += offset; 419 420 #ifdef NEED_DEV_ZERO 421 close(fd); 422 #endif 423 return n; 424 } 425 426 void * 427 xcalloc(size_t size) 428 { 429 430 return memset(xmalloc(size), 0, size); 431 } 432 433 void * 434 xmalloc(size_t size) 435 { 436 void *p = imalloc(size); 437 438 if (p == NULL) 439 xerr(1, "%s", xstrerror(errno)); 440 return p; 441 } 442 443 void * 444 xrealloc(void *p, size_t size) 445 { 446 p = irealloc(p, size); 447 448 if (p == NULL) 449 xerr(1, "%s", xstrerror(errno)); 450 return p; 451 } 452 453 char * 454 xstrdup(const char *str) 455 { 456 size_t len; 457 char *copy; 458 459 len = strlen(str) + 1; 460 copy = xmalloc(len); 461 memcpy(copy, str, len); 462 return (copy); 463 } 464