aboutsummaryrefslogtreecommitdiff
path: root/malloc/hooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'malloc/hooks.c')
-rw-r--r--malloc/hooks.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/malloc/hooks.c b/malloc/hooks.c
new file mode 100644
index 0000000..28629ef
--- /dev/null
+++ b/malloc/hooks.c
@@ -0,0 +1,631 @@
+/* Malloc implementation for multiple threads without lock contention.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* $Id$ */
+
+#ifndef weak_variable
+#define weak_variable /**/
+#endif
+
+#ifndef DEFAULT_CHECK_ACTION
+#define DEFAULT_CHECK_ACTION 1
+#endif
+
+/* What to do if the standard debugging hooks are in place and a
+ corrupt pointer is detected: do nothing (0), print an error message
+ (1), or call abort() (2). */
+
+/* Hooks for debugging versions. The initial hooks just call the
+ initialization routine, then do the normal work. */
+
+static Void_t*
+#if __STD_C
+malloc_hook_ini(size_t sz, const __malloc_ptr_t caller)
+#else
+malloc_hook_ini(sz, caller)
+ size_t sz; const __malloc_ptr_t caller;
+#endif
+{
+ __malloc_hook = NULL;
+ ptmalloc_init();
+ return public_mALLOc(sz);
+}
+
+static Void_t*
+#if __STD_C
+realloc_hook_ini(Void_t* ptr, size_t sz, const __malloc_ptr_t caller)
+#else
+realloc_hook_ini(ptr, sz, caller)
+ Void_t* ptr; size_t sz; const __malloc_ptr_t caller;
+#endif
+{
+ __malloc_hook = NULL;
+ __realloc_hook = NULL;
+ ptmalloc_init();
+ return public_rEALLOc(ptr, sz);
+}
+
+static Void_t*
+#if __STD_C
+memalign_hook_ini(size_t alignment, size_t sz, const __malloc_ptr_t caller)
+#else
+memalign_hook_ini(alignment, sz, caller)
+ size_t alignment; size_t sz; const __malloc_ptr_t caller;
+#endif
+{
+ __memalign_hook = NULL;
+ ptmalloc_init();
+ return public_mEMALIGn(alignment, sz);
+}
+
+void weak_variable (*__malloc_initialize_hook) __MALLOC_P ((void)) = NULL;
+void weak_variable (*__free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
+ const __malloc_ptr_t)) = NULL;
+__malloc_ptr_t weak_variable (*__malloc_hook)
+ __MALLOC_P ((size_t __size, const __malloc_ptr_t)) = malloc_hook_ini;
+__malloc_ptr_t weak_variable (*__realloc_hook)
+ __MALLOC_P ((__malloc_ptr_t __ptr, size_t __size, const __malloc_ptr_t))
+ = realloc_hook_ini;
+__malloc_ptr_t weak_variable (*__memalign_hook)
+ __MALLOC_P ((size_t __alignment, size_t __size, const __malloc_ptr_t))
+ = memalign_hook_ini;
+void weak_variable (*__after_morecore_hook) __MALLOC_P ((void)) = NULL;
+
+
+static int check_action = DEFAULT_CHECK_ACTION;
+
+/* Whether we are using malloc checking. */
+static int using_malloc_checking;
+
+/* A flag that is set by malloc_set_state, to signal that malloc checking
+ must not be enabled on the request from the user (via the MALLOC_CHECK_
+ environment variable). It is reset by __malloc_check_init to tell
+ malloc_set_state that the user has requested malloc checking.
+
+ The purpose of this flag is to make sure that malloc checking is not
+ enabled when the heap to be restored was constructed without malloc
+ checking, and thus does not contain the required magic bytes.
+ Otherwise the heap would be corrupted by calls to free and realloc. If
+ it turns out that the heap was created with malloc checking and the
+ user has requested it malloc_set_state just calls __malloc_check_init
+ again to enable it. On the other hand, reusing such a heap without
+ further malloc checking is safe. */
+static int disallow_malloc_check;
+
+/* Activate a standard set of debugging hooks. */
+void
+__malloc_check_init()
+{
+ if (disallow_malloc_check) {
+ disallow_malloc_check = 0;
+ return;
+ }
+ using_malloc_checking = 1;
+ __malloc_hook = malloc_check;
+ __free_hook = free_check;
+ __realloc_hook = realloc_check;
+ __memalign_hook = memalign_check;
+ if(check_action & 1)
+ fprintf(stderr, "malloc: using debugging hooks\n");
+}
+
+/* A simple, standard set of debugging hooks. Overhead is `only' one
+ byte per chunk; still this will catch most cases of double frees or
+ overruns. The goal here is to avoid obscure crashes due to invalid
+ usage, unlike in the MALLOC_DEBUG code. */
+
+#define MAGICBYTE(p) ( ( ((size_t)p >> 3) ^ ((size_t)p >> 11)) & 0xFF )
+
+/* Instrument a chunk with overrun detector byte(s) and convert it
+ into a user pointer with requested size sz. */
+
+static Void_t*
+internal_function
+#if __STD_C
+mem2mem_check(Void_t *ptr, size_t sz)
+#else
+mem2mem_check(ptr, sz) Void_t *ptr; size_t sz;
+#endif
+{
+ mchunkptr p;
+ unsigned char* m_ptr = (unsigned char*)BOUNDED_N(ptr, sz);
+ size_t i;
+
+ if (!ptr)
+ return ptr;
+ p = mem2chunk(ptr);
+ for(i = chunksize(p) - (chunk_is_mmapped(p) ? 2*SIZE_SZ+1 : SIZE_SZ+1);
+ i > sz;
+ i -= 0xFF) {
+ if(i-sz < 0x100) {
+ m_ptr[i] = (unsigned char)(i-sz);
+ break;
+ }
+ m_ptr[i] = 0xFF;
+ }
+ m_ptr[sz] = MAGICBYTE(p);
+ return (Void_t*)m_ptr;
+}
+
+/* Convert a pointer to be free()d or realloc()ed to a valid chunk
+ pointer. If the provided pointer is not valid, return NULL. */
+
+static mchunkptr
+internal_function
+#if __STD_C
+mem2chunk_check(Void_t* mem)
+#else
+mem2chunk_check(mem) Void_t* mem;
+#endif
+{
+ mchunkptr p;
+ INTERNAL_SIZE_T sz, c;
+ unsigned char magic;
+
+ p = mem2chunk(mem);
+ if(!aligned_OK(p)) return NULL;
+ if( (char*)p>=mp_.sbrk_base &&
+ (char*)p<(mp_.sbrk_base+main_arena.system_mem) ) {
+ /* Must be a chunk in conventional heap memory. */
+ if(chunk_is_mmapped(p) ||
+ ( (sz = chunksize(p)),
+ ((char*)p + sz)>=(mp_.sbrk_base+main_arena.system_mem) ) ||
+ sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ||
+ ( !prev_inuse(p) && (p->prev_size&MALLOC_ALIGN_MASK ||
+ (long)prev_chunk(p)<(long)mp_.sbrk_base ||
+ next_chunk(prev_chunk(p))!=p) ))
+ return NULL;
+ magic = MAGICBYTE(p);
+ for(sz += SIZE_SZ-1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
+ if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
+ }
+ ((unsigned char*)p)[sz] ^= 0xFF;
+ } else {
+ unsigned long offset, page_mask = malloc_getpagesize-1;
+
+ /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
+ alignment relative to the beginning of a page. Check this
+ first. */
+ offset = (unsigned long)mem & page_mask;
+ if((offset!=MALLOC_ALIGNMENT && offset!=0 && offset!=0x10 &&
+ offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
+ offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
+ offset<0x2000) ||
+ !chunk_is_mmapped(p) || (p->size & PREV_INUSE) ||
+ ( (((unsigned long)p - p->prev_size) & page_mask) != 0 ) ||
+ ( (sz = chunksize(p)), ((p->prev_size + sz) & page_mask) != 0 ) )
+ return NULL;
+ magic = MAGICBYTE(p);
+ for(sz -= 1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
+ if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
+ }
+ ((unsigned char*)p)[sz] ^= 0xFF;
+ }
+ return p;
+}
+
+/* Check for corruption of the top chunk, and try to recover if
+ necessary. */
+
+static int
+internal_function
+#if __STD_C
+top_check(void)
+#else
+top_check()
+#endif
+{
+ mchunkptr t = top(&main_arena);
+ char* brk, * new_brk;
+ INTERNAL_SIZE_T front_misalign, sbrk_size;
+ unsigned long pagesz = malloc_getpagesize;
+
+ if((char*)t + chunksize(t) == mp_.sbrk_base + main_arena.system_mem ||
+ t == initial_top(&main_arena)) return 0;
+
+ if(check_action & 1)
+ fprintf(stderr, "malloc: top chunk is corrupt\n");
+ if(check_action & 2)
+ abort();
+
+ /* Try to set up a new top chunk. */
+ brk = MORECORE(0);
+ front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
+ if (front_misalign > 0)
+ front_misalign = MALLOC_ALIGNMENT - front_misalign;
+ sbrk_size = front_misalign + mp_.top_pad + MINSIZE;
+ sbrk_size += pagesz - ((unsigned long)(brk + sbrk_size) & (pagesz - 1));
+ new_brk = (char*)(MORECORE (sbrk_size));
+ if (new_brk == (char*)(MORECORE_FAILURE)) return -1;
+ /* Call the `morecore' hook if necessary. */
+ if (__after_morecore_hook)
+ (*__after_morecore_hook) ();
+ main_arena.system_mem = (new_brk - mp_.sbrk_base) + sbrk_size;
+
+ top(&main_arena) = (mchunkptr)(brk + front_misalign);
+ set_head(top(&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
+
+ return 0;
+}
+
+static Void_t*
+#if __STD_C
+malloc_check(size_t sz, const Void_t *caller)
+#else
+malloc_check(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+ Void_t *victim;
+
+ (void)mutex_lock(&main_arena.mutex);
+ victim = (top_check() >= 0) ? _int_malloc(&main_arena, sz+1) : NULL;
+ (void)mutex_unlock(&main_arena.mutex);
+ return mem2mem_check(victim, sz);
+}
+
+static void
+#if __STD_C
+free_check(Void_t* mem, const Void_t *caller)
+#else
+free_check(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+ mchunkptr p;
+
+ if(!mem) return;
+ (void)mutex_lock(&main_arena.mutex);
+ p = mem2chunk_check(mem);
+ if(!p) {
+ (void)mutex_unlock(&main_arena.mutex);
+ if(check_action & 1)
+ fprintf(stderr, "free(): invalid pointer %p!\n", mem);
+ if(check_action & 2)
+ abort();
+ return;
+ }
+#if HAVE_MMAP
+ if (chunk_is_mmapped(p)) {
+ (void)mutex_unlock(&main_arena.mutex);
+ munmap_chunk(p);
+ return;
+ }
+#endif
+#if 0 /* Erase freed memory. */
+ memset(mem, 0, chunksize(p) - (SIZE_SZ+1));
+#endif
+ _int_free(&main_arena, mem);
+ (void)mutex_unlock(&main_arena.mutex);
+}
+
+static Void_t*
+#if __STD_C
+realloc_check(Void_t* oldmem, size_t bytes, const Void_t *caller)
+#else
+realloc_check(oldmem, bytes, caller)
+ Void_t* oldmem; size_t bytes; const Void_t *caller;
+#endif
+{
+ mchunkptr oldp, newp = 0;
+ INTERNAL_SIZE_T nb, oldsize;
+ Void_t* newmem = 0;
+
+ if (oldmem == 0) return malloc_check(bytes, NULL);
+ (void)mutex_lock(&main_arena.mutex);
+ oldp = mem2chunk_check(oldmem);
+ (void)mutex_unlock(&main_arena.mutex);
+ if(!oldp) {
+ if(check_action & 1)
+ fprintf(stderr, "realloc(): invalid pointer %p!\n", oldmem);
+ if(check_action & 2)
+ abort();
+ return malloc_check(bytes, NULL);
+ }
+ oldsize = chunksize(oldp);
+
+ checked_request2size(bytes+1, nb);
+ (void)mutex_lock(&main_arena.mutex);
+
+#if HAVE_MMAP
+ if (chunk_is_mmapped(oldp)) {
+#if HAVE_MREMAP
+ newp = mremap_chunk(oldp, nb);
+ if(!newp) {
+#endif
+ /* Note the extra SIZE_SZ overhead. */
+ if(oldsize - SIZE_SZ >= nb)
+ newmem = oldmem; /* do nothing */
+ else {
+ /* Must alloc, copy, free. */
+ if (top_check() >= 0)
+ newmem = _int_malloc(&main_arena, bytes+1);
+ if (newmem) {
+ MALLOC_COPY(BOUNDED_N(newmem, bytes+1), oldmem, oldsize - 2*SIZE_SZ);
+ munmap_chunk(oldp);
+ }
+ }
+#if HAVE_MREMAP
+ }
+#endif
+ } else {
+#endif /* HAVE_MMAP */
+ if (top_check() >= 0)
+ newmem = _int_realloc(&main_arena, oldmem, bytes+1);
+#if 0 /* Erase freed memory. */
+ if(newmem)
+ newp = mem2chunk(newmem);
+ nb = chunksize(newp);
+ if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
+ memset((char*)oldmem + 2*sizeof(mbinptr), 0,
+ oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
+ } else if(nb > oldsize+SIZE_SZ) {
+ memset((char*)BOUNDED_N(chunk2mem(newp), bytes) + oldsize,
+ 0, nb - (oldsize+SIZE_SZ));
+ }
+#endif
+#if HAVE_MMAP
+ }
+#endif
+ (void)mutex_unlock(&main_arena.mutex);
+
+ return mem2mem_check(newmem, bytes);
+}
+
+static Void_t*
+#if __STD_C
+memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
+#else
+memalign_check(alignment, bytes, caller)
+ size_t alignment; size_t bytes; const Void_t *caller;
+#endif
+{
+ INTERNAL_SIZE_T nb;
+ Void_t* mem;
+
+ if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
+ if (alignment < MINSIZE) alignment = MINSIZE;
+
+ checked_request2size(bytes+1, nb);
+ (void)mutex_lock(&main_arena.mutex);
+ mem = (top_check() >= 0) ? _int_memalign(&main_arena, alignment, bytes+1) :
+ NULL;
+ (void)mutex_unlock(&main_arena.mutex);
+ return mem2mem_check(mem, bytes);
+}
+
+#ifndef NO_THREADS
+
+/* The following hooks are used when the global initialization in
+ ptmalloc_init() hasn't completed yet. */
+
+static Void_t*
+#if __STD_C
+malloc_starter(size_t sz, const Void_t *caller)
+#else
+malloc_starter(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+ Void_t* victim;
+
+ victim = _int_malloc(&main_arena, sz);
+
+ return victim ? BOUNDED_N(victim, sz) : 0;
+}
+
+static void
+#if __STD_C
+free_starter(Void_t* mem, const Void_t *caller)
+#else
+free_starter(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+ mchunkptr p;
+
+ if(!mem) return;
+ p = mem2chunk(mem);
+#if HAVE_MMAP
+ if (chunk_is_mmapped(p)) {
+ munmap_chunk(p);
+ return;
+ }
+#endif
+ _int_free(&main_arena, mem);
+}
+
+#endif /* NO_THREADS */
+
+
+/* Get/set state: malloc_get_state() records the current state of all
+ malloc variables (_except_ for the actual heap contents and `hook'
+ function pointers) in a system dependent, opaque data structure.
+ This data structure is dynamically allocated and can be free()d
+ after use. malloc_set_state() restores the state of all malloc
+ variables to the previously obtained state. This is especially
+ useful when using this malloc as part of a shared library, and when
+ the heap contents are saved/restored via some other method. The
+ primary example for this is GNU Emacs with its `dumping' procedure.
+ `Hook' function pointers are never saved or restored by these
+ functions, with two exceptions: If malloc checking was in use when
+ malloc_get_state() was called, then malloc_set_state() calls
+ __malloc_check_init() if possible; if malloc checking was not in
+ use in the recorded state but the user requested malloc checking,
+ then the hooks are reset to 0. */
+
+#define MALLOC_STATE_MAGIC 0x444c4541l
+#define MALLOC_STATE_VERSION (0*0x100l + 2l) /* major*0x100 + minor */
+
+struct malloc_save_state {
+ long magic;
+ long version;
+ mbinptr av[NBINS * 2 + 2];
+ char* sbrk_base;
+ int sbrked_mem_bytes;
+ unsigned long trim_threshold;
+ unsigned long top_pad;
+ unsigned int n_mmaps_max;
+ unsigned long mmap_threshold;
+ int check_action;
+ unsigned long max_sbrked_mem;
+ unsigned long max_total_mem;
+ unsigned int n_mmaps;
+ unsigned int max_n_mmaps;
+ unsigned long mmapped_mem;
+ unsigned long max_mmapped_mem;
+ int using_malloc_checking;
+};
+
+Void_t*
+public_gET_STATe(void)
+{
+ struct malloc_save_state* ms;
+ int i;
+ mbinptr b;
+
+ ms = (struct malloc_save_state*)public_mALLOc(sizeof(*ms));
+ if (!ms)
+ return 0;
+ (void)mutex_lock(&main_arena.mutex);
+ malloc_consolidate(&main_arena);
+ ms->magic = MALLOC_STATE_MAGIC;
+ ms->version = MALLOC_STATE_VERSION;
+ ms->av[0] = 0;
+ ms->av[1] = 0; /* used to be binblocks, now no longer used */
+ ms->av[2] = top(&main_arena);
+ ms->av[3] = 0; /* used to be undefined */
+ for(i=1; i<NBINS; i++) {
+ b = bin_at(&main_arena, i);
+ if(first(b) == b)
+ ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin */
+ else {
+ ms->av[2*i+2] = first(b);
+ ms->av[2*i+3] = last(b);
+ }
+ }
+ ms->sbrk_base = mp_.sbrk_base;
+ ms->sbrked_mem_bytes = main_arena.system_mem;
+ ms->trim_threshold = mp_.trim_threshold;
+ ms->top_pad = mp_.top_pad;
+ ms->n_mmaps_max = mp_.n_mmaps_max;
+ ms->mmap_threshold = mp_.mmap_threshold;
+ ms->check_action = check_action;
+ ms->max_sbrked_mem = main_arena.max_system_mem;
+#ifdef NO_THREADS
+ ms->max_total_mem = max_total_mem;
+#else
+ ms->max_total_mem = 0;
+#endif
+ ms->n_mmaps = mp_.n_mmaps;
+ ms->max_n_mmaps = mp_.max_n_mmaps;
+ ms->mmapped_mem = mp_.mmapped_mem;
+ ms->max_mmapped_mem = mp_.max_mmapped_mem;
+ ms->using_malloc_checking = using_malloc_checking;
+ (void)mutex_unlock(&main_arena.mutex);
+ return (Void_t*)ms;
+}
+
+int
+public_sET_STATe(Void_t* msptr)
+{
+ struct malloc_save_state* ms = (struct malloc_save_state*)msptr;
+ int i;
+ mbinptr b;
+
+ disallow_malloc_check = 1;
+ ptmalloc_init();
+ if(ms->magic != MALLOC_STATE_MAGIC) return -1;
+ /* Must fail if the major version is too high. */
+ if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
+ (void)mutex_lock(&main_arena.mutex);
+ /* There are no fastchunks. */
+ clear_fastchunks(&main_arena);
+ set_max_fast(&main_arena, DEFAULT_MXFAST);
+ for (i=0; i<NFASTBINS; ++i)
+ main_arena.fastbins[i] = 0;
+ for (i=0; i<BINMAPSIZE; ++i)
+ main_arena.binmap[i] = 0;
+ top(&main_arena) = ms->av[2];
+ main_arena.last_remainder = 0;
+ for(i=1; i<NBINS; i++) {
+ b = bin_at(&main_arena, i);
+ if(ms->av[2*i+2] == 0) {
+ assert(ms->av[2*i+3] == 0);
+ first(b) = last(b) = b;
+ } else {
+ if(i<NSMALLBINS || (largebin_index(chunksize(ms->av[2*i+2]))==i &&
+ largebin_index(chunksize(ms->av[2*i+3]))==i)) {
+ first(b) = ms->av[2*i+2];
+ last(b) = ms->av[2*i+3];
+ /* Make sure the links to the bins within the heap are correct. */
+ first(b)->bk = b;
+ last(b)->fd = b;
+ /* Set bit in binblocks. */
+ mark_bin(&main_arena, i);
+ } else {
+ /* Oops, index computation from chunksize must have changed.
+ Link the whole list into unsorted_chunks. */
+ first(b) = last(b) = b;
+ b = unsorted_chunks(&main_arena);
+ ms->av[2*i+2]->bk = b;
+ ms->av[2*i+3]->fd = b->fd;
+ b->fd->bk = ms->av[2*i+3];
+ b->fd = ms->av[2*i+2];
+ }
+ }
+ }
+ mp_.sbrk_base = ms->sbrk_base;
+ main_arena.system_mem = ms->sbrked_mem_bytes;
+ mp_.trim_threshold = ms->trim_threshold;
+ mp_.top_pad = ms->top_pad;
+ mp_.n_mmaps_max = ms->n_mmaps_max;
+ mp_.mmap_threshold = ms->mmap_threshold;
+ check_action = ms->check_action;
+ main_arena.max_system_mem = ms->max_sbrked_mem;
+#ifdef NO_THREADS
+ mp_.max_total_mem = ms->max_total_mem;
+#endif
+ mp_.n_mmaps = ms->n_mmaps;
+ mp_.max_n_mmaps = ms->max_n_mmaps;
+ mp_.mmapped_mem = ms->mmapped_mem;
+ mp_.max_mmapped_mem = ms->max_mmapped_mem;
+ /* add version-dependent code here */
+ if (ms->version >= 1) {
+ /* Check whether it is safe to enable malloc checking, or whether
+ it is necessary to disable it. */
+ if (ms->using_malloc_checking && !using_malloc_checking &&
+ !disallow_malloc_check)
+ __malloc_check_init ();
+ else if (!ms->using_malloc_checking && using_malloc_checking) {
+ __malloc_hook = 0;
+ __free_hook = 0;
+ __realloc_hook = 0;
+ __memalign_hook = 0;
+ using_malloc_checking = 0;
+ }
+ }
+ check_malloc_state(&main_arena);
+
+ (void)mutex_unlock(&main_arena.mutex);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 2
+ * End:
+ */