xref: /minix3/lib/libc/stdlib/malloc.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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