@ -1,4 +1,4 @@
/* $OpenBSD: malloc.c,v 1.111 2008/12/15 19:47:49 otto Exp $ */
/* $OpenBSD: malloc.c,v 1.112 2008/12/29 22:25:50 djm Exp $ */
/*
/*
* Copyright ( c ) 2008 Otto Moerbeek < otto @ drijf . net >
* Copyright ( c ) 2008 Otto Moerbeek < otto @ drijf . net >
*
*
@ -88,6 +88,23 @@
# define MMAPA(a,sz) mmap((a), (size_t)(sz), PROT_READ | PROT_WRITE, \
# define MMAPA(a,sz) mmap((a), (size_t)(sz), PROT_READ | PROT_WRITE, \
MAP_ANON | MAP_PRIVATE , - 1 , ( off_t ) 0 )
MAP_ANON | MAP_PRIVATE , - 1 , ( off_t ) 0 )
/* Protect and unprotect g_pool structure as we enter/exit the allocator */
# define DIR_INFO_RSZ ((sizeof(struct dir_info) + PAGE_MASK) & ~PAGE_MASK)
# define PROTECT_G_POOL() \
do { \
if ( g_pool ! = NULL & & mopts . malloc_poolprot ) { \
mprotect ( ( void * ) ( ( uintptr_t ) g_pool & ~ PAGE_MASK ) , \
DIR_INFO_RSZ , PROT_NONE ) ; \
} \
} while ( 0 )
# define UNPROTECT_G_POOL() \
do { \
if ( g_pool ! = NULL & & mopts . malloc_poolprot ) { \
mprotect ( ( void * ) ( ( uintptr_t ) g_pool & ~ PAGE_MASK ) , \
DIR_INFO_RSZ , PROT_READ | PROT_WRITE ) ; \
} \
} while ( 0 )
struct region_info {
struct region_info {
void * p ; /* page; low bits used to mark chunks */
void * p ; /* page; low bits used to mark chunks */
uintptr_t size ; /* size for pages, or chunk_info pointer */
uintptr_t size ; /* size for pages, or chunk_info pointer */
@ -145,29 +162,41 @@ struct chunk_info {
u_long bits [ ( MALLOC_PAGESIZE / MALLOC_MINSIZE ) / MALLOC_BITS ] ;
u_long bits [ ( MALLOC_PAGESIZE / MALLOC_MINSIZE ) / MALLOC_BITS ] ;
} ;
} ;
static struct dir_info g_pool ;
static char * malloc_func ; /* current function */
struct malloc_readonly {
struct dir_info * g_pool ; /* Main bookkeeping information */
int malloc_abort ; /* abort() on error */
int malloc_poolprot ; /* mprotect heap PROT_NONE? */
int malloc_freeprot ; /* mprotect free pages PROT_NONE? */
int malloc_hint ; /* call madvice on free pages? */
int malloc_junk ; /* junk fill? */
int malloc_move ; /* move allocations to end of page? */
int malloc_realloc ; /* always realloc? */
int malloc_xmalloc ; /* xmalloc behaviour? */
int malloc_zero ; /* zero fill? */
size_t malloc_guard ; /* use guard pages after allocations? */
u_int malloc_cache ; /* free pages we cache */
# ifdef MALLOC_STATS
int malloc_stats ; /* dump statistics at end */
# endif
u_int32_t malloc_canary ; /* Matched against ones in g_pool */
} ;
/* This object is mapped PROT_READ after initialisation to prevent tampering */
static union {
struct malloc_readonly mopts ;
u_char _pad [ PAGE_SIZE ] ;
} malloc_readonly __attribute__ ( ( aligned ( PAGE_SIZE ) ) ) ;
# define mopts malloc_readonly.mopts
# define g_pool mopts.g_pool
char * malloc_options ; /* compile-time options */
char * malloc_options ; /* compile-time options */
static int malloc_abort = 1 ; /* abort() on error */
static char * malloc_func ; /* current function */
static int malloc_active ; /* status of malloc */
static int malloc_active ; /* status of malloc */
static int malloc_freeprot ; /* mprotect free pages PROT_NONE? */
static int malloc_hint ; /* call madvice on free pages? */
static int malloc_junk ; /* junk fill? */
static int malloc_move = 1 ; /* move allocations to end of page? */
static int malloc_realloc ; /* always realloc? */
static int malloc_xmalloc ; /* xmalloc behaviour? */
static int malloc_zero ; /* zero fill? */
static size_t malloc_guard ; /* use guard pages after allocations? */
static u_int malloc_cache = 64 ; /* free pages we cache */
static size_t malloc_guarded ; /* bytes used for guards */
static size_t malloc_guarded ; /* bytes used for guards */
static size_t malloc_used ; /* bytes allocated */
static size_t malloc_used ; /* bytes allocated */
# ifdef MALLOC_STATS
static int malloc_stats ; /* dump statistics at end */
# endif
static size_t rbytesused ; /* random bytes used */
static size_t rbytesused ; /* random bytes used */
static u_char rbytes [ 512 ] ; /* random bytes */
static u_char rbytes [ 512 ] ; /* random bytes */
static u_char getrbyte ( void ) ;
static u_char getrbyte ( void ) ;
@ -247,7 +276,7 @@ dump_free_page_info(int fd, struct dir_info *d)
snprintf ( buf , sizeof ( buf ) , " Free pages cached: %zu \n " ,
snprintf ( buf , sizeof ( buf ) , " Free pages cached: %zu \n " ,
d - > free_regions_size ) ;
d - > free_regions_size ) ;
write ( fd , buf , strlen ( buf ) ) ;
write ( fd , buf , strlen ( buf ) ) ;
for ( i = 0 ; i < malloc_cache ; i + + ) {
for ( i = 0 ; i < mopts . m alloc_cache ; i + + ) {
if ( d - > free_regions [ i ] . p ! = NULL ) {
if ( d - > free_regions [ i ] . p ! = NULL ) {
snprintf ( buf , sizeof ( buf ) , " %2d) " , i ) ;
snprintf ( buf , sizeof ( buf ) , " %2d) " , i ) ;
write ( fd , buf , strlen ( buf ) ) ;
write ( fd , buf , strlen ( buf ) ) ;
@ -266,6 +295,8 @@ malloc_dump1(int fd, struct dir_info *d)
snprintf ( buf , sizeof ( buf ) , " Malloc dir of %s at %p \n " , __progname , d ) ;
snprintf ( buf , sizeof ( buf ) , " Malloc dir of %s at %p \n " , __progname , d ) ;
write ( fd , buf , strlen ( buf ) ) ;
write ( fd , buf , strlen ( buf ) ) ;
if ( d = = NULL )
return ;
snprintf ( buf , sizeof ( buf ) , " Regions slots %zu \n " , d - > regions_total ) ;
snprintf ( buf , sizeof ( buf ) , " Regions slots %zu \n " , d - > regions_total ) ;
write ( fd , buf , strlen ( buf ) ) ;
write ( fd , buf , strlen ( buf ) ) ;
snprintf ( buf , sizeof ( buf ) , " Finds %zu/%zu %f \n " , d - > finds ,
snprintf ( buf , sizeof ( buf ) , " Finds %zu/%zu %f \n " , d - > finds ,
@ -313,7 +344,7 @@ malloc_dump1(int fd, struct dir_info *d)
void
void
malloc_dump ( int fd )
malloc_dump ( int fd )
{
{
malloc_dump1 ( fd , & g_pool ) ;
malloc_dump1 ( fd , g_pool ) ;
}
}
static void
static void
@ -353,11 +384,11 @@ wrterror(char *p)
writev ( STDERR_FILENO , iov , 5 ) ;
writev ( STDERR_FILENO , iov , 5 ) ;
# ifdef MALLOC_STATS
# ifdef MALLOC_STATS
if ( malloc_stats )
if ( mopts . m alloc_stats )
malloc_dump ( STDERR_FILENO ) ;
malloc_dump ( STDERR_FILENO ) ;
# endif /* MALLOC_STATS */
# endif /* MALLOC_STATS */
/ / malloc_active - - ;
/ / malloc_active - - ;
if ( malloc_abort )
if ( mopts . m alloc_abort )
abort ( ) ;
abort ( ) ;
}
}
@ -381,19 +412,19 @@ unmap(struct dir_info *d, void *p, size_t sz)
return ;
return ;
}
}
if ( psz > malloc_cache ) {
if ( psz > mopts . m alloc_cache ) {
if ( munmap ( p , sz ) )
if ( munmap ( p , sz ) )
wrterror ( " munmap " ) ;
wrterror ( " munmap " ) ;
malloc_used - = sz ;
malloc_used - = sz ;
return ;
return ;
}
}
tounmap = 0 ;
tounmap = 0 ;
rsz = malloc_cache - d - > free_regions_size ;
rsz = mopts . m alloc_cache - d - > free_regions_size ;
if ( psz > rsz )
if ( psz > rsz )
tounmap = psz - rsz ;
tounmap = psz - rsz ;
offset = getrbyte ( ) ;
offset = getrbyte ( ) ;
for ( i = 0 ; tounmap > 0 & & i < malloc_cache ; i + + ) {
r = & d - > free_regions [ ( i + offset ) & ( malloc_cache - 1 ) ] ;
for ( i = 0 ; tounmap > 0 & & i < mopts . m alloc_cache ; i + + ) {
r = & d - > free_regions [ ( i + offset ) & ( mopts . m alloc_cache - 1 ) ] ;
if ( r - > p ! = NULL ) {
if ( r - > p ! = NULL ) {
rsz = r - > size < < MALLOC_PAGESHIFT ;
rsz = r - > size < < MALLOC_PAGESHIFT ;
if ( munmap ( r - > p , rsz ) )
if ( munmap ( r - > p , rsz ) )
@ -410,12 +441,12 @@ unmap(struct dir_info *d, void *p, size_t sz)
}
}
if ( tounmap > 0 )
if ( tounmap > 0 )
wrterror ( " malloc cache underflow " ) ;
wrterror ( " malloc cache underflow " ) ;
for ( i = 0 ; i < malloc_cache ; i + + ) {
for ( i = 0 ; i < mopts . m alloc_cache ; i + + ) {
r = & d - > free_regions [ i ] ;
r = & d - > free_regions [ i ] ;
if ( r - > p = = NULL ) {
if ( r - > p = = NULL ) {
if ( malloc_hint )
if ( mopts . m alloc_hint )
madvise ( p , sz , MADV_FREE ) ;
madvise ( p , sz , MADV_FREE ) ;
if ( malloc_freeprot )
if ( mopts . m alloc_freeprot )
mprotect ( p , sz , PROT_NONE ) ;
mprotect ( p , sz , PROT_NONE ) ;
r - > p = p ;
r - > p = p ;
r - > size = psz ;
r - > size = psz ;
@ -423,9 +454,9 @@ unmap(struct dir_info *d, void *p, size_t sz)
break ;
break ;
}
}
}
}
if ( i = = malloc_cache )
if ( i = = mopts . m alloc_cache )
wrterror ( " malloc free slot lost " ) ;
wrterror ( " malloc free slot lost " ) ;
if ( d - > free_regions_size > malloc_cache )
if ( d - > free_regions_size > mopts . m alloc_cache )
wrterror ( " malloc cache overflow " ) ;
wrterror ( " malloc cache overflow " ) ;
}
}
@ -436,7 +467,7 @@ zapcacheregion(struct dir_info *d, void *p)
struct region_info * r ;
struct region_info * r ;
size_t rsz ;
size_t rsz ;
for ( i = 0 ; i < malloc_cache ; i + + ) {
for ( i = 0 ; i < mopts . m alloc_cache ; i + + ) {
r = & d - > free_regions [ i ] ;
r = & d - > free_regions [ i ] ;
if ( r - > p = = p ) {
if ( r - > p = = p ) {
rsz = r - > size < < MALLOC_PAGESHIFT ;
rsz = r - > size < < MALLOC_PAGESHIFT ;
@ -458,6 +489,9 @@ map(struct dir_info *d, size_t sz, int zero_fill)
u_int i , offset ;
u_int i , offset ;
void * p ;
void * p ;
if ( mopts . malloc_canary ! = ( d - > canary1 ^ ( u_int32_t ) d ) | |
d - > canary1 ! = ~ d - > canary2 )
wrterror ( " internal struct corrupt " ) ;
if ( sz ! = PAGEROUND ( sz ) ) {
if ( sz ! = PAGEROUND ( sz ) ) {
wrterror ( " map round " ) ;
wrterror ( " map round " ) ;
return NULL ;
return NULL ;
@ -470,21 +504,22 @@ map(struct dir_info *d, size_t sz, int zero_fill)
return p ;
return p ;
}
}
offset = getrbyte ( ) ;
offset = getrbyte ( ) ;
for ( i = 0 ; i < malloc_cache ; i + + ) {
r = & d - > free_regions [ ( i + offset ) & ( malloc_cache - 1 ) ] ;
for ( i = 0 ; i < mopts . m alloc_cache ; i + + ) {
r = & d - > free_regions [ ( i + offset ) & ( mopts . m alloc_cache - 1 ) ] ;
if ( r - > p ! = NULL ) {
if ( r - > p ! = NULL ) {
if ( r - > size = = psz ) {
if ( r - > size = = psz ) {
p = r - > p ;
p = r - > p ;
if ( malloc_freeprot )
if ( mopts . m alloc_freeprot )
mprotect ( p , sz , PROT_READ | PROT_WRITE ) ;
mprotect ( p , sz , PROT_READ | PROT_WRITE ) ;
if ( malloc_hint )
if ( mopts . m alloc_hint )
madvise ( p , sz , MADV_NORMAL ) ;
madvise ( p , sz , MADV_NORMAL ) ;
r - > p = NULL ;
r - > p = NULL ;
r - > size = 0 ;
r - > size = 0 ;
d - > free_regions_size - = psz ;
d - > free_regions_size - = psz ;
if ( zero_fill )
if ( zero_fill )
memset ( p , 0 , sz ) ;
memset ( p , 0 , sz ) ;
else if ( malloc_junk & & malloc_freeprot )
else if ( mopts . malloc_junk & &
mopts . malloc_freeprot )
memset ( p , SOME_FREEJUNK , sz ) ;
memset ( p , SOME_FREEJUNK , sz ) ;
return p ;
return p ;
} else if ( r - > size > psz )
} else if ( r - > size > psz )
@ -494,9 +529,9 @@ map(struct dir_info *d, size_t sz, int zero_fill)
if ( big ! = NULL ) {
if ( big ! = NULL ) {
r = big ;
r = big ;
p = ( char * ) r - > p + ( ( r - > size - psz ) < < MALLOC_PAGESHIFT ) ;
p = ( char * ) r - > p + ( ( r - > size - psz ) < < MALLOC_PAGESHIFT ) ;
if ( malloc_freeprot )
if ( mopts . m alloc_freeprot )
mprotect ( p , sz , PROT_READ | PROT_WRITE ) ;
mprotect ( p , sz , PROT_READ | PROT_WRITE ) ;
if ( malloc_hint )
if ( mopts . m alloc_hint )
madvise ( p , sz , MADV_NORMAL ) ;
madvise ( p , sz , MADV_NORMAL ) ;
r - > size - = psz ;
r - > size - = psz ;
d - > free_regions_size - = psz ;
d - > free_regions_size - = psz ;
@ -507,7 +542,7 @@ map(struct dir_info *d, size_t sz, int zero_fill)
p = MMAP ( sz ) ;
p = MMAP ( sz ) ;
if ( p ! = MAP_FAILED )
if ( p ! = MAP_FAILED )
malloc_used + = sz ;
malloc_used + = sz ;
if ( d - > free_regions_size > malloc_cache )
if ( d - > free_regions_size > mopts . m alloc_cache )
wrterror ( " malloc cache " ) ;
wrterror ( " malloc cache " ) ;
/* zero fill not needed */
/* zero fill not needed */
return p ;
return p ;
@ -532,14 +567,22 @@ getrbyte(void)
* Initialize a dir_info , which should have been cleared by caller
* Initialize a dir_info , which should have been cleared by caller
*/
*/
static int
static int
omalloc_init ( struct dir_info * d )
omalloc_init ( struct dir_info * * dp )
{
{
char * p , b [ 64 ] ;
char * p , b [ 64 ] ;
int i , j ;
int i , j ;
size_t regioninfo_size ;
size_t d_avail , regioninfo_size ;
struct dir_info * d ;
rbytes_init ( ) ;
rbytes_init ( ) ;
/*
* Default options
*/
mopts . malloc_abort = 1 ;
mopts . malloc_move = 1 ;
mopts . malloc_cache = 64 ;
for ( i = 0 ; i < 3 ; i + + ) {
for ( i = 0 ; i < 3 ; i + + ) {
switch ( i ) {
switch ( i ) {
case 0 :
case 0 :
@ -565,77 +608,83 @@ omalloc_init(struct dir_info *d)
for ( ; p ! = NULL & & * p ! = ' \0 ' ; p + + ) {
for ( ; p ! = NULL & & * p ! = ' \0 ' ; p + + ) {
switch ( * p ) {
switch ( * p ) {
case ' > ' :
case ' > ' :
malloc_cache < < = 1 ;
if ( malloc_cache > MALLOC_MAXCACHE )
malloc_cache = MALLOC_MAXCACHE ;
mopts . m alloc_cache < < = 1 ;
if ( mopts . m alloc_cache > MALLOC_MAXCACHE )
mopts . m alloc_cache = MALLOC_MAXCACHE ;
break ;
break ;
case ' < ' :
case ' < ' :
malloc_cache > > = 1 ;
mopts . m alloc_cache > > = 1 ;
break ;
break ;
case ' a ' :
case ' a ' :
malloc_abort = 0 ;
mopts . m alloc_abort = 0 ;
break ;
break ;
case ' A ' :
case ' A ' :
malloc_abort = 1 ;
mopts . m alloc_abort = 1 ;
break ;
break ;
# ifdef MALLOC_STATS
# ifdef MALLOC_STATS
case ' d ' :
case ' d ' :
malloc_stats = 0 ;
mopts . m alloc_stats = 0 ;
break ;
break ;
case ' D ' :
case ' D ' :
malloc_stats = 1 ;
mopts . m alloc_stats = 1 ;
break ;
break ;
# endif /* MALLOC_STATS */
# endif /* MALLOC_STATS */
case ' f ' :
case ' f ' :
malloc_freeprot = 0 ;
mopts . m alloc_freeprot = 0 ;
break ;
break ;
case ' F ' :
case ' F ' :
malloc_freeprot = 1 ;
mopts . m alloc_freeprot = 1 ;
break ;
break ;
case ' g ' :
case ' g ' :
malloc_guard = 0 ;
mopts . m alloc_guard = 0 ;
break ;
break ;
case ' G ' :
case ' G ' :
malloc_guard = MALLOC_PAGESIZE ;
mopts . m alloc_guard = MALLOC_PAGESIZE ;
break ;
break ;
case ' h ' :
case ' h ' :
malloc_hint = 0 ;
mopts . m alloc_hint = 0 ;
break ;
break ;
case ' H ' :
case ' H ' :
malloc_hint = 1 ;
mopts . m alloc_hint = 1 ;
break ;
break ;
case ' j ' :
case ' j ' :
malloc_junk = 0 ;
mopts . m alloc_junk = 0 ;
break ;
break ;
case ' J ' :
case ' J ' :
malloc_junk = 1 ;
mopts . malloc_junk = 1 ;
break ;
case ' l ' :
mopts . malloc_poolprot = 0 ;
break ;
case ' L ' :
mopts . malloc_poolprot = 1 ;
break ;
break ;
case ' n ' :
case ' n ' :
case ' N ' :
case ' N ' :
break ;
break ;
case ' p ' :
case ' p ' :
malloc_move = 0 ;
mopts . m alloc_move = 0 ;
break ;
break ;
case ' P ' :
case ' P ' :
malloc_move = 1 ;
mopts . m alloc_move = 1 ;
break ;
break ;
case ' r ' :
case ' r ' :
malloc_realloc = 0 ;
mopts . m alloc_realloc = 0 ;
break ;
break ;
case ' R ' :
case ' R ' :
malloc_realloc = 1 ;
mopts . m alloc_realloc = 1 ;
break ;
break ;
case ' x ' :
case ' x ' :
malloc_xmalloc = 0 ;
mopts . m alloc_xmalloc = 0 ;
break ;
break ;
case ' X ' :
case ' X ' :
malloc_xmalloc = 1 ;
mopts . m alloc_xmalloc = 1 ;
break ;
break ;
case ' z ' :
case ' z ' :
malloc_zero = 0 ;
mopts . m alloc_zero = 0 ;
break ;
break ;
case ' Z ' :
case ' Z ' :
malloc_zero = 1 ;
mopts . m alloc_zero = 1 ;
break ;
break ;
default : {
default : {
static const char q [ ] = " malloc() warning: "
static const char q [ ] = " malloc() warning: "
@ -651,17 +700,33 @@ omalloc_init(struct dir_info *d)
* We want junk in the entire allocation , and zero only in the part
* We want junk in the entire allocation , and zero only in the part
* the user asked for .
* the user asked for .
*/
*/
if ( malloc_zero )
malloc_junk = 1 ;
if ( mopts . m alloc_zero )
mopts . m alloc_junk = 1 ;
# ifdef MALLOC_STATS
# ifdef MALLOC_STATS
if ( malloc_stats & & ( atexit ( malloc_exit ) = = - 1 ) ) {
if ( mopts . m alloc_stats & & ( atexit ( malloc_exit ) = = - 1 ) ) {
static const char q [ ] = " malloc() warning: atexit(2) failed. "
static const char q [ ] = " malloc() warning: atexit(2) failed. "
" Will not be able to dump stats on exit \n " ;
" Will not be able to dump stats on exit \n " ;
write ( STDERR_FILENO , q , sizeof ( q ) - 1 ) ;
write ( STDERR_FILENO , q , sizeof ( q ) - 1 ) ;
}
}
# endif /* MALLOC_STATS */
# endif /* MALLOC_STATS */
while ( ( mopts . malloc_canary = arc4random ( ) ) = = 0 )
;
/*
* Allocate dir_info with a guard page on either side . Also
* randomise offset inside the page at which the dir_info
* lies ( subject to alignment by 1 < < MALLOC_MINSHIFT )
*/
if ( ( p = MMAP ( PAGE_SIZE + DIR_INFO_RSZ + PAGE_SIZE ) ) = = NULL )
return - 1 ;
mprotect ( p , PAGE_SIZE , PROT_NONE ) ;
mprotect ( p + PAGE_SIZE + DIR_INFO_RSZ , PAGE_SIZE , PROT_NONE ) ;
d_avail = ( DIR_INFO_RSZ - sizeof ( * d ) ) > > MALLOC_MINSHIFT ;
d = ( struct dir_info * ) ( p + PAGE_SIZE +
( arc4random_uniform ( d_avail ) < < MALLOC_MINSHIFT ) ) ;
d - > regions_bits = 9 ;
d - > regions_bits = 9 ;
d - > regions_free = d - > regions_total = 1 < < d - > regions_bits ;
d - > regions_free = d - > regions_total = 1 < < d - > regions_bits ;
regioninfo_size = d - > regions_total * sizeof ( struct region_info ) ;
regioninfo_size = d - > regions_total * sizeof ( struct region_info ) ;
@ -673,8 +738,18 @@ omalloc_init(struct dir_info *d)
}
}
malloc_used + = regioninfo_size ;
malloc_used + = regioninfo_size ;
memset ( d - > r , 0 , regioninfo_size ) ;
memset ( d - > r , 0 , regioninfo_size ) ;
d - > canary1 = arc4random ( ) ;
d - > canary1 = mopts . malloc_canary ^ ( u_int32_t ) d ;
d - > canary2 = ~ d - > canary1 ;
d - > canary2 = ~ d - > canary1 ;
* dp = d ;
/*
* Options have been set and will never be reset .
* Prevent further tampering with them .
*/
if ( ( ( uintptr_t ) & malloc_readonly & PAGE_MASK ) = = 0 )
mprotect ( & malloc_readonly , sizeof ( malloc_readonly ) , PROT_READ ) ;
return 0 ;
return 0 ;
}
}
@ -792,7 +867,8 @@ find(struct dir_info *d, void *p)
size_t mask = d - > regions_total - 1 ;
size_t mask = d - > regions_total - 1 ;
void * q , * r ;
void * q , * r ;
if ( d - > canary1 ! = ~ d - > canary2 )
if ( mopts . malloc_canary ! = ( d - > canary1 ^ ( u_int32_t ) d ) | |
d - > canary1 ! = ~ d - > canary2 )
wrterror ( " internal struct corrupt " ) ;
wrterror ( " internal struct corrupt " ) ;
p = MASK_POINTER ( p ) ;
p = MASK_POINTER ( p ) ;
index = hash ( p ) & mask ;
index = hash ( p ) & mask ;
@ -818,7 +894,7 @@ delete(struct dir_info *d, struct region_info *ri)
if ( d - > regions_total & ( d - > regions_total - 1 ) )
if ( d - > regions_total & ( d - > regions_total - 1 ) )
wrterror ( " regions_total not 2^x " ) ;
wrterror ( " regions_total not 2^x " ) ;
d - > regions_free + + ;
d - > regions_free + + ;
STATS_INC ( g_pool . deletes ) ;
STATS_INC ( g_pool - > deletes ) ;
i = ri - d - > r ;
i = ri - d - > r ;
for ( ; ; ) {
for ( ; ; ) {
@ -834,7 +910,7 @@ delete(struct dir_info *d, struct region_info *ri)
( j < i & & i < = r ) )
( j < i & & i < = r ) )
continue ;
continue ;
d - > r [ j ] = d - > r [ i ] ;
d - > r [ j ] = d - > r [ i ] ;
STATS_INC ( g_pool . delete_moves ) ;
STATS_INC ( g_pool - > delete_moves ) ;
break ;
break ;
}
}
@ -919,6 +995,9 @@ malloc_bytes(struct dir_info *d, size_t size)
u_long u , * lp ;
u_long u , * lp ;
struct chunk_info * bp ;
struct chunk_info * bp ;
if ( mopts . malloc_canary ! = ( d - > canary1 ^ ( u_int32_t ) d ) | |
d - > canary1 ! = ~ d - > canary2 )
wrterror ( " internal struct corrupt " ) ;
/* Don't bother with anything less than this */
/* Don't bother with anything less than this */
/* unless we have a malloc(0) requests */
/* unless we have a malloc(0) requests */
if ( size ! = 0 & & size < MALLOC_MINSIZE )
if ( size ! = 0 & & size < MALLOC_MINSIZE )
@ -983,7 +1062,7 @@ malloc_bytes(struct dir_info *d, size_t size)
k + = ( lp - bp - > bits ) * MALLOC_BITS ;
k + = ( lp - bp - > bits ) * MALLOC_BITS ;
k < < = bp - > shift ;
k < < = bp - > shift ;
if ( malloc_junk & & bp - > size > 0 )
if ( mopts . m alloc_junk & & bp - > size > 0 )
memset ( ( char * ) bp - > page + k , SOME_JUNK , bp - > size ) ;
memset ( ( char * ) bp - > page + k , SOME_JUNK , bp - > size ) ;
return ( ( char * ) bp - > page + k ) ;
return ( ( char * ) bp - > page + k ) ;
}
}
@ -1047,7 +1126,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr)
}
}
* mp = info - > next ;
* mp = info - > next ;
if ( info - > size = = 0 & & ! malloc_freeprot )
if ( info - > size = = 0 & & ! mopts . m alloc_freeprot )
mprotect ( info - > page , MALLOC_PAGESIZE , PROT_READ | PROT_WRITE ) ;
mprotect ( info - > page , MALLOC_PAGESIZE , PROT_READ | PROT_WRITE ) ;
unmap ( d , info - > page , MALLOC_PAGESIZE ) ;
unmap ( d , info - > page , MALLOC_PAGESIZE ) ;
@ -1064,54 +1143,55 @@ omalloc(size_t sz, int zero_fill)
size_t psz ;
size_t psz ;
if ( sz > MALLOC_MAXCHUNK ) {
if ( sz > MALLOC_MAXCHUNK ) {
if ( sz > = SIZE_MAX - malloc_guard - MALLOC_PAGESIZE ) {
if ( sz > = SIZE_MAX - mopts . m alloc_guard - MALLOC_PAGESIZE ) {
errno = ENOMEM ;
errno = ENOMEM ;
return NULL ;
return NULL ;
}
}
sz + = malloc_guard ;
sz + = mopts . m alloc_guard ;
psz = PAGEROUND ( sz ) ;
psz = PAGEROUND ( sz ) ;
p = map ( & g_pool , psz , zero_fill ) ;
p = map ( g_pool , psz , zero_fill ) ;
if ( p = = MAP_FAILED ) {
if ( p = = MAP_FAILED ) {
errno = ENOMEM ;
errno = ENOMEM ;
return NULL ;
return NULL ;
}
}
if ( insert ( & g_pool , p , sz ) ) {
unmap ( & g_pool , p , psz ) ;
if ( insert ( g_pool , p , sz ) ) {
unmap ( g_pool , p , psz ) ;
errno = ENOMEM ;
errno = ENOMEM ;
return NULL ;
return NULL ;
}
}
if ( malloc_guard ) {
if ( mprotect ( ( char * ) p + psz - malloc_guard ,
malloc_guard , PROT_NONE ) )
if ( mopts . m alloc_guard ) {
if ( mprotect ( ( char * ) p + psz - mopts . m alloc_guard ,
mopts . m alloc_guard , PROT_NONE ) )
wrterror ( " mprotect " ) ;
wrterror ( " mprotect " ) ;
malloc_guarded + = malloc_guard ;
malloc_guarded + = mopts . m alloc_guard ;
}
}
if ( malloc_move & &
sz - malloc_guard < MALLOC_PAGESIZE - MALLOC_LEEWAY ) {
if ( mopts . malloc_move & &
sz - mopts . malloc_guard < MALLOC_PAGESIZE -
MALLOC_LEEWAY ) {
/* fill whole allocation */
/* fill whole allocation */
if ( malloc_junk )
memset ( p , SOME_JUNK , psz - malloc_guard ) ;
if ( mopts . m alloc_junk )
memset ( p , SOME_JUNK , psz - mopts . m alloc_guard ) ;
/* shift towards the end */
/* shift towards the end */
p = ( ( char * ) p ) + ( ( MALLOC_PAGESIZE - MALLOC_LEEWAY -
p = ( ( char * ) p ) + ( ( MALLOC_PAGESIZE - MALLOC_LEEWAY -
( sz - malloc_guard ) ) & ~ ( MALLOC_MINSIZE - 1 ) ) ;
( sz - mopts . m alloc_guard ) ) & ~ ( MALLOC_MINSIZE - 1 ) ) ;
/* fill zeros if needed and overwritten above */
/* fill zeros if needed and overwritten above */
if ( zero_fill & & malloc_junk )
memset ( p , 0 , sz - malloc_guard ) ;
if ( zero_fill & & mopts . m alloc_junk )
memset ( p , 0 , sz - mopts . m alloc_guard ) ;
} else {
} else {
if ( malloc_junk ) {
if ( mopts . m alloc_junk ) {
if ( zero_fill )
if ( zero_fill )
memset ( p + sz - malloc_guard ,
memset ( p + sz - mopts . m alloc_guard ,
SOME_JUNK , psz - sz ) ;
SOME_JUNK , psz - sz ) ;
else
else
memset ( p ,
SOME_JUNK , psz - malloc_guard ) ;
memset ( p , SOME_JUNK ,
psz - mopts . malloc_guard ) ;
}
}
}
}
} else {
} else {
/* takes care of SOME_JUNK */
/* takes care of SOME_JUNK */
p = malloc_bytes ( & g_pool , sz ) ;
p = malloc_bytes ( g_pool , sz ) ;
if ( zero_fill & & p ! = NULL & & sz > 0 )
if ( zero_fill & & p ! = NULL & & sz > 0 )
memset ( p , 0 , sz ) ;
memset ( p , 0 , sz ) ;
}
}
@ -1134,10 +1214,33 @@ malloc_recurse(void)
wrterror ( " recursive call " ) ;
wrterror ( " recursive call " ) ;
}
}
malloc_active - - ;
malloc_active - - ;
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
_MALLOC_UNLOCK ( ) ;
errno = EDEADLK ;
errno = EDEADLK ;
}
}
static void
malloc_global_corrupt ( void )
{
wrterror ( " global malloc data corrupt " ) ;
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
errno = EINVAL ;
}
static int
malloc_init ( void )
{
if ( omalloc_init ( & g_pool ) ) {
_MALLOC_UNLOCK ( ) ;
if ( mopts . malloc_xmalloc )
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
return - 1 ;
}
return 0 ;
}
void *
void *
malloc ( size_t size )
malloc ( size_t size )
{
{
@ -1145,24 +1248,21 @@ malloc(size_t size)
int saved_errno = errno ;
int saved_errno = errno ;
_MALLOC_LOCK ( ) ;
_MALLOC_LOCK ( ) ;
UNPROTECT_G_POOL ( ) ;
malloc_func = " in malloc(): " ;
malloc_func = " in malloc(): " ;
if ( ! g_pool . regions_total ) {
if ( omalloc_init ( & g_pool ) ) {
_MALLOC_UNLOCK ( ) ;
if ( malloc_xmalloc )
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
if ( g_pool = = NULL ) {
if ( malloc_init ( ) ! = 0 )
return NULL ;
return NULL ;
}
}
}
if ( malloc_active + + ) {
if ( malloc_active + + ) {
malloc_recurse ( ) ;
malloc_recurse ( ) ;
return NULL ;
return NULL ;
}
}
r = omalloc ( size , malloc_zero ) ;
r = omalloc ( size , mopts . m alloc_zero ) ;
malloc_active - - ;
malloc_active - - ;
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
_MALLOC_UNLOCK ( ) ;
if ( r = = NULL & & malloc_xmalloc ) {
if ( r = = NULL & & mopts . m alloc_xmalloc ) {
wrterror ( " out of memory " ) ;
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
errno = ENOMEM ;
}
}
@ -1177,14 +1277,15 @@ ofree(void *p)
struct region_info * r ;
struct region_info * r ;
size_t sz ;
size_t sz ;
r = find ( & g_pool , p ) ;
r = find ( g_pool , p ) ;
if ( r = = NULL ) {
if ( r = = NULL ) {
wrterror ( " bogus pointer (double free?) " ) ;
wrterror ( " bogus pointer (double free?) " ) ;
return ;
return ;
}
}
REALSIZE ( sz , r ) ;
REALSIZE ( sz , r ) ;
if ( sz > MALLOC_MAXCHUNK ) {
if ( sz > MALLOC_MAXCHUNK ) {
if ( sz - malloc_guard > = MALLOC_PAGESIZE - MALLOC_LEEWAY ) {
if ( sz - mopts . malloc_guard > = MALLOC_PAGESIZE -
MALLOC_LEEWAY ) {
if ( r - > p ! = p ) {
if ( r - > p ! = p ) {
wrterror ( " bogus pointer " ) ;
wrterror ( " bogus pointer " ) ;
return ;
return ;
@ -1193,46 +1294,47 @@ ofree(void *p)
# if notyetbecause_of_realloc
# if notyetbecause_of_realloc
/* shifted towards the end */
/* shifted towards the end */
if ( p ! = ( ( char * ) r - > p ) + ( ( MALLOC_PAGESIZE -
if ( p ! = ( ( char * ) r - > p ) + ( ( MALLOC_PAGESIZE -
MALLOC_MINSIZE - sz - malloc_guard ) &
MALLOC_MINSIZE - sz - mopts . m alloc_guard ) &
~ ( MALLOC_MINSIZE - 1 ) ) ) {
~ ( MALLOC_MINSIZE - 1 ) ) ) {
}
}
# endif
# endif
p = r - > p ;
p = r - > p ;
}
}
if ( malloc_guard ) {
if ( sz < malloc_guard )
if ( mopts . m alloc_guard ) {
if ( sz < mopts . m alloc_guard )
wrterror ( " guard size " ) ;
wrterror ( " guard size " ) ;
if ( ! malloc_freeprot ) {
if ( ! mopts . m alloc_freeprot ) {
if ( mprotect ( ( char * ) p + PAGEROUND ( sz ) -
if ( mprotect ( ( char * ) p + PAGEROUND ( sz ) -
malloc_guard , malloc_guard ,
mopts . m alloc_guard , mopts . malloc_guard ,
PROT_READ | PROT_WRITE ) )
PROT_READ | PROT_WRITE ) )
wrterror ( " mprotect " ) ;
wrterror ( " mprotect " ) ;
}
}
malloc_guarded - = malloc_guard ;
malloc_guarded - = mopts . m alloc_guard ;
}
}
if ( malloc_junk & & ! malloc_freeprot )
memset ( p , SOME_FREEJUNK , PAGEROUND ( sz ) - malloc_guard ) ;
unmap ( & g_pool , p , PAGEROUND ( sz ) ) ;
delete ( & g_pool , r ) ;
if ( mopts . malloc_junk & & ! mopts . malloc_freeprot )
memset ( p , SOME_FREEJUNK ,
PAGEROUND ( sz ) - mopts . malloc_guard ) ;
unmap ( g_pool , p , PAGEROUND ( sz ) ) ;
delete ( g_pool , r ) ;
} else {
} else {
void * tmp ;
void * tmp ;
int i ;
int i ;
if ( malloc_junk & & sz > 0 )
if ( mopts . m alloc_junk & & sz > 0 )
memset ( p , SOME_FREEJUNK , sz ) ;
memset ( p , SOME_FREEJUNK , sz ) ;
if ( ! malloc_freeprot ) {
if ( ! mopts . m alloc_freeprot ) {
i = getrbyte ( ) & ( MALLOC_DELAYED_CHUNKS - 1 ) ;
i = getrbyte ( ) & ( MALLOC_DELAYED_CHUNKS - 1 ) ;
tmp = p ;
tmp = p ;
p = g_pool . delayed_chunks [ i ] ;
g_pool . delayed_chunks [ i ] = tmp ;
p = g_pool - > delayed_chunks [ i ] ;
g_pool - > delayed_chunks [ i ] = tmp ;
}
}
if ( p ! = NULL ) {
if ( p ! = NULL ) {
r = find ( & g_pool , p ) ;
r = find ( g_pool , p ) ;
if ( r = = NULL ) {
if ( r = = NULL ) {
wrterror ( " bogus pointer (double free?) " ) ;
wrterror ( " bogus pointer (double free?) " ) ;
return ;
return ;
}
}
free_bytes ( & g_pool , r , p ) ;
free_bytes ( g_pool , r , p ) ;
}
}
}
}
}
}
@ -1247,13 +1349,20 @@ free(void *ptr)
return ;
return ;
_MALLOC_LOCK ( ) ;
_MALLOC_LOCK ( ) ;
UNPROTECT_G_POOL ( ) ;
malloc_func = " in free(): " ;
malloc_func = " in free(): " ;
if ( g_pool = = NULL ) {
_MALLOC_UNLOCK ( ) ;
wrterror ( " free() called before allocation " ) ;
return ;
}
if ( malloc_active + + ) {
if ( malloc_active + + ) {
malloc_recurse ( ) ;
malloc_recurse ( ) ;
return ;
return ;
}
}
ofree ( ptr ) ;
ofree ( ptr ) ;
malloc_active - - ;
malloc_active - - ;
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
_MALLOC_UNLOCK ( ) ;
errno = saved_errno ;
errno = saved_errno ;
}
}
@ -1269,12 +1378,12 @@ orealloc(void *p, size_t newsz)
if ( p = = NULL )
if ( p = = NULL )
return omalloc ( newsz , 0 ) ;
return omalloc ( newsz , 0 ) ;
r = find ( & g_pool , p ) ;
r = find ( g_pool , p ) ;
if ( r = = NULL ) {
if ( r = = NULL ) {
wrterror ( " bogus pointer (double free?) " ) ;
wrterror ( " bogus pointer (double free?) " ) ;
return NULL ;
return NULL ;
}
}
if ( newsz > = SIZE_MAX - malloc_guard - MALLOC_PAGESIZE ) {
if ( newsz > = SIZE_MAX - mopts . m alloc_guard - MALLOC_PAGESIZE ) {
errno = ENOMEM ;
errno = ENOMEM ;
return NULL ;
return NULL ;
}
}
@ -1282,61 +1391,63 @@ orealloc(void *p, size_t newsz)
REALSIZE ( oldsz , r ) ;
REALSIZE ( oldsz , r ) ;
goldsz = oldsz ;
goldsz = oldsz ;
if ( oldsz > MALLOC_MAXCHUNK ) {
if ( oldsz > MALLOC_MAXCHUNK ) {
if ( oldsz < malloc_guard )
if ( oldsz < mopts . m alloc_guard )
wrterror ( " guard size " ) ;
wrterror ( " guard size " ) ;
oldsz - = malloc_guard ;
oldsz - = mopts . m alloc_guard ;
}
}
gnewsz = newsz ;
gnewsz = newsz ;
if ( gnewsz > MALLOC_MAXCHUNK )
if ( gnewsz > MALLOC_MAXCHUNK )
gnewsz + = malloc_guard ;
gnewsz + = mopts . m alloc_guard ;
if ( newsz > MALLOC_MAXCHUNK & & oldsz > MALLOC_MAXCHUNK & & p = = r - > p & &
if ( newsz > MALLOC_MAXCHUNK & & oldsz > MALLOC_MAXCHUNK & & p = = r - > p & &
! malloc_realloc ) {
! mopts . m alloc_realloc ) {
size_t roldsz = PAGEROUND ( goldsz ) ;
size_t roldsz = PAGEROUND ( goldsz ) ;
size_t rnewsz = PAGEROUND ( gnewsz ) ;
size_t rnewsz = PAGEROUND ( gnewsz ) ;
if ( rnewsz > roldsz ) {
if ( rnewsz > roldsz ) {
if ( ! malloc_guard ) {
if ( ! mopts . m alloc_guard ) {
STATS_INC ( g_pool . cheap_realloc_tries ) ;
STATS_INC ( g_pool . cheap_realloc_tries ) ;
zapcacheregion ( & g_pool , p + roldsz ) ;
zapcacheregion ( g_pool , p + roldsz ) ;
q = MMAPA ( p + roldsz , rnewsz - roldsz ) ;
q = MMAPA ( p + roldsz , rnewsz - roldsz ) ;
if ( q = = p + roldsz ) {
if ( q = = p + roldsz ) {
malloc_used + = rnewsz - roldsz ;
malloc_used + = rnewsz - roldsz ;
if ( malloc_junk )
if ( mopts . m alloc_junk )
memset ( q , SOME_JUNK ,
memset ( q , SOME_JUNK ,
rnewsz - roldsz ) ;
rnewsz - roldsz ) ;
r - > size = newsz ;
r - > size = newsz ;
STATS_INC ( g_pool . cheap_reallocs ) ;
STATS_INC ( g_pool - > cheap_reallocs ) ;
return p ;
return p ;
} else if ( q ! = MAP_FAILED )
} else if ( q ! = MAP_FAILED )
munmap ( q , rnewsz - roldsz ) ;
munmap ( q , rnewsz - roldsz ) ;
}
}
} else if ( rnewsz < roldsz ) {
} else if ( rnewsz < roldsz ) {
if ( malloc_guard ) {
if ( mprotect ( ( char * ) p + roldsz - malloc_guard ,
malloc_guard , PROT_READ | PROT_WRITE ) )
if ( mopts . malloc_guard ) {
if ( mprotect ( ( char * ) p + roldsz -
mopts . malloc_guard , mopts . malloc_guard ,
PROT_READ | PROT_WRITE ) )
wrterror ( " mprotect " ) ;
wrterror ( " mprotect " ) ;
if ( mprotect ( ( char * ) p + rnewsz - malloc_guard ,
malloc_guard , PROT_NONE ) )
if ( mprotect ( ( char * ) p + rnewsz -
mopts . malloc_guard , mopts . malloc_guard ,
PROT_NONE ) )
wrterror ( " mprotect " ) ;
wrterror ( " mprotect " ) ;
}
}
unmap ( & g_pool , ( char * ) p + rnewsz , roldsz - rnewsz ) ;
unmap ( g_pool , ( char * ) p + rnewsz , roldsz - rnewsz ) ;
r - > size = gnewsz ;
r - > size = gnewsz ;
return p ;
return p ;
} else {
} else {
if ( newsz > oldsz & & malloc_junk )
if ( newsz > oldsz & & mopts . m alloc_junk )
memset ( ( char * ) p + newsz , SOME_JUNK ,
memset ( ( char * ) p + newsz , SOME_JUNK ,
rnewsz - malloc_guard - newsz ) ;
rnewsz - mopts . m alloc_guard - newsz ) ;
r - > size = gnewsz ;
r - > size = gnewsz ;
return p ;
return p ;
}
}
}
}
if ( newsz < = oldsz & & newsz > oldsz / 2 & & ! malloc_realloc ) {
if ( malloc_junk & & newsz > 0 )
if ( newsz < = oldsz & & newsz > oldsz / 2 & & ! mopts . m alloc_realloc ) {
if ( mopts . m alloc_junk & & newsz > 0 )
memset ( ( char * ) p + newsz , SOME_JUNK , oldsz - newsz ) ;
memset ( ( char * ) p + newsz , SOME_JUNK , oldsz - newsz ) ;
return p ;
return p ;
} else if ( newsz ! = oldsz | | malloc_realloc ) {
} else if ( newsz ! = oldsz | | mopts . m alloc_realloc ) {
q = omalloc ( newsz , 0 ) ;
q = omalloc ( newsz , 0 ) ;
if ( q = = NULL )
if ( q = = NULL )
return NULL ;
return NULL ;
@ -1355,26 +1466,22 @@ realloc(void *ptr, size_t size)
int saved_errno = errno ;
int saved_errno = errno ;
_MALLOC_LOCK ( ) ;
_MALLOC_LOCK ( ) ;
UNPROTECT_G_POOL ( ) ;
malloc_func = " in realloc(): " ;
malloc_func = " in realloc(): " ;
if ( ! g_pool . regions_total ) {
if ( omalloc_init ( & g_pool ) ) {
_MALLOC_UNLOCK ( ) ;
if ( malloc_xmalloc )
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
if ( g_pool = = NULL ) {
if ( malloc_init ( ) ! = 0 )
return NULL ;
return NULL ;
}
}
}
if ( malloc_active + + ) {
if ( malloc_active + + ) {
malloc_recurse ( ) ;
malloc_recurse ( ) ;
return NULL ;
return NULL ;
}
}
r = orealloc ( ptr , size ) ;
r = orealloc ( ptr , size ) ;
malloc_active - - ;
malloc_active - - ;
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
_MALLOC_UNLOCK ( ) ;
if ( r = = NULL & & malloc_xmalloc ) {
if ( r = = NULL & & mopts . m alloc_xmalloc ) {
wrterror ( " out of memory " ) ;
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
errno = ENOMEM ;
}
}
@ -1393,20 +1500,17 @@ calloc(size_t nmemb, size_t size)
int saved_errno = errno ;
int saved_errno = errno ;
_MALLOC_LOCK ( ) ;
_MALLOC_LOCK ( ) ;
UNPROTECT_G_POOL ( ) ;
malloc_func = " in calloc(): " ;
malloc_func = " in calloc(): " ;
if ( ! g_pool . regions_total ) {
if ( omalloc_init ( & g_pool ) ) {
_MALLOC_UNLOCK ( ) ;
if ( malloc_xmalloc )
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
if ( g_pool = = NULL ) {
if ( malloc_init ( ) ! = 0 )
return NULL ;
return NULL ;
}
}
}
if ( ( nmemb > = MUL_NO_OVERFLOW | | size > = MUL_NO_OVERFLOW ) & &
if ( ( nmemb > = MUL_NO_OVERFLOW | | size > = MUL_NO_OVERFLOW ) & &
nmemb > 0 & & SIZE_MAX / nmemb < size ) {
nmemb > 0 & & SIZE_MAX / nmemb < size ) {
_MALLOC_UNLOCK ( ) ;
if ( malloc_xmalloc )
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
if ( mopts . malloc_xmalloc )
wrterror ( " out of memory " ) ;
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
errno = ENOMEM ;
return NULL ;
return NULL ;
@ -1421,8 +1525,9 @@ calloc(size_t nmemb, size_t size)
r = omalloc ( size , 1 ) ;
r = omalloc ( size , 1 ) ;
malloc_active - - ;
malloc_active - - ;
PROTECT_G_POOL ( ) ;
_MALLOC_UNLOCK ( ) ;
_MALLOC_UNLOCK ( ) ;
if ( r = = NULL & & malloc_xmalloc ) {
if ( r = = NULL & & mopts . m alloc_xmalloc ) {
wrterror ( " out of memory " ) ;
wrterror ( " out of memory " ) ;
errno = ENOMEM ;
errno = ENOMEM ;
}
}
@ -1430,3 +1535,4 @@ calloc(size_t nmemb, size_t size)
errno = saved_errno ;
errno = saved_errno ;
return r ;
return r ;
}
}