1*0a6a1f1dSLionel Sambuc /* $NetBSD: malloc.c,v 1.56 2014/09/18 13:58:20 christos Exp $ */
29152e1c5SLionel Sambuc
39152e1c5SLionel Sambuc /*
49152e1c5SLionel Sambuc * ----------------------------------------------------------------------------
59152e1c5SLionel Sambuc * "THE BEER-WARE LICENSE" (Revision 42):
69152e1c5SLionel Sambuc * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
79152e1c5SLionel Sambuc * can do whatever you want with this stuff. If we meet some day, and you think
89152e1c5SLionel Sambuc * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
99152e1c5SLionel Sambuc * ----------------------------------------------------------------------------
109152e1c5SLionel Sambuc *
119152e1c5SLionel Sambuc * From FreeBSD: malloc.c,v 1.91 2006/01/12 07:28:20 jasone
129152e1c5SLionel Sambuc *
139152e1c5SLionel Sambuc */
149152e1c5SLionel Sambuc
15*0a6a1f1dSLionel Sambuc #if defined(__minix)
1684d9c625SLionel Sambuc #include "extern.h"
179152e1c5SLionel Sambuc #ifdef _LIBSYS
189152e1c5SLionel Sambuc #include <minix/sysutil.h>
1984d9c625SLionel Sambuc #include <machine/param.h>
20dda632a2SBen Gras #include <machine/vmparam.h>
219152e1c5SLionel Sambuc #define MALLOC_NO_SYSCALLS
229152e1c5SLionel Sambuc #define wrtwarning(w) printf("libminc malloc warning: %s\n", w)
239152e1c5SLionel Sambuc #define wrterror(w) panic("libminc malloc error: %s\n", w)
249152e1c5SLionel Sambuc #endif
25*0a6a1f1dSLionel Sambuc #endif /* defined(__minix) */
269152e1c5SLionel Sambuc
279152e1c5SLionel Sambuc /*
289152e1c5SLionel Sambuc * Defining MALLOC_EXTRA_SANITY will enable extra checks which are related
299152e1c5SLionel Sambuc * to internal conditions and consistency in malloc.c. This has a
309152e1c5SLionel Sambuc * noticeable runtime performance hit, and generally will not do you
319152e1c5SLionel Sambuc * any good unless you fiddle with the internals of malloc or want
329152e1c5SLionel Sambuc * to catch random pointer corruption as early as possible.
339152e1c5SLionel Sambuc */
349152e1c5SLionel Sambuc #ifndef MALLOC_EXTRA_SANITY
359152e1c5SLionel Sambuc #undef MALLOC_EXTRA_SANITY
369152e1c5SLionel Sambuc #endif
379152e1c5SLionel Sambuc
389152e1c5SLionel Sambuc /*
399152e1c5SLionel Sambuc * What to use for Junk. This is the byte value we use to fill with
409152e1c5SLionel Sambuc * when the 'J' option is enabled.
419152e1c5SLionel Sambuc */
429152e1c5SLionel Sambuc #define SOME_JUNK 0xd0 /* as in "Duh" :-) */
439152e1c5SLionel Sambuc
449152e1c5SLionel Sambuc /*
459152e1c5SLionel Sambuc * The basic parameters you can tweak.
469152e1c5SLionel Sambuc *
479152e1c5SLionel Sambuc * malloc_minsize minimum size of an allocation in bytes.
489152e1c5SLionel Sambuc * If this is too small it's too much work
499152e1c5SLionel Sambuc * to manage them. This is also the smallest
509152e1c5SLionel Sambuc * unit of alignment used for the storage
519152e1c5SLionel Sambuc * returned by malloc/realloc.
529152e1c5SLionel Sambuc *
539152e1c5SLionel Sambuc */
549152e1c5SLionel Sambuc
559152e1c5SLionel Sambuc #include "namespace.h"
569152e1c5SLionel Sambuc #if defined(__FreeBSD__)
579152e1c5SLionel Sambuc # if defined(__i386__)
589152e1c5SLionel Sambuc # define malloc_minsize 16U
599152e1c5SLionel Sambuc # endif
609152e1c5SLionel Sambuc # if defined(__ia64__)
619152e1c5SLionel Sambuc # define malloc_pageshift 13U
629152e1c5SLionel Sambuc # define malloc_minsize 16U
639152e1c5SLionel Sambuc # endif
649152e1c5SLionel Sambuc # if defined(__alpha__)
659152e1c5SLionel Sambuc # define malloc_pageshift 13U
669152e1c5SLionel Sambuc # define malloc_minsize 16U
679152e1c5SLionel Sambuc # endif
689152e1c5SLionel Sambuc # if defined(__sparc64__)
699152e1c5SLionel Sambuc # define malloc_pageshift 13U
709152e1c5SLionel Sambuc # define malloc_minsize 16U
719152e1c5SLionel Sambuc # endif
729152e1c5SLionel Sambuc # if defined(__amd64__)
739152e1c5SLionel Sambuc # define malloc_pageshift 12U
749152e1c5SLionel Sambuc # define malloc_minsize 16U
759152e1c5SLionel Sambuc # endif
769152e1c5SLionel Sambuc # if defined(__arm__)
779152e1c5SLionel Sambuc # define malloc_pageshift 12U
789152e1c5SLionel Sambuc # define malloc_minsize 16U
799152e1c5SLionel Sambuc # endif
809152e1c5SLionel Sambuc # define HAS_UTRACE
819152e1c5SLionel Sambuc # define UTRACE_LABEL
829152e1c5SLionel Sambuc
839152e1c5SLionel Sambuc #include <sys/cdefs.h>
849152e1c5SLionel Sambuc void utrace(struct ut *, int);
859152e1c5SLionel Sambuc
869152e1c5SLionel Sambuc /*
879152e1c5SLionel Sambuc * Make malloc/free/realloc thread-safe in libc for use with
889152e1c5SLionel Sambuc * kernel threads.
899152e1c5SLionel Sambuc */
909152e1c5SLionel Sambuc # include "libc_private.h"
919152e1c5SLionel Sambuc # include "spinlock.h"
929152e1c5SLionel Sambuc static spinlock_t thread_lock = _SPINLOCK_INITIALIZER;
939152e1c5SLionel Sambuc # define _MALLOC_LOCK() if (__isthreaded) _SPINLOCK(&thread_lock);
949152e1c5SLionel Sambuc # define _MALLOC_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock);
959152e1c5SLionel Sambuc #endif /* __FreeBSD__ */
969152e1c5SLionel Sambuc
97*0a6a1f1dSLionel Sambuc #if defined(__minix)
9829edcad3SBen Gras /* #undef these things so that malloc uses the non-internal symbols.
9929edcad3SBen Gras * This is necessary for VM to be able to define its own versions, and
10029edcad3SBen Gras * use this malloc.
10129edcad3SBen Gras */
102dda632a2SBen Gras #undef mmap
103dda632a2SBen Gras #undef munmap
104*0a6a1f1dSLionel Sambuc #endif /* defined(__minix) */
10529edcad3SBen Gras
1069152e1c5SLionel Sambuc #include <sys/types.h>
1079152e1c5SLionel Sambuc #if defined(__NetBSD__)
1089152e1c5SLionel Sambuc # define malloc_minsize 16U
1099152e1c5SLionel Sambuc # define HAS_UTRACE
1109152e1c5SLionel Sambuc # define UTRACE_LABEL "malloc",
1119152e1c5SLionel Sambuc #include <sys/cdefs.h>
1129152e1c5SLionel Sambuc #include "extern.h"
1139152e1c5SLionel Sambuc #if defined(LIBC_SCCS) && !defined(lint)
114*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: malloc.c,v 1.56 2014/09/18 13:58:20 christos Exp $");
1159152e1c5SLionel Sambuc #endif /* LIBC_SCCS and not lint */
1169152e1c5SLionel Sambuc int utrace(const char *, void *, size_t);
1179152e1c5SLionel Sambuc
1189152e1c5SLionel Sambuc #include <reentrant.h>
1199152e1c5SLionel Sambuc extern int __isthreaded;
1209152e1c5SLionel Sambuc static mutex_t thread_lock = MUTEX_INITIALIZER;
1219152e1c5SLionel Sambuc #define _MALLOC_LOCK() if (__isthreaded) mutex_lock(&thread_lock);
1229152e1c5SLionel Sambuc #define _MALLOC_UNLOCK() if (__isthreaded) mutex_unlock(&thread_lock);
1239152e1c5SLionel Sambuc #endif /* __NetBSD__ */
1249152e1c5SLionel Sambuc
1259152e1c5SLionel Sambuc #if defined(__sparc__) && defined(sun)
1269152e1c5SLionel Sambuc # define malloc_minsize 16U
1279152e1c5SLionel Sambuc # define MAP_ANON (0)
1289152e1c5SLionel Sambuc static int fdzero;
1299152e1c5SLionel Sambuc # define MMAP_FD fdzero
1309152e1c5SLionel Sambuc # define INIT_MMAP() \
131*0a6a1f1dSLionel Sambuc { if ((fdzero = open(_PATH_DEVZERO, O_RDWR | O_CLOEXEC, 0000)) == -1) \
1329152e1c5SLionel Sambuc wrterror("open of /dev/zero"); }
1339152e1c5SLionel Sambuc #endif /* __sparc__ */
1349152e1c5SLionel Sambuc
1359152e1c5SLionel Sambuc /* Insert your combination here... */
1369152e1c5SLionel Sambuc #if defined(__FOOCPU__) && defined(__BAROS__)
1379152e1c5SLionel Sambuc # define malloc_minsize 16U
1389152e1c5SLionel Sambuc #endif /* __FOOCPU__ && __BAROS__ */
1399152e1c5SLionel Sambuc
1409152e1c5SLionel Sambuc #ifndef ZEROSIZEPTR
1419152e1c5SLionel Sambuc #define ZEROSIZEPTR ((void *)(uintptr_t)(1UL << (malloc_pageshift - 1)))
1429152e1c5SLionel Sambuc #endif
1439152e1c5SLionel Sambuc
1449152e1c5SLionel Sambuc /*
1459152e1c5SLionel Sambuc * No user serviceable parts behind this point.
1469152e1c5SLionel Sambuc */
1479152e1c5SLionel Sambuc #include <sys/types.h>
1489152e1c5SLionel Sambuc #include <sys/mman.h>
1499152e1c5SLionel Sambuc #include <errno.h>
1509152e1c5SLionel Sambuc #include <fcntl.h>
1519152e1c5SLionel Sambuc #include <paths.h>
1529152e1c5SLionel Sambuc #include <stddef.h>
1539152e1c5SLionel Sambuc #include <stdio.h>
1549152e1c5SLionel Sambuc #include <stdlib.h>
1559152e1c5SLionel Sambuc #include <string.h>
1569152e1c5SLionel Sambuc #include <unistd.h>
1579152e1c5SLionel Sambuc
1589152e1c5SLionel Sambuc /*
1599152e1c5SLionel Sambuc * This structure describes a page worth of chunks.
1609152e1c5SLionel Sambuc */
1619152e1c5SLionel Sambuc
1629152e1c5SLionel Sambuc struct pginfo {
1639152e1c5SLionel Sambuc struct pginfo *next; /* next on the free list */
1649152e1c5SLionel Sambuc void *page; /* Pointer to the page */
1659152e1c5SLionel Sambuc u_short size; /* size of this page's chunks */
1669152e1c5SLionel Sambuc u_short shift; /* How far to shift for this size chunks */
1679152e1c5SLionel Sambuc u_short free; /* How many free chunks */
1689152e1c5SLionel Sambuc u_short total; /* How many chunk */
1699152e1c5SLionel Sambuc u_int bits[1]; /* Which chunks are free */
1709152e1c5SLionel Sambuc };
1719152e1c5SLionel Sambuc
1729152e1c5SLionel Sambuc /*
1739152e1c5SLionel Sambuc * This structure describes a number of free pages.
1749152e1c5SLionel Sambuc */
1759152e1c5SLionel Sambuc
1769152e1c5SLionel Sambuc struct pgfree {
1779152e1c5SLionel Sambuc struct pgfree *next; /* next run of free pages */
1789152e1c5SLionel Sambuc struct pgfree *prev; /* prev run of free pages */
1799152e1c5SLionel Sambuc void *page; /* pointer to free pages */
1809152e1c5SLionel Sambuc void *end; /* pointer to end of free pages */
1819152e1c5SLionel Sambuc size_t size; /* number of bytes free */
1829152e1c5SLionel Sambuc };
1839152e1c5SLionel Sambuc
1849152e1c5SLionel Sambuc /*
1859152e1c5SLionel Sambuc * How many bits per u_int in the bitmap.
1869152e1c5SLionel Sambuc * Change only if not 8 bits/byte
1879152e1c5SLionel Sambuc */
1889152e1c5SLionel Sambuc #define MALLOC_BITS ((int)(8*sizeof(u_int)))
1899152e1c5SLionel Sambuc
1909152e1c5SLionel Sambuc /*
1919152e1c5SLionel Sambuc * Magic values to put in the page_directory
1929152e1c5SLionel Sambuc */
1939152e1c5SLionel Sambuc #define MALLOC_NOT_MINE ((struct pginfo*) 0)
1949152e1c5SLionel Sambuc #define MALLOC_FREE ((struct pginfo*) 1)
1959152e1c5SLionel Sambuc #define MALLOC_FIRST ((struct pginfo*) 2)
1969152e1c5SLionel Sambuc #define MALLOC_FOLLOW ((struct pginfo*) 3)
1979152e1c5SLionel Sambuc #define MALLOC_MAGIC ((struct pginfo*) 4)
1989152e1c5SLionel Sambuc
1999152e1c5SLionel Sambuc /*
2009152e1c5SLionel Sambuc * Page size related parameters, computed at run-time.
2019152e1c5SLionel Sambuc */
2029152e1c5SLionel Sambuc static size_t malloc_pagesize;
2039152e1c5SLionel Sambuc static size_t malloc_pageshift;
2049152e1c5SLionel Sambuc static size_t malloc_pagemask;
2059152e1c5SLionel Sambuc
2069152e1c5SLionel Sambuc #ifndef malloc_minsize
2079152e1c5SLionel Sambuc #define malloc_minsize 16U
2089152e1c5SLionel Sambuc #endif
2099152e1c5SLionel Sambuc
2109152e1c5SLionel Sambuc #ifndef malloc_maxsize
2119152e1c5SLionel Sambuc #define malloc_maxsize ((malloc_pagesize)>>1)
2129152e1c5SLionel Sambuc #endif
2139152e1c5SLionel Sambuc
2149152e1c5SLionel Sambuc #define pageround(foo) (((foo) + (malloc_pagemask))&(~(malloc_pagemask)))
2159152e1c5SLionel Sambuc #define ptr2idx(foo) \
2169152e1c5SLionel Sambuc (((size_t)(uintptr_t)(foo) >> malloc_pageshift)-malloc_origo)
2179152e1c5SLionel Sambuc
2189152e1c5SLionel Sambuc #ifndef _MALLOC_LOCK
2199152e1c5SLionel Sambuc #define _MALLOC_LOCK()
2209152e1c5SLionel Sambuc #endif
2219152e1c5SLionel Sambuc
2229152e1c5SLionel Sambuc #ifndef _MALLOC_UNLOCK
2239152e1c5SLionel Sambuc #define _MALLOC_UNLOCK()
2249152e1c5SLionel Sambuc #endif
2259152e1c5SLionel Sambuc
2269152e1c5SLionel Sambuc #ifndef MMAP_FD
2279152e1c5SLionel Sambuc #define MMAP_FD (-1)
2289152e1c5SLionel Sambuc #endif
2299152e1c5SLionel Sambuc
2309152e1c5SLionel Sambuc #ifndef INIT_MMAP
2319152e1c5SLionel Sambuc #define INIT_MMAP()
2329152e1c5SLionel Sambuc #endif
2339152e1c5SLionel Sambuc
23484d9c625SLionel Sambuc #if !defined(__minix)
2359152e1c5SLionel Sambuc #ifndef MADV_FREE
2369152e1c5SLionel Sambuc #define MADV_FREE MADV_DONTNEED
2379152e1c5SLionel Sambuc #endif
23884d9c625SLionel Sambuc #else
23984d9c625SLionel Sambuc #undef MADV_FREE
24084d9c625SLionel Sambuc #endif /* !defined(__minix) */
2419152e1c5SLionel Sambuc
2429152e1c5SLionel Sambuc /* Number of free pages we cache */
2439152e1c5SLionel Sambuc static size_t malloc_cache = 16;
2449152e1c5SLionel Sambuc
2459152e1c5SLionel Sambuc /* The offset from pagenumber to index into the page directory */
2469152e1c5SLionel Sambuc static size_t malloc_origo;
2479152e1c5SLionel Sambuc
2489152e1c5SLionel Sambuc /* The last index in the page directory we care about */
2499152e1c5SLionel Sambuc static size_t last_idx;
2509152e1c5SLionel Sambuc
2519152e1c5SLionel Sambuc /* Pointer to page directory. Allocated "as if with" malloc */
2529152e1c5SLionel Sambuc static struct pginfo **page_dir;
2539152e1c5SLionel Sambuc
2549152e1c5SLionel Sambuc /* How many slots in the page directory */
2559152e1c5SLionel Sambuc static size_t malloc_ninfo;
2569152e1c5SLionel Sambuc
2579152e1c5SLionel Sambuc /* Free pages line up here */
2589152e1c5SLionel Sambuc static struct pgfree free_list;
2599152e1c5SLionel Sambuc
2609152e1c5SLionel Sambuc /* Abort(), user doesn't handle problems. */
2619152e1c5SLionel Sambuc static int malloc_abort;
2629152e1c5SLionel Sambuc
2639152e1c5SLionel Sambuc /* Are we trying to die ? */
2649152e1c5SLionel Sambuc static int suicide;
2659152e1c5SLionel Sambuc
2669152e1c5SLionel Sambuc /* always realloc ? */
2679152e1c5SLionel Sambuc static int malloc_realloc;
2689152e1c5SLionel Sambuc
2699152e1c5SLionel Sambuc /* pass the kernel a hint on free pages ? */
2709152e1c5SLionel Sambuc #if defined(MADV_FREE)
2719152e1c5SLionel Sambuc static int malloc_hint = 0;
2729152e1c5SLionel Sambuc #endif
2739152e1c5SLionel Sambuc
2749152e1c5SLionel Sambuc /* xmalloc behaviour ? */
2759152e1c5SLionel Sambuc static int malloc_xmalloc;
2769152e1c5SLionel Sambuc
2779152e1c5SLionel Sambuc /* sysv behaviour for malloc(0) ? */
2789152e1c5SLionel Sambuc static int malloc_sysv;
2799152e1c5SLionel Sambuc
2809152e1c5SLionel Sambuc /* zero fill ? */
2819152e1c5SLionel Sambuc static int malloc_zero;
2829152e1c5SLionel Sambuc
2839152e1c5SLionel Sambuc /* junk fill ? */
2849152e1c5SLionel Sambuc static int malloc_junk;
2859152e1c5SLionel Sambuc
2869152e1c5SLionel Sambuc #ifdef HAS_UTRACE
2879152e1c5SLionel Sambuc
2889152e1c5SLionel Sambuc /* utrace ? */
2899152e1c5SLionel Sambuc static int malloc_utrace;
2909152e1c5SLionel Sambuc
2919152e1c5SLionel Sambuc struct ut { void *p; size_t s; void *r; };
2929152e1c5SLionel Sambuc
2939152e1c5SLionel Sambuc #define UTRACE(a, b, c) \
2949152e1c5SLionel Sambuc if (malloc_utrace) { \
2959152e1c5SLionel Sambuc struct ut u; \
2969152e1c5SLionel Sambuc u.p=a; u.s = b; u.r=c; \
2979152e1c5SLionel Sambuc utrace(UTRACE_LABEL (void *) &u, sizeof u); \
2989152e1c5SLionel Sambuc }
2999152e1c5SLionel Sambuc #else /* !HAS_UTRACE */
3009152e1c5SLionel Sambuc #define UTRACE(a,b,c)
3019152e1c5SLionel Sambuc #endif /* HAS_UTRACE */
3029152e1c5SLionel Sambuc
3039152e1c5SLionel Sambuc /* my last break. */
3049152e1c5SLionel Sambuc static void *malloc_brk;
3059152e1c5SLionel Sambuc
3069152e1c5SLionel Sambuc /* one location cache for free-list holders */
3079152e1c5SLionel Sambuc static struct pgfree *px;
3089152e1c5SLionel Sambuc
3099152e1c5SLionel Sambuc /* compile-time options */
3109152e1c5SLionel Sambuc const char *_malloc_options;
3119152e1c5SLionel Sambuc
3129152e1c5SLionel Sambuc /* Name of the current public function */
3139152e1c5SLionel Sambuc static const char *malloc_func;
3149152e1c5SLionel Sambuc
3159152e1c5SLionel Sambuc /* Macro for mmap */
3169152e1c5SLionel Sambuc #define MMAP(size) \
3179152e1c5SLionel Sambuc mmap(NULL, (size), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, \
3189152e1c5SLionel Sambuc MMAP_FD, (off_t)0);
3199152e1c5SLionel Sambuc
3209152e1c5SLionel Sambuc /*
3219152e1c5SLionel Sambuc * Necessary function declarations
3229152e1c5SLionel Sambuc */
3239152e1c5SLionel Sambuc static int extend_pgdir(size_t idx);
3249152e1c5SLionel Sambuc static void *imalloc(size_t size);
3259152e1c5SLionel Sambuc static void ifree(void *ptr);
3269152e1c5SLionel Sambuc static void *irealloc(void *ptr, size_t size);
3279152e1c5SLionel Sambuc
3289152e1c5SLionel Sambuc #ifndef MALLOC_NO_SYSCALLS
3299152e1c5SLionel Sambuc static void
wrtmessage(const char * p1,const char * p2,const char * p3,const char * p4)3309152e1c5SLionel Sambuc wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4)
3319152e1c5SLionel Sambuc {
3329152e1c5SLionel Sambuc
3339152e1c5SLionel Sambuc write(STDERR_FILENO, p1, strlen(p1));
3349152e1c5SLionel Sambuc write(STDERR_FILENO, p2, strlen(p2));
3359152e1c5SLionel Sambuc write(STDERR_FILENO, p3, strlen(p3));
3369152e1c5SLionel Sambuc write(STDERR_FILENO, p4, strlen(p4));
3379152e1c5SLionel Sambuc }
3389152e1c5SLionel Sambuc
3399152e1c5SLionel Sambuc void (*_malloc_message)(const char *p1, const char *p2, const char *p3,
3409152e1c5SLionel Sambuc const char *p4) = wrtmessage;
3419152e1c5SLionel Sambuc static void
wrterror(const char * p)3429152e1c5SLionel Sambuc wrterror(const char *p)
3439152e1c5SLionel Sambuc {
3449152e1c5SLionel Sambuc
3459152e1c5SLionel Sambuc suicide = 1;
3469152e1c5SLionel Sambuc _malloc_message(getprogname(), malloc_func, " error: ", p);
3479152e1c5SLionel Sambuc abort();
3489152e1c5SLionel Sambuc }
3499152e1c5SLionel Sambuc
3509152e1c5SLionel Sambuc static void
wrtwarning(const char * p)3519152e1c5SLionel Sambuc wrtwarning(const char *p)
3529152e1c5SLionel Sambuc {
3539152e1c5SLionel Sambuc
3549152e1c5SLionel Sambuc /*
3559152e1c5SLionel Sambuc * Sensitive processes, somewhat arbitrarily defined here as setuid,
3569152e1c5SLionel Sambuc * setgid, root and wheel cannot afford to have malloc mistakes.
3579152e1c5SLionel Sambuc */
3589152e1c5SLionel Sambuc if (malloc_abort || issetugid() || getuid() == 0 || getgid() == 0)
3599152e1c5SLionel Sambuc wrterror(p);
3609152e1c5SLionel Sambuc }
3619152e1c5SLionel Sambuc #endif
3629152e1c5SLionel Sambuc
3639152e1c5SLionel Sambuc /*
3649152e1c5SLionel Sambuc * Allocate a number of pages from the OS
3659152e1c5SLionel Sambuc */
3669152e1c5SLionel Sambuc static void *
map_pages(size_t pages)3679152e1c5SLionel Sambuc map_pages(size_t pages)
3689152e1c5SLionel Sambuc {
3699152e1c5SLionel Sambuc caddr_t result, rresult, tail;
3709152e1c5SLionel Sambuc intptr_t bytes = pages << malloc_pageshift;
3719152e1c5SLionel Sambuc
3729152e1c5SLionel Sambuc if (bytes < 0 || (size_t)bytes < pages) {
3739152e1c5SLionel Sambuc errno = ENOMEM;
3749152e1c5SLionel Sambuc return NULL;
3759152e1c5SLionel Sambuc }
3769152e1c5SLionel Sambuc
3779152e1c5SLionel Sambuc if ((result = sbrk(bytes)) == (void *)-1)
3789152e1c5SLionel Sambuc return NULL;
3799152e1c5SLionel Sambuc
3809152e1c5SLionel Sambuc /*
3819152e1c5SLionel Sambuc * Round to a page, in case sbrk(2) did not do this for us
3829152e1c5SLionel Sambuc */
3839152e1c5SLionel Sambuc rresult = (caddr_t)pageround((size_t)(uintptr_t)result);
3849152e1c5SLionel Sambuc if (result < rresult) {
3859152e1c5SLionel Sambuc /* make sure we have enough space to fit bytes */
3869152e1c5SLionel Sambuc if (sbrk((intptr_t)(rresult - result)) == (void *) -1) {
3879152e1c5SLionel Sambuc /* we failed, put everything back */
3889152e1c5SLionel Sambuc if (brk(result)) {
3899152e1c5SLionel Sambuc wrterror("brk(2) failed [internal error]\n");
3909152e1c5SLionel Sambuc }
3919152e1c5SLionel Sambuc }
3929152e1c5SLionel Sambuc }
3939152e1c5SLionel Sambuc tail = rresult + (size_t)bytes;
3949152e1c5SLionel Sambuc
3959152e1c5SLionel Sambuc last_idx = ptr2idx(tail) - 1;
3969152e1c5SLionel Sambuc malloc_brk = tail;
3979152e1c5SLionel Sambuc
3989152e1c5SLionel Sambuc if ((last_idx+1) >= malloc_ninfo && !extend_pgdir(last_idx)) {
3999152e1c5SLionel Sambuc malloc_brk = result;
4009152e1c5SLionel Sambuc last_idx = ptr2idx(malloc_brk) - 1;
4019152e1c5SLionel Sambuc /* Put back break point since we failed. */
4029152e1c5SLionel Sambuc if (brk(malloc_brk))
4039152e1c5SLionel Sambuc wrterror("brk(2) failed [internal error]\n");
4049152e1c5SLionel Sambuc return 0;
4059152e1c5SLionel Sambuc }
4069152e1c5SLionel Sambuc
4079152e1c5SLionel Sambuc return rresult;
4089152e1c5SLionel Sambuc }
4099152e1c5SLionel Sambuc
4109152e1c5SLionel Sambuc /*
4119152e1c5SLionel Sambuc * Extend page directory
4129152e1c5SLionel Sambuc */
4139152e1c5SLionel Sambuc static int
extend_pgdir(size_t idx)4149152e1c5SLionel Sambuc extend_pgdir(size_t idx)
4159152e1c5SLionel Sambuc {
4169152e1c5SLionel Sambuc struct pginfo **new, **old;
4179152e1c5SLionel Sambuc size_t newlen, oldlen;
4189152e1c5SLionel Sambuc
4199152e1c5SLionel Sambuc /* check for overflow */
4209152e1c5SLionel Sambuc if ((((~(1UL << ((sizeof(size_t) * NBBY) - 1)) / sizeof(*page_dir)) + 1)
4219152e1c5SLionel Sambuc + (malloc_pagesize / sizeof *page_dir)) < idx) {
4229152e1c5SLionel Sambuc errno = ENOMEM;
4239152e1c5SLionel Sambuc return 0;
4249152e1c5SLionel Sambuc }
4259152e1c5SLionel Sambuc
4269152e1c5SLionel Sambuc /* Make it this many pages */
4279152e1c5SLionel Sambuc newlen = pageround(idx * sizeof *page_dir) + malloc_pagesize;
4289152e1c5SLionel Sambuc
4299152e1c5SLionel Sambuc /* remember the old mapping size */
4309152e1c5SLionel Sambuc oldlen = malloc_ninfo * sizeof *page_dir;
4319152e1c5SLionel Sambuc
4329152e1c5SLionel Sambuc /*
4339152e1c5SLionel Sambuc * NOTE: we allocate new pages and copy the directory rather than tempt
4349152e1c5SLionel Sambuc * fate by trying to "grow" the region.. There is nothing to prevent
4359152e1c5SLionel Sambuc * us from accidentally re-mapping space that's been allocated by our caller
4369152e1c5SLionel Sambuc * via dlopen() or other mmap().
4379152e1c5SLionel Sambuc *
4389152e1c5SLionel Sambuc * The copy problem is not too bad, as there is 4K of page index per
4399152e1c5SLionel Sambuc * 4MB of malloc arena.
4409152e1c5SLionel Sambuc *
4419152e1c5SLionel Sambuc * We can totally avoid the copy if we open a file descriptor to associate
4429152e1c5SLionel Sambuc * the anon mappings with. Then, when we remap the pages at the new
4439152e1c5SLionel Sambuc * address, the old pages will be "magically" remapped.. But this means
4449152e1c5SLionel Sambuc * keeping open a "secret" file descriptor.....
4459152e1c5SLionel Sambuc */
4469152e1c5SLionel Sambuc
4479152e1c5SLionel Sambuc /* Get new pages */
4489152e1c5SLionel Sambuc new = MMAP(newlen);
4499152e1c5SLionel Sambuc if (new == MAP_FAILED)
4509152e1c5SLionel Sambuc return 0;
4519152e1c5SLionel Sambuc
4529152e1c5SLionel Sambuc /* Copy the old stuff */
4539152e1c5SLionel Sambuc memcpy(new, page_dir, oldlen);
4549152e1c5SLionel Sambuc
4559152e1c5SLionel Sambuc /* register the new size */
4569152e1c5SLionel Sambuc malloc_ninfo = newlen / sizeof *page_dir;
4579152e1c5SLionel Sambuc
4589152e1c5SLionel Sambuc /* swap the pointers */
4599152e1c5SLionel Sambuc old = page_dir;
4609152e1c5SLionel Sambuc page_dir = new;
4619152e1c5SLionel Sambuc
4629152e1c5SLionel Sambuc /* Now free the old stuff */
4639152e1c5SLionel Sambuc munmap(old, oldlen);
4649152e1c5SLionel Sambuc return 1;
4659152e1c5SLionel Sambuc }
4669152e1c5SLionel Sambuc
4679152e1c5SLionel Sambuc /*
4689152e1c5SLionel Sambuc * Initialize the world
4699152e1c5SLionel Sambuc */
4709152e1c5SLionel Sambuc static void
malloc_init(void)4719152e1c5SLionel Sambuc malloc_init(void)
4729152e1c5SLionel Sambuc {
4739152e1c5SLionel Sambuc #ifndef MALLOC_NO_SYSCALLS
4749152e1c5SLionel Sambuc const char *p;
4759152e1c5SLionel Sambuc char b[64];
4769152e1c5SLionel Sambuc size_t i;
4779152e1c5SLionel Sambuc ssize_t j;
478f14fb602SLionel Sambuc #endif
479f14fb602SLionel Sambuc int serrno = errno;
480f14fb602SLionel Sambuc #ifndef MALLOC_NO_SYSCALLS
4819152e1c5SLionel Sambuc
4829152e1c5SLionel Sambuc /*
4839152e1c5SLionel Sambuc * Compute page-size related variables.
4849152e1c5SLionel Sambuc */
4859152e1c5SLionel Sambuc malloc_pagesize = (size_t)sysconf(_SC_PAGESIZE);
4869152e1c5SLionel Sambuc #else
4879152e1c5SLionel Sambuc malloc_pagesize = PAGE_SIZE;
4889152e1c5SLionel Sambuc #endif
4899152e1c5SLionel Sambuc malloc_pagemask = malloc_pagesize - 1;
4909152e1c5SLionel Sambuc for (malloc_pageshift = 0;
4919152e1c5SLionel Sambuc (1UL << malloc_pageshift) != malloc_pagesize;
4929152e1c5SLionel Sambuc malloc_pageshift++)
4939152e1c5SLionel Sambuc /* nothing */ ;
4949152e1c5SLionel Sambuc
4959152e1c5SLionel Sambuc INIT_MMAP();
4969152e1c5SLionel Sambuc
4979152e1c5SLionel Sambuc #ifdef MALLOC_EXTRA_SANITY
4989152e1c5SLionel Sambuc malloc_junk = 1;
4999152e1c5SLionel Sambuc #endif /* MALLOC_EXTRA_SANITY */
5009152e1c5SLionel Sambuc
5019152e1c5SLionel Sambuc #ifndef MALLOC_NO_SYSCALLS
5029152e1c5SLionel Sambuc for (i = 0; i < 3; i++) {
5039152e1c5SLionel Sambuc if (i == 0) {
5049152e1c5SLionel Sambuc j = readlink("/etc/malloc.conf", b, sizeof b - 1);
505f14fb602SLionel Sambuc if (j == -1)
5069152e1c5SLionel Sambuc continue;
5079152e1c5SLionel Sambuc b[j] = '\0';
5089152e1c5SLionel Sambuc p = b;
5099152e1c5SLionel Sambuc } else if (i == 1 && issetugid() == 0) {
5109152e1c5SLionel Sambuc p = getenv("MALLOC_OPTIONS");
5119152e1c5SLionel Sambuc } else if (i == 1) {
5129152e1c5SLionel Sambuc continue;
5139152e1c5SLionel Sambuc } else {
5149152e1c5SLionel Sambuc p = _malloc_options;
5159152e1c5SLionel Sambuc }
5169152e1c5SLionel Sambuc for (; p != NULL && *p != '\0'; p++) {
5179152e1c5SLionel Sambuc switch (*p) {
5189152e1c5SLionel Sambuc case '>': malloc_cache <<= 1; break;
5199152e1c5SLionel Sambuc case '<': malloc_cache >>= 1; break;
5209152e1c5SLionel Sambuc case 'a': malloc_abort = 0; break;
5219152e1c5SLionel Sambuc case 'A': malloc_abort = 1; break;
52284d9c625SLionel Sambuc #if !defined(__minix)
5239152e1c5SLionel Sambuc case 'h': malloc_hint = 0; break;
5249152e1c5SLionel Sambuc case 'H': malloc_hint = 1; break;
52584d9c625SLionel Sambuc #endif /* !defined(__minix) */
5269152e1c5SLionel Sambuc case 'r': malloc_realloc = 0; break;
5279152e1c5SLionel Sambuc case 'R': malloc_realloc = 1; break;
5289152e1c5SLionel Sambuc case 'j': malloc_junk = 0; break;
5299152e1c5SLionel Sambuc case 'J': malloc_junk = 1; break;
5309152e1c5SLionel Sambuc #ifdef HAS_UTRACE
5319152e1c5SLionel Sambuc case 'u': malloc_utrace = 0; break;
5329152e1c5SLionel Sambuc case 'U': malloc_utrace = 1; break;
5339152e1c5SLionel Sambuc #endif
5349152e1c5SLionel Sambuc case 'v': malloc_sysv = 0; break;
5359152e1c5SLionel Sambuc case 'V': malloc_sysv = 1; break;
5369152e1c5SLionel Sambuc case 'x': malloc_xmalloc = 0; break;
5379152e1c5SLionel Sambuc case 'X': malloc_xmalloc = 1; break;
5389152e1c5SLionel Sambuc case 'z': malloc_zero = 0; break;
5399152e1c5SLionel Sambuc case 'Z': malloc_zero = 1; break;
5409152e1c5SLionel Sambuc default:
5419152e1c5SLionel Sambuc _malloc_message(getprogname(), malloc_func,
5429152e1c5SLionel Sambuc " warning: ", "unknown char in MALLOC_OPTIONS\n");
5439152e1c5SLionel Sambuc break;
5449152e1c5SLionel Sambuc }
5459152e1c5SLionel Sambuc }
5469152e1c5SLionel Sambuc }
5479152e1c5SLionel Sambuc #endif
5489152e1c5SLionel Sambuc
5499152e1c5SLionel Sambuc UTRACE(0, 0, 0);
5509152e1c5SLionel Sambuc
5519152e1c5SLionel Sambuc /*
5529152e1c5SLionel Sambuc * We want junk in the entire allocation, and zero only in the part
5539152e1c5SLionel Sambuc * the user asked for.
5549152e1c5SLionel Sambuc */
5559152e1c5SLionel Sambuc if (malloc_zero)
5569152e1c5SLionel Sambuc malloc_junk = 1;
5579152e1c5SLionel Sambuc
5589152e1c5SLionel Sambuc /* Allocate one page for the page directory */
5599152e1c5SLionel Sambuc page_dir = MMAP(malloc_pagesize);
5609152e1c5SLionel Sambuc
5619152e1c5SLionel Sambuc if (page_dir == MAP_FAILED)
5629152e1c5SLionel Sambuc wrterror("mmap(2) failed, check limits.\n");
5639152e1c5SLionel Sambuc
5649152e1c5SLionel Sambuc /*
5659152e1c5SLionel Sambuc * We need a maximum of malloc_pageshift buckets, steal these from the
5669152e1c5SLionel Sambuc * front of the page_directory;
5679152e1c5SLionel Sambuc */
5689152e1c5SLionel Sambuc malloc_origo = pageround((size_t)(uintptr_t)sbrk((intptr_t)0))
5699152e1c5SLionel Sambuc >> malloc_pageshift;
5709152e1c5SLionel Sambuc malloc_origo -= malloc_pageshift;
5719152e1c5SLionel Sambuc
5729152e1c5SLionel Sambuc malloc_ninfo = malloc_pagesize / sizeof *page_dir;
5739152e1c5SLionel Sambuc
5749152e1c5SLionel Sambuc /* Recalculate the cache size in bytes, and make sure it's nonzero */
5759152e1c5SLionel Sambuc
5769152e1c5SLionel Sambuc if (!malloc_cache)
5779152e1c5SLionel Sambuc malloc_cache++;
5789152e1c5SLionel Sambuc
5799152e1c5SLionel Sambuc malloc_cache <<= malloc_pageshift;
5809152e1c5SLionel Sambuc
5819152e1c5SLionel Sambuc /*
5829152e1c5SLionel Sambuc * This is a nice hack from Kaleb Keithly (kaleb@x.org).
5839152e1c5SLionel Sambuc * We can sbrk(2) further back when we keep this on a low address.
5849152e1c5SLionel Sambuc */
5859152e1c5SLionel Sambuc px = imalloc(sizeof *px);
5869152e1c5SLionel Sambuc
587f14fb602SLionel Sambuc errno = serrno;
5889152e1c5SLionel Sambuc }
5899152e1c5SLionel Sambuc
5909152e1c5SLionel Sambuc /*
5919152e1c5SLionel Sambuc * Allocate a number of complete pages
5929152e1c5SLionel Sambuc */
5939152e1c5SLionel Sambuc static void *
malloc_pages(size_t size)5949152e1c5SLionel Sambuc malloc_pages(size_t size)
5959152e1c5SLionel Sambuc {
5969152e1c5SLionel Sambuc void *p, *delay_free = NULL;
5979152e1c5SLionel Sambuc size_t i;
5989152e1c5SLionel Sambuc struct pgfree *pf;
5999152e1c5SLionel Sambuc size_t idx;
6009152e1c5SLionel Sambuc
6019152e1c5SLionel Sambuc idx = pageround(size);
6029152e1c5SLionel Sambuc if (idx < size) {
6039152e1c5SLionel Sambuc errno = ENOMEM;
6049152e1c5SLionel Sambuc return NULL;
6059152e1c5SLionel Sambuc } else
6069152e1c5SLionel Sambuc size = idx;
6079152e1c5SLionel Sambuc
6089152e1c5SLionel Sambuc p = NULL;
6099152e1c5SLionel Sambuc
6109152e1c5SLionel Sambuc /* Look for free pages before asking for more */
6119152e1c5SLionel Sambuc for(pf = free_list.next; pf; pf = pf->next) {
6129152e1c5SLionel Sambuc
6139152e1c5SLionel Sambuc #ifdef MALLOC_EXTRA_SANITY
6149152e1c5SLionel Sambuc if (pf->size & malloc_pagemask)
6159152e1c5SLionel Sambuc wrterror("(ES): junk length entry on free_list.\n");
6169152e1c5SLionel Sambuc if (!pf->size)
6179152e1c5SLionel Sambuc wrterror("(ES): zero length entry on free_list.\n");
6189152e1c5SLionel Sambuc if (pf->page == pf->end)
6199152e1c5SLionel Sambuc wrterror("(ES): zero entry on free_list.\n");
6209152e1c5SLionel Sambuc if (pf->page > pf->end)
6219152e1c5SLionel Sambuc wrterror("(ES): sick entry on free_list.\n");
6229152e1c5SLionel Sambuc if ((void*)pf->page >= (void*)sbrk(0))
6239152e1c5SLionel Sambuc wrterror("(ES): entry on free_list past brk.\n");
6249152e1c5SLionel Sambuc if (page_dir[ptr2idx(pf->page)] != MALLOC_FREE)
6259152e1c5SLionel Sambuc wrterror("(ES): non-free first page on free-list.\n");
6269152e1c5SLionel Sambuc if (page_dir[ptr2idx(pf->end)-1] != MALLOC_FREE)
6279152e1c5SLionel Sambuc wrterror("(ES): non-free last page on free-list.\n");
6289152e1c5SLionel Sambuc #endif /* MALLOC_EXTRA_SANITY */
6299152e1c5SLionel Sambuc
6309152e1c5SLionel Sambuc if (pf->size < size)
6319152e1c5SLionel Sambuc continue;
6329152e1c5SLionel Sambuc
6339152e1c5SLionel Sambuc if (pf->size == size) {
6349152e1c5SLionel Sambuc p = pf->page;
6359152e1c5SLionel Sambuc if (pf->next != NULL)
6369152e1c5SLionel Sambuc pf->next->prev = pf->prev;
6379152e1c5SLionel Sambuc pf->prev->next = pf->next;
6389152e1c5SLionel Sambuc delay_free = pf;
6399152e1c5SLionel Sambuc break;
6409152e1c5SLionel Sambuc }
6419152e1c5SLionel Sambuc
6429152e1c5SLionel Sambuc p = pf->page;
6439152e1c5SLionel Sambuc pf->page = (char *)pf->page + size;
6449152e1c5SLionel Sambuc pf->size -= size;
6459152e1c5SLionel Sambuc break;
6469152e1c5SLionel Sambuc }
6479152e1c5SLionel Sambuc
6489152e1c5SLionel Sambuc #ifdef MALLOC_EXTRA_SANITY
6499152e1c5SLionel Sambuc if (p != NULL && page_dir[ptr2idx(p)] != MALLOC_FREE)
6509152e1c5SLionel Sambuc wrterror("(ES): allocated non-free page on free-list.\n");
6519152e1c5SLionel Sambuc #endif /* MALLOC_EXTRA_SANITY */
6529152e1c5SLionel Sambuc
6539152e1c5SLionel Sambuc size >>= malloc_pageshift;
6549152e1c5SLionel Sambuc
6559152e1c5SLionel Sambuc /* Map new pages */
6569152e1c5SLionel Sambuc if (p == NULL)
6579152e1c5SLionel Sambuc p = map_pages(size);
6589152e1c5SLionel Sambuc
6599152e1c5SLionel Sambuc if (p != NULL) {
6609152e1c5SLionel Sambuc
6619152e1c5SLionel Sambuc idx = ptr2idx(p);
6629152e1c5SLionel Sambuc page_dir[idx] = MALLOC_FIRST;
6639152e1c5SLionel Sambuc for (i=1;i<size;i++)
6649152e1c5SLionel Sambuc page_dir[idx+i] = MALLOC_FOLLOW;
6659152e1c5SLionel Sambuc
6669152e1c5SLionel Sambuc if (malloc_junk)
6679152e1c5SLionel Sambuc memset(p, SOME_JUNK, size << malloc_pageshift);
6689152e1c5SLionel Sambuc }
6699152e1c5SLionel Sambuc
6709152e1c5SLionel Sambuc if (delay_free) {
6719152e1c5SLionel Sambuc if (px == NULL)
6729152e1c5SLionel Sambuc px = delay_free;
6739152e1c5SLionel Sambuc else
6749152e1c5SLionel Sambuc ifree(delay_free);
6759152e1c5SLionel Sambuc }
6769152e1c5SLionel Sambuc
6779152e1c5SLionel Sambuc return p;
6789152e1c5SLionel Sambuc }
6799152e1c5SLionel Sambuc
6809152e1c5SLionel Sambuc /*
6819152e1c5SLionel Sambuc * Allocate a page of fragments
6829152e1c5SLionel Sambuc */
6839152e1c5SLionel Sambuc
6849152e1c5SLionel Sambuc static inline int
malloc_make_chunks(int bits)6859152e1c5SLionel Sambuc malloc_make_chunks(int bits)
6869152e1c5SLionel Sambuc {
6879152e1c5SLionel Sambuc struct pginfo *bp;
6889152e1c5SLionel Sambuc void *pp;
6899152e1c5SLionel Sambuc int i, k;
6909152e1c5SLionel Sambuc long l;
6919152e1c5SLionel Sambuc
6929152e1c5SLionel Sambuc /* Allocate a new bucket */
6939152e1c5SLionel Sambuc pp = malloc_pages(malloc_pagesize);
6949152e1c5SLionel Sambuc if (pp == NULL)
6959152e1c5SLionel Sambuc return 0;
6969152e1c5SLionel Sambuc
6979152e1c5SLionel Sambuc /* Find length of admin structure */
6989152e1c5SLionel Sambuc l = (long)offsetof(struct pginfo, bits[0]);
6999152e1c5SLionel Sambuc l += (long)sizeof bp->bits[0] *
7009152e1c5SLionel Sambuc (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS);
7019152e1c5SLionel Sambuc
7029152e1c5SLionel Sambuc /* Don't waste more than two chunks on this */
7039152e1c5SLionel Sambuc if ((1<<(bits)) <= l+l) {
7049152e1c5SLionel Sambuc bp = (struct pginfo *)pp;
7059152e1c5SLionel Sambuc } else {
7069152e1c5SLionel Sambuc bp = imalloc((size_t)l);
7079152e1c5SLionel Sambuc if (bp == NULL) {
7089152e1c5SLionel Sambuc ifree(pp);
7099152e1c5SLionel Sambuc return 0;
7109152e1c5SLionel Sambuc }
7119152e1c5SLionel Sambuc }
7129152e1c5SLionel Sambuc
7139152e1c5SLionel Sambuc bp->size = (1<<bits);
7149152e1c5SLionel Sambuc bp->shift = bits;
7159152e1c5SLionel Sambuc bp->total = bp->free = (u_short)(malloc_pagesize >> bits);
7169152e1c5SLionel Sambuc bp->page = pp;
7179152e1c5SLionel Sambuc
7189152e1c5SLionel Sambuc /* set all valid bits in the bitmap */
7199152e1c5SLionel Sambuc k = bp->total;
7209152e1c5SLionel Sambuc i = 0;
7219152e1c5SLionel Sambuc
7229152e1c5SLionel Sambuc /* Do a bunch at a time */
7239152e1c5SLionel Sambuc for(;k-i >= MALLOC_BITS; i += MALLOC_BITS)
7249152e1c5SLionel Sambuc bp->bits[i / MALLOC_BITS] = ~0U;
7259152e1c5SLionel Sambuc
7269152e1c5SLionel Sambuc for(; i < k; i++)
7279152e1c5SLionel Sambuc bp->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS);
7289152e1c5SLionel Sambuc
7299152e1c5SLionel Sambuc if (bp == bp->page) {
7309152e1c5SLionel Sambuc /* Mark the ones we stole for ourselves */
7319152e1c5SLionel Sambuc for(i = 0; l > 0; i++) {
7329152e1c5SLionel Sambuc bp->bits[i / MALLOC_BITS] &= ~(1 << (i % MALLOC_BITS));
7339152e1c5SLionel Sambuc bp->free--;
7349152e1c5SLionel Sambuc bp->total--;
7359152e1c5SLionel Sambuc l -= (long)(1 << bits);
7369152e1c5SLionel Sambuc }
7379152e1c5SLionel Sambuc }
7389152e1c5SLionel Sambuc
7399152e1c5SLionel Sambuc /* MALLOC_LOCK */
7409152e1c5SLionel Sambuc
7419152e1c5SLionel Sambuc page_dir[ptr2idx(pp)] = bp;
7429152e1c5SLionel Sambuc
7439152e1c5SLionel Sambuc bp->next = page_dir[bits];
7449152e1c5SLionel Sambuc page_dir[bits] = bp;
7459152e1c5SLionel Sambuc
7469152e1c5SLionel Sambuc /* MALLOC_UNLOCK */
7479152e1c5SLionel Sambuc
7489152e1c5SLionel Sambuc return 1;
7499152e1c5SLionel Sambuc }
7509152e1c5SLionel Sambuc
7519152e1c5SLionel Sambuc /*
7529152e1c5SLionel Sambuc * Allocate a fragment
7539152e1c5SLionel Sambuc */
7549152e1c5SLionel Sambuc static void *
malloc_bytes(size_t size)7559152e1c5SLionel Sambuc malloc_bytes(size_t size)
7569152e1c5SLionel Sambuc {
7579152e1c5SLionel Sambuc size_t i;
7589152e1c5SLionel Sambuc int j;
7599152e1c5SLionel Sambuc u_int u;
7609152e1c5SLionel Sambuc struct pginfo *bp;
7619152e1c5SLionel Sambuc size_t k;
7629152e1c5SLionel Sambuc u_int *lp;
7639152e1c5SLionel Sambuc
7649152e1c5SLionel Sambuc /* Don't bother with anything less than this */
7659152e1c5SLionel Sambuc if (size < malloc_minsize)
7669152e1c5SLionel Sambuc size = malloc_minsize;
7679152e1c5SLionel Sambuc
7689152e1c5SLionel Sambuc
7699152e1c5SLionel Sambuc /* Find the right bucket */
7709152e1c5SLionel Sambuc j = 1;
7719152e1c5SLionel Sambuc i = size-1;
7729152e1c5SLionel Sambuc while (i >>= 1)
7739152e1c5SLionel Sambuc j++;
7749152e1c5SLionel Sambuc
7759152e1c5SLionel Sambuc /* If it's empty, make a page more of that size chunks */
7769152e1c5SLionel Sambuc if (page_dir[j] == NULL && !malloc_make_chunks(j))
7779152e1c5SLionel Sambuc return NULL;
7789152e1c5SLionel Sambuc
7799152e1c5SLionel Sambuc bp = page_dir[j];
7809152e1c5SLionel Sambuc
7819152e1c5SLionel Sambuc /* Find first word of bitmap which isn't empty */
7829152e1c5SLionel Sambuc for (lp = bp->bits; !*lp; lp++)
7839152e1c5SLionel Sambuc ;
7849152e1c5SLionel Sambuc
7859152e1c5SLionel Sambuc /* Find that bit, and tweak it */
7869152e1c5SLionel Sambuc u = 1;
7879152e1c5SLionel Sambuc k = 0;
7889152e1c5SLionel Sambuc while (!(*lp & u)) {
7899152e1c5SLionel Sambuc u += u;
7909152e1c5SLionel Sambuc k++;
7919152e1c5SLionel Sambuc }
7929152e1c5SLionel Sambuc *lp ^= u;
7939152e1c5SLionel Sambuc
7949152e1c5SLionel Sambuc /* If there are no more free, remove from free-list */
7959152e1c5SLionel Sambuc if (!--bp->free) {
7969152e1c5SLionel Sambuc page_dir[j] = bp->next;
7979152e1c5SLionel Sambuc bp->next = NULL;
7989152e1c5SLionel Sambuc }
7999152e1c5SLionel Sambuc
8009152e1c5SLionel Sambuc /* Adjust to the real offset of that chunk */
8019152e1c5SLionel Sambuc k += (lp-bp->bits)*MALLOC_BITS;
8029152e1c5SLionel Sambuc k <<= bp->shift;
8039152e1c5SLionel Sambuc
8049152e1c5SLionel Sambuc if (malloc_junk)
8059152e1c5SLionel Sambuc memset((u_char*)bp->page + k, SOME_JUNK, (size_t)bp->size);
8069152e1c5SLionel Sambuc
8079152e1c5SLionel Sambuc return (u_char *)bp->page + k;
8089152e1c5SLionel Sambuc }
8099152e1c5SLionel Sambuc
8109152e1c5SLionel Sambuc /*
8119152e1c5SLionel Sambuc * Allocate a piece of memory
8129152e1c5SLionel Sambuc */
8139152e1c5SLionel Sambuc static void *
imalloc(size_t size)8149152e1c5SLionel Sambuc imalloc(size_t size)
8159152e1c5SLionel Sambuc {
8169152e1c5SLionel Sambuc void *result;
8179152e1c5SLionel Sambuc
8189152e1c5SLionel Sambuc if (suicide)
8199152e1c5SLionel Sambuc abort();
8209152e1c5SLionel Sambuc
8219152e1c5SLionel Sambuc if ((size + malloc_pagesize) < size) /* Check for overflow */
8229152e1c5SLionel Sambuc result = NULL;
8232e23f175SDavid van Moolenbroek #ifndef __minix
8249152e1c5SLionel Sambuc else if ((size + malloc_pagesize) >= (uintptr_t)page_dir)
8259152e1c5SLionel Sambuc result = NULL;
8262e23f175SDavid van Moolenbroek #endif /* !__minix */
8279152e1c5SLionel Sambuc else if (size <= malloc_maxsize)
8289152e1c5SLionel Sambuc result = malloc_bytes(size);
8299152e1c5SLionel Sambuc else
8309152e1c5SLionel Sambuc result = malloc_pages(size);
8319152e1c5SLionel Sambuc
8329152e1c5SLionel Sambuc if (malloc_abort && result == NULL)
8339152e1c5SLionel Sambuc wrterror("allocation failed.\n");
8349152e1c5SLionel Sambuc
8359152e1c5SLionel Sambuc if (malloc_zero && result != NULL)
8369152e1c5SLionel Sambuc memset(result, 0, size);
8379152e1c5SLionel Sambuc
8389152e1c5SLionel Sambuc return result;
8399152e1c5SLionel Sambuc }
8409152e1c5SLionel Sambuc
8419152e1c5SLionel Sambuc /*
8429152e1c5SLionel Sambuc * Change the size of an allocation.
8439152e1c5SLionel Sambuc */
8449152e1c5SLionel Sambuc static void *
irealloc(void * ptr,size_t size)8459152e1c5SLionel Sambuc irealloc(void *ptr, size_t size)
8469152e1c5SLionel Sambuc {
8479152e1c5SLionel Sambuc void *p;
8489152e1c5SLionel Sambuc size_t osize, idx;
8499152e1c5SLionel Sambuc struct pginfo **mp;
8509152e1c5SLionel Sambuc size_t i;
8519152e1c5SLionel Sambuc
8529152e1c5SLionel Sambuc if (suicide)
8539152e1c5SLionel Sambuc abort();
8549152e1c5SLionel Sambuc
8559152e1c5SLionel Sambuc idx = ptr2idx(ptr);
8569152e1c5SLionel Sambuc
8579152e1c5SLionel Sambuc if (idx < malloc_pageshift) {
8589152e1c5SLionel Sambuc wrtwarning("junk pointer, too low to make sense.\n");
8599152e1c5SLionel Sambuc return 0;
8609152e1c5SLionel Sambuc }
8619152e1c5SLionel Sambuc
8629152e1c5SLionel Sambuc if (idx > last_idx) {
8639152e1c5SLionel Sambuc wrtwarning("junk pointer, too high to make sense.\n");
8649152e1c5SLionel Sambuc return 0;
8659152e1c5SLionel Sambuc }
8669152e1c5SLionel Sambuc
8679152e1c5SLionel Sambuc mp = &page_dir[idx];
8689152e1c5SLionel Sambuc
8699152e1c5SLionel Sambuc if (*mp == MALLOC_FIRST) { /* Page allocation */
8709152e1c5SLionel Sambuc
8719152e1c5SLionel Sambuc /* Check the pointer */
8729152e1c5SLionel Sambuc if ((size_t)(uintptr_t)ptr & malloc_pagemask) {
8739152e1c5SLionel Sambuc wrtwarning("modified (page-) pointer.\n");
8749152e1c5SLionel Sambuc return NULL;
8759152e1c5SLionel Sambuc }
8769152e1c5SLionel Sambuc
8779152e1c5SLionel Sambuc /* Find the size in bytes */
8789152e1c5SLionel Sambuc for (osize = malloc_pagesize; *++mp == MALLOC_FOLLOW;)
8799152e1c5SLionel Sambuc osize += malloc_pagesize;
8809152e1c5SLionel Sambuc
8819152e1c5SLionel Sambuc if (!malloc_realloc && /* unless we have to, */
8829152e1c5SLionel Sambuc size <= osize && /* .. or are too small, */
8839152e1c5SLionel Sambuc size > (osize - malloc_pagesize)) { /* .. or can free a page, */
8849152e1c5SLionel Sambuc if (malloc_junk)
8859152e1c5SLionel Sambuc memset((u_char *)ptr + size, SOME_JUNK, osize-size);
8869152e1c5SLionel Sambuc return ptr; /* don't do anything. */
8879152e1c5SLionel Sambuc }
8889152e1c5SLionel Sambuc
8899152e1c5SLionel Sambuc } else if (*mp >= MALLOC_MAGIC) { /* Chunk allocation */
8909152e1c5SLionel Sambuc
8919152e1c5SLionel Sambuc /* Check the pointer for sane values */
8929152e1c5SLionel Sambuc if (((size_t)(uintptr_t)ptr & ((*mp)->size-1))) {
8939152e1c5SLionel Sambuc wrtwarning("modified (chunk-) pointer.\n");
8949152e1c5SLionel Sambuc return NULL;
8959152e1c5SLionel Sambuc }
8969152e1c5SLionel Sambuc
8979152e1c5SLionel Sambuc /* Find the chunk index in the page */
8989152e1c5SLionel Sambuc i = ((size_t)(uintptr_t)ptr & malloc_pagemask) >> (*mp)->shift;
8999152e1c5SLionel Sambuc
9009152e1c5SLionel Sambuc /* Verify that it isn't a free chunk already */
9019152e1c5SLionel Sambuc if ((*mp)->bits[i/MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) {
9029152e1c5SLionel Sambuc wrtwarning("chunk is already free.\n");
9039152e1c5SLionel Sambuc return NULL;
9049152e1c5SLionel Sambuc }
9059152e1c5SLionel Sambuc
9069152e1c5SLionel Sambuc osize = (*mp)->size;
9079152e1c5SLionel Sambuc
9089152e1c5SLionel Sambuc if (!malloc_realloc && /* Unless we have to, */
9099152e1c5SLionel Sambuc size <= osize && /* ..or are too small, */
9109152e1c5SLionel Sambuc (size > osize / 2 || /* ..or could use a smaller size, */
9119152e1c5SLionel Sambuc osize == malloc_minsize)) { /* ..(if there is one) */
9129152e1c5SLionel Sambuc if (malloc_junk)
9139152e1c5SLionel Sambuc memset((u_char *)ptr + size, SOME_JUNK, osize-size);
9149152e1c5SLionel Sambuc return ptr; /* ..Don't do anything */
9159152e1c5SLionel Sambuc }
9169152e1c5SLionel Sambuc
9179152e1c5SLionel Sambuc } else {
9189152e1c5SLionel Sambuc wrtwarning("pointer to wrong page.\n");
9199152e1c5SLionel Sambuc return NULL;
9209152e1c5SLionel Sambuc }
9219152e1c5SLionel Sambuc
9229152e1c5SLionel Sambuc p = imalloc(size);
9239152e1c5SLionel Sambuc
9249152e1c5SLionel Sambuc if (p != NULL) {
9259152e1c5SLionel Sambuc /* copy the lesser of the two sizes, and free the old one */
9269152e1c5SLionel Sambuc if (!size || !osize)
9279152e1c5SLionel Sambuc ;
9289152e1c5SLionel Sambuc else if (osize < size)
9299152e1c5SLionel Sambuc memcpy(p, ptr, osize);
9309152e1c5SLionel Sambuc else
9319152e1c5SLionel Sambuc memcpy(p, ptr, size);
9329152e1c5SLionel Sambuc ifree(ptr);
9339152e1c5SLionel Sambuc }
9349152e1c5SLionel Sambuc return p;
9359152e1c5SLionel Sambuc }
9369152e1c5SLionel Sambuc
9379152e1c5SLionel Sambuc /*
9389152e1c5SLionel Sambuc * Free a sequence of pages
9399152e1c5SLionel Sambuc */
9409152e1c5SLionel Sambuc
9419152e1c5SLionel Sambuc static inline void
free_pages(void * ptr,size_t idx,struct pginfo * info)9429152e1c5SLionel Sambuc free_pages(void *ptr, size_t idx, struct pginfo *info)
9439152e1c5SLionel Sambuc {
9449152e1c5SLionel Sambuc size_t i;
9459152e1c5SLionel Sambuc struct pgfree *pf, *pt=NULL;
9469152e1c5SLionel Sambuc size_t l;
9479152e1c5SLionel Sambuc void *tail;
9489152e1c5SLionel Sambuc
9499152e1c5SLionel Sambuc if (info == MALLOC_FREE) {
9509152e1c5SLionel Sambuc wrtwarning("page is already free.\n");
9519152e1c5SLionel Sambuc return;
9529152e1c5SLionel Sambuc }
9539152e1c5SLionel Sambuc
9549152e1c5SLionel Sambuc if (info != MALLOC_FIRST) {
9559152e1c5SLionel Sambuc wrtwarning("pointer to wrong page.\n");
9569152e1c5SLionel Sambuc return;
9579152e1c5SLionel Sambuc }
9589152e1c5SLionel Sambuc
9599152e1c5SLionel Sambuc if ((size_t)(uintptr_t)ptr & malloc_pagemask) {
9609152e1c5SLionel Sambuc wrtwarning("modified (page-) pointer.\n");
9619152e1c5SLionel Sambuc return;
9629152e1c5SLionel Sambuc }
9639152e1c5SLionel Sambuc
9649152e1c5SLionel Sambuc /* Count how many pages and mark them free at the same time */
9659152e1c5SLionel Sambuc page_dir[idx] = MALLOC_FREE;
9669152e1c5SLionel Sambuc for (i = 1; page_dir[idx+i] == MALLOC_FOLLOW; i++)
9679152e1c5SLionel Sambuc page_dir[idx + i] = MALLOC_FREE;
9689152e1c5SLionel Sambuc
9699152e1c5SLionel Sambuc l = i << malloc_pageshift;
9709152e1c5SLionel Sambuc
9719152e1c5SLionel Sambuc if (malloc_junk)
9729152e1c5SLionel Sambuc memset(ptr, SOME_JUNK, l);
9739152e1c5SLionel Sambuc
97484d9c625SLionel Sambuc #if !defined(__minix)
9759152e1c5SLionel Sambuc if (malloc_hint)
9769152e1c5SLionel Sambuc madvise(ptr, l, MADV_FREE);
97784d9c625SLionel Sambuc #endif /* !defined(__minix) */
9789152e1c5SLionel Sambuc
9799152e1c5SLionel Sambuc tail = (char *)ptr+l;
9809152e1c5SLionel Sambuc
9819152e1c5SLionel Sambuc /* add to free-list */
9829152e1c5SLionel Sambuc if (px == NULL)
9839152e1c5SLionel Sambuc px = imalloc(sizeof *px); /* This cannot fail... */
9849152e1c5SLionel Sambuc px->page = ptr;
9859152e1c5SLionel Sambuc px->end = tail;
9869152e1c5SLionel Sambuc px->size = l;
9879152e1c5SLionel Sambuc if (free_list.next == NULL) {
9889152e1c5SLionel Sambuc
9899152e1c5SLionel Sambuc /* Nothing on free list, put this at head */
9909152e1c5SLionel Sambuc px->next = free_list.next;
9919152e1c5SLionel Sambuc px->prev = &free_list;
9929152e1c5SLionel Sambuc free_list.next = px;
9939152e1c5SLionel Sambuc pf = px;
9949152e1c5SLionel Sambuc px = NULL;
9959152e1c5SLionel Sambuc
9969152e1c5SLionel Sambuc } else {
9979152e1c5SLionel Sambuc
9989152e1c5SLionel Sambuc /* Find the right spot, leave pf pointing to the modified entry. */
9999152e1c5SLionel Sambuc tail = (char *)ptr+l;
10009152e1c5SLionel Sambuc
10019152e1c5SLionel Sambuc for(pf = free_list.next; pf->end < ptr && pf->next != NULL;
10029152e1c5SLionel Sambuc pf = pf->next)
10039152e1c5SLionel Sambuc ; /* Race ahead here */
10049152e1c5SLionel Sambuc
10059152e1c5SLionel Sambuc if (pf->page > tail) {
10069152e1c5SLionel Sambuc /* Insert before entry */
10079152e1c5SLionel Sambuc px->next = pf;
10089152e1c5SLionel Sambuc px->prev = pf->prev;
10099152e1c5SLionel Sambuc pf->prev = px;
10109152e1c5SLionel Sambuc px->prev->next = px;
10119152e1c5SLionel Sambuc pf = px;
10129152e1c5SLionel Sambuc px = NULL;
10139152e1c5SLionel Sambuc } else if (pf->end == ptr ) {
10149152e1c5SLionel Sambuc /* Append to the previous entry */
10159152e1c5SLionel Sambuc pf->end = (char *)pf->end + l;
10169152e1c5SLionel Sambuc pf->size += l;
10179152e1c5SLionel Sambuc if (pf->next != NULL && pf->end == pf->next->page ) {
10189152e1c5SLionel Sambuc /* And collapse the next too. */
10199152e1c5SLionel Sambuc pt = pf->next;
10209152e1c5SLionel Sambuc pf->end = pt->end;
10219152e1c5SLionel Sambuc pf->size += pt->size;
10229152e1c5SLionel Sambuc pf->next = pt->next;
10239152e1c5SLionel Sambuc if (pf->next != NULL)
10249152e1c5SLionel Sambuc pf->next->prev = pf;
10259152e1c5SLionel Sambuc }
10269152e1c5SLionel Sambuc } else if (pf->page == tail) {
10279152e1c5SLionel Sambuc /* Prepend to entry */
10289152e1c5SLionel Sambuc pf->size += l;
10299152e1c5SLionel Sambuc pf->page = ptr;
10309152e1c5SLionel Sambuc } else if (pf->next == NULL) {
10319152e1c5SLionel Sambuc /* Append at tail of chain */
10329152e1c5SLionel Sambuc px->next = NULL;
10339152e1c5SLionel Sambuc px->prev = pf;
10349152e1c5SLionel Sambuc pf->next = px;
10359152e1c5SLionel Sambuc pf = px;
10369152e1c5SLionel Sambuc px = NULL;
10379152e1c5SLionel Sambuc } else {
10389152e1c5SLionel Sambuc wrterror("freelist is destroyed.\n");
10399152e1c5SLionel Sambuc }
10409152e1c5SLionel Sambuc }
10419152e1c5SLionel Sambuc
10429152e1c5SLionel Sambuc /* Return something to OS ? */
10439152e1c5SLionel Sambuc if (pf->next == NULL && /* If we're the last one, */
10449152e1c5SLionel Sambuc pf->size > malloc_cache && /* ..and the cache is full, */
10459152e1c5SLionel Sambuc pf->end == malloc_brk && /* ..and none behind us, */
10469152e1c5SLionel Sambuc malloc_brk == sbrk((intptr_t)0)) { /* ..and it's OK to do... */
1047f14fb602SLionel Sambuc
10489152e1c5SLionel Sambuc /*
10499152e1c5SLionel Sambuc * Keep the cache intact. Notice that the '>' above guarantees that
10509152e1c5SLionel Sambuc * the pf will always have at least one page afterwards.
10519152e1c5SLionel Sambuc */
10529152e1c5SLionel Sambuc pf->end = (char *)pf->page + malloc_cache;
10539152e1c5SLionel Sambuc pf->size = malloc_cache;
10549152e1c5SLionel Sambuc
1055f14fb602SLionel Sambuc brk(pf->end);
10569152e1c5SLionel Sambuc malloc_brk = pf->end;
10579152e1c5SLionel Sambuc
10589152e1c5SLionel Sambuc idx = ptr2idx(pf->end);
10599152e1c5SLionel Sambuc
10609152e1c5SLionel Sambuc for(i=idx;i <= last_idx;)
10619152e1c5SLionel Sambuc page_dir[i++] = MALLOC_NOT_MINE;
10629152e1c5SLionel Sambuc
10639152e1c5SLionel Sambuc last_idx = idx - 1;
10649152e1c5SLionel Sambuc
10659152e1c5SLionel Sambuc /* XXX: We could realloc/shrink the pagedir here I guess. */
10669152e1c5SLionel Sambuc }
10679152e1c5SLionel Sambuc if (pt != NULL)
10689152e1c5SLionel Sambuc ifree(pt);
10699152e1c5SLionel Sambuc }
10709152e1c5SLionel Sambuc
10719152e1c5SLionel Sambuc /*
10729152e1c5SLionel Sambuc * Free a chunk, and possibly the page it's on, if the page becomes empty.
10739152e1c5SLionel Sambuc */
10749152e1c5SLionel Sambuc
10759152e1c5SLionel Sambuc static inline void
free_bytes(void * ptr,size_t idx,struct pginfo * info)10769152e1c5SLionel Sambuc free_bytes(void *ptr, size_t idx, struct pginfo *info)
10779152e1c5SLionel Sambuc {
10789152e1c5SLionel Sambuc size_t i;
10799152e1c5SLionel Sambuc struct pginfo **mp;
10809152e1c5SLionel Sambuc void *vp;
10819152e1c5SLionel Sambuc
10829152e1c5SLionel Sambuc /* Find the chunk number on the page */
10839152e1c5SLionel Sambuc i = ((size_t)(uintptr_t)ptr & malloc_pagemask) >> info->shift;
10849152e1c5SLionel Sambuc
10859152e1c5SLionel Sambuc if (((size_t)(uintptr_t)ptr & (info->size-1))) {
10869152e1c5SLionel Sambuc wrtwarning("modified (chunk-) pointer.\n");
10879152e1c5SLionel Sambuc return;
10889152e1c5SLionel Sambuc }
10899152e1c5SLionel Sambuc
10909152e1c5SLionel Sambuc if (info->bits[i/MALLOC_BITS] & (1UL << (i % MALLOC_BITS))) {
10919152e1c5SLionel Sambuc wrtwarning("chunk is already free.\n");
10929152e1c5SLionel Sambuc return;
10939152e1c5SLionel Sambuc }
10949152e1c5SLionel Sambuc
10959152e1c5SLionel Sambuc if (malloc_junk)
10969152e1c5SLionel Sambuc memset(ptr, SOME_JUNK, (size_t)info->size);
10979152e1c5SLionel Sambuc
10989152e1c5SLionel Sambuc info->bits[i/MALLOC_BITS] |= (u_int)(1UL << (i % MALLOC_BITS));
10999152e1c5SLionel Sambuc info->free++;
11009152e1c5SLionel Sambuc
11019152e1c5SLionel Sambuc mp = page_dir + info->shift;
11029152e1c5SLionel Sambuc
11039152e1c5SLionel Sambuc if (info->free == 1) {
11049152e1c5SLionel Sambuc
11059152e1c5SLionel Sambuc /* Page became non-full */
11069152e1c5SLionel Sambuc
11079152e1c5SLionel Sambuc mp = page_dir + info->shift;
11089152e1c5SLionel Sambuc /* Insert in address order */
11099152e1c5SLionel Sambuc while (*mp && (*mp)->next && (*mp)->next->page < info->page)
11109152e1c5SLionel Sambuc mp = &(*mp)->next;
11119152e1c5SLionel Sambuc info->next = *mp;
11129152e1c5SLionel Sambuc *mp = info;
11139152e1c5SLionel Sambuc return;
11149152e1c5SLionel Sambuc }
11159152e1c5SLionel Sambuc
11169152e1c5SLionel Sambuc if (info->free != info->total)
11179152e1c5SLionel Sambuc return;
11189152e1c5SLionel Sambuc
11199152e1c5SLionel Sambuc /* Find & remove this page in the queue */
11209152e1c5SLionel Sambuc while (*mp != info) {
11219152e1c5SLionel Sambuc mp = &((*mp)->next);
11229152e1c5SLionel Sambuc #ifdef MALLOC_EXTRA_SANITY
11239152e1c5SLionel Sambuc if (!*mp)
11249152e1c5SLionel Sambuc wrterror("(ES): Not on queue.\n");
11259152e1c5SLionel Sambuc #endif /* MALLOC_EXTRA_SANITY */
11269152e1c5SLionel Sambuc }
11279152e1c5SLionel Sambuc *mp = info->next;
11289152e1c5SLionel Sambuc
11299152e1c5SLionel Sambuc /* Free the page & the info structure if need be */
11309152e1c5SLionel Sambuc page_dir[idx] = MALLOC_FIRST;
11319152e1c5SLionel Sambuc vp = info->page; /* Order is important ! */
11329152e1c5SLionel Sambuc if(vp != (void*)info)
11339152e1c5SLionel Sambuc ifree(info);
11349152e1c5SLionel Sambuc ifree(vp);
11359152e1c5SLionel Sambuc }
11369152e1c5SLionel Sambuc
11379152e1c5SLionel Sambuc static void
ifree(void * ptr)11389152e1c5SLionel Sambuc ifree(void *ptr)
11399152e1c5SLionel Sambuc {
11409152e1c5SLionel Sambuc struct pginfo *info;
11419152e1c5SLionel Sambuc size_t idx;
11429152e1c5SLionel Sambuc
11439152e1c5SLionel Sambuc /* This is legal */
11449152e1c5SLionel Sambuc if (ptr == NULL)
11459152e1c5SLionel Sambuc return;
11469152e1c5SLionel Sambuc
11479152e1c5SLionel Sambuc /* If we're already sinking, don't make matters any worse. */
11489152e1c5SLionel Sambuc if (suicide)
11499152e1c5SLionel Sambuc return;
11509152e1c5SLionel Sambuc
11519152e1c5SLionel Sambuc idx = ptr2idx(ptr);
11529152e1c5SLionel Sambuc
11539152e1c5SLionel Sambuc if (idx < malloc_pageshift) {
11549152e1c5SLionel Sambuc wrtwarning("junk pointer, too low to make sense.\n");
11559152e1c5SLionel Sambuc return;
11569152e1c5SLionel Sambuc }
11579152e1c5SLionel Sambuc
11589152e1c5SLionel Sambuc if (idx > last_idx) {
11599152e1c5SLionel Sambuc wrtwarning("junk pointer, too high to make sense.\n");
11609152e1c5SLionel Sambuc return;
11619152e1c5SLionel Sambuc }
11629152e1c5SLionel Sambuc
11639152e1c5SLionel Sambuc info = page_dir[idx];
11649152e1c5SLionel Sambuc
11659152e1c5SLionel Sambuc if (info < MALLOC_MAGIC)
11669152e1c5SLionel Sambuc free_pages(ptr, idx, info);
11679152e1c5SLionel Sambuc else
11689152e1c5SLionel Sambuc free_bytes(ptr, idx, info);
11699152e1c5SLionel Sambuc return;
11709152e1c5SLionel Sambuc }
11719152e1c5SLionel Sambuc
117284d9c625SLionel Sambuc static int malloc_active; /* Recursion flag for public interface. */
11739152e1c5SLionel Sambuc static unsigned malloc_started; /* Set when initialization has been done */
11749152e1c5SLionel Sambuc
11759152e1c5SLionel Sambuc static void *
pubrealloc(void * ptr,size_t size,const char * func)11769152e1c5SLionel Sambuc pubrealloc(void *ptr, size_t size, const char *func)
11779152e1c5SLionel Sambuc {
11789152e1c5SLionel Sambuc void *r;
11799152e1c5SLionel Sambuc int err = 0;
11809152e1c5SLionel Sambuc
11819152e1c5SLionel Sambuc /*
11829152e1c5SLionel Sambuc * If a thread is inside our code with a functional lock held, and then
11839152e1c5SLionel Sambuc * catches a signal which calls us again, we would get a deadlock if the
11849152e1c5SLionel Sambuc * lock is not of a recursive type.
11859152e1c5SLionel Sambuc */
11869152e1c5SLionel Sambuc _MALLOC_LOCK();
11879152e1c5SLionel Sambuc malloc_func = func;
11889152e1c5SLionel Sambuc if (malloc_active > 0) {
11899152e1c5SLionel Sambuc if (malloc_active == 1) {
11909152e1c5SLionel Sambuc wrtwarning("recursive call\n");
11919152e1c5SLionel Sambuc malloc_active = 2;
11929152e1c5SLionel Sambuc }
11939152e1c5SLionel Sambuc _MALLOC_UNLOCK();
11949152e1c5SLionel Sambuc errno = EINVAL;
11959152e1c5SLionel Sambuc return (NULL);
11969152e1c5SLionel Sambuc }
11979152e1c5SLionel Sambuc malloc_active = 1;
11989152e1c5SLionel Sambuc
11999152e1c5SLionel Sambuc if (!malloc_started) {
12009152e1c5SLionel Sambuc if (ptr != NULL) {
12019152e1c5SLionel Sambuc wrtwarning("malloc() has never been called\n");
12029152e1c5SLionel Sambuc malloc_active = 0;
12039152e1c5SLionel Sambuc _MALLOC_UNLOCK();
12049152e1c5SLionel Sambuc errno = EINVAL;
12059152e1c5SLionel Sambuc return (NULL);
12069152e1c5SLionel Sambuc }
12079152e1c5SLionel Sambuc malloc_init();
12089152e1c5SLionel Sambuc malloc_started = 1;
12099152e1c5SLionel Sambuc }
12109152e1c5SLionel Sambuc
12119152e1c5SLionel Sambuc if (ptr == ZEROSIZEPTR)
12129152e1c5SLionel Sambuc ptr = NULL;
12139152e1c5SLionel Sambuc if (malloc_sysv && !size) {
12149152e1c5SLionel Sambuc if (ptr != NULL)
12159152e1c5SLionel Sambuc ifree(ptr);
12169152e1c5SLionel Sambuc r = NULL;
12179152e1c5SLionel Sambuc } else if (!size) {
12189152e1c5SLionel Sambuc if (ptr != NULL)
12199152e1c5SLionel Sambuc ifree(ptr);
12209152e1c5SLionel Sambuc r = ZEROSIZEPTR;
12219152e1c5SLionel Sambuc } else if (ptr == NULL) {
12229152e1c5SLionel Sambuc r = imalloc(size);
12239152e1c5SLionel Sambuc err = (r == NULL);
12249152e1c5SLionel Sambuc } else {
12259152e1c5SLionel Sambuc r = irealloc(ptr, size);
12269152e1c5SLionel Sambuc err = (r == NULL);
12279152e1c5SLionel Sambuc }
12289152e1c5SLionel Sambuc UTRACE(ptr, size, r);
12299152e1c5SLionel Sambuc malloc_active = 0;
12309152e1c5SLionel Sambuc _MALLOC_UNLOCK();
12319152e1c5SLionel Sambuc if (malloc_xmalloc && err)
12329152e1c5SLionel Sambuc wrterror("out of memory\n");
12339152e1c5SLionel Sambuc if (err)
12349152e1c5SLionel Sambuc errno = ENOMEM;
12359152e1c5SLionel Sambuc return (r);
12369152e1c5SLionel Sambuc }
12379152e1c5SLionel Sambuc
12389152e1c5SLionel Sambuc /*
12399152e1c5SLionel Sambuc * These are the public exported interface routines.
12409152e1c5SLionel Sambuc */
12419152e1c5SLionel Sambuc
12429152e1c5SLionel Sambuc void *
malloc(size_t size)12439152e1c5SLionel Sambuc malloc(size_t size)
12449152e1c5SLionel Sambuc {
12459152e1c5SLionel Sambuc
12469152e1c5SLionel Sambuc return pubrealloc(NULL, size, " in malloc():");
12479152e1c5SLionel Sambuc }
12489152e1c5SLionel Sambuc
12499152e1c5SLionel Sambuc int
posix_memalign(void ** memptr,size_t alignment,size_t size)12509152e1c5SLionel Sambuc posix_memalign(void **memptr, size_t alignment, size_t size)
12519152e1c5SLionel Sambuc {
12529152e1c5SLionel Sambuc int err;
12539152e1c5SLionel Sambuc void *result;
12549152e1c5SLionel Sambuc
12559152e1c5SLionel Sambuc if (!malloc_started) {
12569152e1c5SLionel Sambuc malloc_init();
12579152e1c5SLionel Sambuc malloc_started = 1;
12589152e1c5SLionel Sambuc }
12599152e1c5SLionel Sambuc /* Make sure that alignment is a large enough power of 2. */
12609152e1c5SLionel Sambuc if (((alignment - 1) & alignment) != 0 || alignment < sizeof(void *) ||
12619152e1c5SLionel Sambuc alignment > malloc_pagesize)
12629152e1c5SLionel Sambuc return EINVAL;
12639152e1c5SLionel Sambuc
12649152e1c5SLionel Sambuc /*
12659152e1c5SLionel Sambuc * (size | alignment) is enough to assure the requested alignment, since
12669152e1c5SLionel Sambuc * the allocator always allocates power-of-two blocks.
12679152e1c5SLionel Sambuc */
12689152e1c5SLionel Sambuc err = errno; /* Protect errno against changes in pubrealloc(). */
12699152e1c5SLionel Sambuc result = pubrealloc(NULL, (size | alignment), " in posix_memalign()");
12709152e1c5SLionel Sambuc errno = err;
12719152e1c5SLionel Sambuc
12729152e1c5SLionel Sambuc if (result == NULL)
12739152e1c5SLionel Sambuc return ENOMEM;
12749152e1c5SLionel Sambuc
12759152e1c5SLionel Sambuc *memptr = result;
12769152e1c5SLionel Sambuc return 0;
12779152e1c5SLionel Sambuc }
12789152e1c5SLionel Sambuc
12799152e1c5SLionel Sambuc void *
calloc(size_t num,size_t size)12809152e1c5SLionel Sambuc calloc(size_t num, size_t size)
12819152e1c5SLionel Sambuc {
12829152e1c5SLionel Sambuc void *ret;
12839152e1c5SLionel Sambuc
12849152e1c5SLionel Sambuc if (size != 0 && (num * size) / size != num) {
12859152e1c5SLionel Sambuc /* size_t overflow. */
12869152e1c5SLionel Sambuc errno = ENOMEM;
12879152e1c5SLionel Sambuc return (NULL);
12889152e1c5SLionel Sambuc }
12899152e1c5SLionel Sambuc
12909152e1c5SLionel Sambuc ret = pubrealloc(NULL, num * size, " in calloc():");
12919152e1c5SLionel Sambuc
12929152e1c5SLionel Sambuc if (ret != NULL)
12939152e1c5SLionel Sambuc memset(ret, 0, num * size);
12949152e1c5SLionel Sambuc
12959152e1c5SLionel Sambuc return ret;
12969152e1c5SLionel Sambuc }
12979152e1c5SLionel Sambuc
12989152e1c5SLionel Sambuc void
free(void * ptr)12999152e1c5SLionel Sambuc free(void *ptr)
13009152e1c5SLionel Sambuc {
13019152e1c5SLionel Sambuc
13029152e1c5SLionel Sambuc pubrealloc(ptr, 0, " in free():");
13039152e1c5SLionel Sambuc }
13049152e1c5SLionel Sambuc
13059152e1c5SLionel Sambuc void *
realloc(void * ptr,size_t size)13069152e1c5SLionel Sambuc realloc(void *ptr, size_t size)
13079152e1c5SLionel Sambuc {
13089152e1c5SLionel Sambuc
13099152e1c5SLionel Sambuc return pubrealloc(ptr, size, " in realloc():");
13109152e1c5SLionel Sambuc }
13119152e1c5SLionel Sambuc
13129152e1c5SLionel Sambuc /*
13139152e1c5SLionel Sambuc * Begin library-private functions, used by threading libraries for protection
13149152e1c5SLionel Sambuc * of malloc during fork(). These functions are only called if the program is
13159152e1c5SLionel Sambuc * running in threaded mode, so there is no need to check whether the program
13169152e1c5SLionel Sambuc * is threaded here.
13179152e1c5SLionel Sambuc */
13189152e1c5SLionel Sambuc
13199152e1c5SLionel Sambuc void
_malloc_prefork(void)13209152e1c5SLionel Sambuc _malloc_prefork(void)
13219152e1c5SLionel Sambuc {
13229152e1c5SLionel Sambuc
13239152e1c5SLionel Sambuc _MALLOC_LOCK();
13249152e1c5SLionel Sambuc }
13259152e1c5SLionel Sambuc
13269152e1c5SLionel Sambuc void
_malloc_postfork(void)13279152e1c5SLionel Sambuc _malloc_postfork(void)
13289152e1c5SLionel Sambuc {
13299152e1c5SLionel Sambuc
13309152e1c5SLionel Sambuc _MALLOC_UNLOCK();
13319152e1c5SLionel Sambuc }
1332