Part of malloc implementation.
[kopensolaris-gnu/glibc.git] / malloc / hooks.c
1 /* Malloc implementation for multiple threads without lock contention.
2    Copyright (C) 2001 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* $Id$ */
22
23 #ifndef weak_variable
24 #define weak_variable /**/
25 #endif
26
27 #ifndef DEFAULT_CHECK_ACTION
28 #define DEFAULT_CHECK_ACTION 1
29 #endif
30
31 /* What to do if the standard debugging hooks are in place and a
32    corrupt pointer is detected: do nothing (0), print an error message
33    (1), or call abort() (2). */
34
35 /* Hooks for debugging versions.  The initial hooks just call the
36    initialization routine, then do the normal work. */
37
38 static Void_t*
39 #if __STD_C
40 malloc_hook_ini(size_t sz, const __malloc_ptr_t caller)
41 #else
42 malloc_hook_ini(sz, caller)
43      size_t sz; const __malloc_ptr_t caller;
44 #endif
45 {
46   __malloc_hook = NULL;
47   ptmalloc_init();
48   return public_mALLOc(sz);
49 }
50
51 static Void_t*
52 #if __STD_C
53 realloc_hook_ini(Void_t* ptr, size_t sz, const __malloc_ptr_t caller)
54 #else
55 realloc_hook_ini(ptr, sz, caller)
56      Void_t* ptr; size_t sz; const __malloc_ptr_t caller;
57 #endif
58 {
59   __malloc_hook = NULL;
60   __realloc_hook = NULL;
61   ptmalloc_init();
62   return public_rEALLOc(ptr, sz);
63 }
64
65 static Void_t*
66 #if __STD_C
67 memalign_hook_ini(size_t alignment, size_t sz, const __malloc_ptr_t caller)
68 #else
69 memalign_hook_ini(alignment, sz, caller)
70      size_t alignment; size_t sz; const __malloc_ptr_t caller;
71 #endif
72 {
73   __memalign_hook = NULL;
74   ptmalloc_init();
75   return public_mEMALIGn(alignment, sz);
76 }
77
78 void weak_variable (*__malloc_initialize_hook) __MALLOC_P ((void)) = NULL;
79 void weak_variable (*__free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
80                                                const __malloc_ptr_t)) = NULL;
81 __malloc_ptr_t weak_variable (*__malloc_hook)
82  __MALLOC_P ((size_t __size, const __malloc_ptr_t)) = malloc_hook_ini;
83 __malloc_ptr_t weak_variable (*__realloc_hook)
84  __MALLOC_P ((__malloc_ptr_t __ptr, size_t __size, const __malloc_ptr_t))
85      = realloc_hook_ini;
86 __malloc_ptr_t weak_variable (*__memalign_hook)
87  __MALLOC_P ((size_t __alignment, size_t __size, const __malloc_ptr_t))
88      = memalign_hook_ini;
89 void weak_variable (*__after_morecore_hook) __MALLOC_P ((void)) = NULL;
90
91
92 static int check_action = DEFAULT_CHECK_ACTION;
93
94 /* Whether we are using malloc checking.  */
95 static int using_malloc_checking;
96
97 /* A flag that is set by malloc_set_state, to signal that malloc checking
98    must not be enabled on the request from the user (via the MALLOC_CHECK_
99    environment variable).  It is reset by __malloc_check_init to tell
100    malloc_set_state that the user has requested malloc checking.
101
102    The purpose of this flag is to make sure that malloc checking is not
103    enabled when the heap to be restored was constructed without malloc
104    checking, and thus does not contain the required magic bytes.
105    Otherwise the heap would be corrupted by calls to free and realloc.  If
106    it turns out that the heap was created with malloc checking and the
107    user has requested it malloc_set_state just calls __malloc_check_init
108    again to enable it.  On the other hand, reusing such a heap without
109    further malloc checking is safe.  */
110 static int disallow_malloc_check;
111
112 /* Activate a standard set of debugging hooks. */
113 void
114 __malloc_check_init()
115 {
116   if (disallow_malloc_check) {
117     disallow_malloc_check = 0;
118     return;
119   }
120   using_malloc_checking = 1;
121   __malloc_hook = malloc_check;
122   __free_hook = free_check;
123   __realloc_hook = realloc_check;
124   __memalign_hook = memalign_check;
125   if(check_action & 1)
126     fprintf(stderr, "malloc: using debugging hooks\n");
127 }
128
129 /* A simple, standard set of debugging hooks.  Overhead is `only' one
130    byte per chunk; still this will catch most cases of double frees or
131    overruns.  The goal here is to avoid obscure crashes due to invalid
132    usage, unlike in the MALLOC_DEBUG code. */
133
134 #define MAGICBYTE(p) ( ( ((size_t)p >> 3) ^ ((size_t)p >> 11)) & 0xFF )
135
136 /* Instrument a chunk with overrun detector byte(s) and convert it
137    into a user pointer with requested size sz. */
138
139 static Void_t*
140 internal_function
141 #if __STD_C
142 mem2mem_check(Void_t *ptr, size_t sz)
143 #else
144 mem2mem_check(ptr, sz) Void_t *ptr; size_t sz;
145 #endif
146 {
147   mchunkptr p;
148   unsigned char* m_ptr = (unsigned char*)BOUNDED_N(ptr, sz);
149   size_t i;
150
151   if (!ptr)
152     return ptr;
153   p = mem2chunk(ptr);
154   for(i = chunksize(p) - (chunk_is_mmapped(p) ? 2*SIZE_SZ+1 : SIZE_SZ+1);
155       i > sz;
156       i -= 0xFF) {
157     if(i-sz < 0x100) {
158       m_ptr[i] = (unsigned char)(i-sz);
159       break;
160     }
161     m_ptr[i] = 0xFF;
162   }
163   m_ptr[sz] = MAGICBYTE(p);
164   return (Void_t*)m_ptr;
165 }
166
167 /* Convert a pointer to be free()d or realloc()ed to a valid chunk
168    pointer.  If the provided pointer is not valid, return NULL. */
169
170 static mchunkptr
171 internal_function
172 #if __STD_C
173 mem2chunk_check(Void_t* mem)
174 #else
175 mem2chunk_check(mem) Void_t* mem;
176 #endif
177 {
178   mchunkptr p;
179   INTERNAL_SIZE_T sz, c;
180   unsigned char magic;
181
182   p = mem2chunk(mem);
183   if(!aligned_OK(p)) return NULL;
184   if( (char*)p>=mp_.sbrk_base &&
185       (char*)p<(mp_.sbrk_base+main_arena.system_mem) ) {
186     /* Must be a chunk in conventional heap memory. */
187     if(chunk_is_mmapped(p) ||
188        ( (sz = chunksize(p)),
189          ((char*)p + sz)>=(mp_.sbrk_base+main_arena.system_mem) ) ||
190        sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ||
191        ( !prev_inuse(p) && (p->prev_size&MALLOC_ALIGN_MASK ||
192                             (long)prev_chunk(p)<(long)mp_.sbrk_base ||
193                             next_chunk(prev_chunk(p))!=p) ))
194       return NULL;
195     magic = MAGICBYTE(p);
196     for(sz += SIZE_SZ-1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
197       if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
198     }
199     ((unsigned char*)p)[sz] ^= 0xFF;
200   } else {
201     unsigned long offset, page_mask = malloc_getpagesize-1;
202
203     /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
204        alignment relative to the beginning of a page.  Check this
205        first. */
206     offset = (unsigned long)mem & page_mask;
207     if((offset!=MALLOC_ALIGNMENT && offset!=0 && offset!=0x10 &&
208         offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
209         offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
210         offset<0x2000) ||
211        !chunk_is_mmapped(p) || (p->size & PREV_INUSE) ||
212        ( (((unsigned long)p - p->prev_size) & page_mask) != 0 ) ||
213        ( (sz = chunksize(p)), ((p->prev_size + sz) & page_mask) != 0 ) )
214       return NULL;
215     magic = MAGICBYTE(p);
216     for(sz -= 1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
217       if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
218     }
219     ((unsigned char*)p)[sz] ^= 0xFF;
220   }
221   return p;
222 }
223
224 /* Check for corruption of the top chunk, and try to recover if
225    necessary. */
226
227 static int
228 internal_function
229 #if __STD_C
230 top_check(void)
231 #else
232 top_check()
233 #endif
234 {
235   mchunkptr t = top(&main_arena);
236   char* brk, * new_brk;
237   INTERNAL_SIZE_T front_misalign, sbrk_size;
238   unsigned long pagesz = malloc_getpagesize;
239
240   if((char*)t + chunksize(t) == mp_.sbrk_base + main_arena.system_mem ||
241      t == initial_top(&main_arena)) return 0;
242
243   if(check_action & 1)
244     fprintf(stderr, "malloc: top chunk is corrupt\n");
245   if(check_action & 2)
246     abort();
247
248   /* Try to set up a new top chunk. */
249   brk = MORECORE(0);
250   front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
251   if (front_misalign > 0)
252     front_misalign = MALLOC_ALIGNMENT - front_misalign;
253   sbrk_size = front_misalign + mp_.top_pad + MINSIZE;
254   sbrk_size += pagesz - ((unsigned long)(brk + sbrk_size) & (pagesz - 1));
255   new_brk = (char*)(MORECORE (sbrk_size));
256   if (new_brk == (char*)(MORECORE_FAILURE)) return -1;
257   /* Call the `morecore' hook if necessary.  */
258   if (__after_morecore_hook)
259     (*__after_morecore_hook) ();
260   main_arena.system_mem = (new_brk - mp_.sbrk_base) + sbrk_size;
261
262   top(&main_arena) = (mchunkptr)(brk + front_misalign);
263   set_head(top(&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
264
265   return 0;
266 }
267
268 static Void_t*
269 #if __STD_C
270 malloc_check(size_t sz, const Void_t *caller)
271 #else
272 malloc_check(sz, caller) size_t sz; const Void_t *caller;
273 #endif
274 {
275   Void_t *victim;
276
277   (void)mutex_lock(&main_arena.mutex);
278   victim = (top_check() >= 0) ? _int_malloc(&main_arena, sz+1) : NULL;
279   (void)mutex_unlock(&main_arena.mutex);
280   return mem2mem_check(victim, sz);
281 }
282
283 static void
284 #if __STD_C
285 free_check(Void_t* mem, const Void_t *caller)
286 #else
287 free_check(mem, caller) Void_t* mem; const Void_t *caller;
288 #endif
289 {
290   mchunkptr p;
291
292   if(!mem) return;
293   (void)mutex_lock(&main_arena.mutex);
294   p = mem2chunk_check(mem);
295   if(!p) {
296     (void)mutex_unlock(&main_arena.mutex);
297     if(check_action & 1)
298       fprintf(stderr, "free(): invalid pointer %p!\n", mem);
299     if(check_action & 2)
300       abort();
301     return;
302   }
303 #if HAVE_MMAP
304   if (chunk_is_mmapped(p)) {
305     (void)mutex_unlock(&main_arena.mutex);
306     munmap_chunk(p);
307     return;
308   }
309 #endif
310 #if 0 /* Erase freed memory. */
311   memset(mem, 0, chunksize(p) - (SIZE_SZ+1));
312 #endif
313   _int_free(&main_arena, mem);
314   (void)mutex_unlock(&main_arena.mutex);
315 }
316
317 static Void_t*
318 #if __STD_C
319 realloc_check(Void_t* oldmem, size_t bytes, const Void_t *caller)
320 #else
321 realloc_check(oldmem, bytes, caller)
322      Void_t* oldmem; size_t bytes; const Void_t *caller;
323 #endif
324 {
325   mchunkptr oldp, newp = 0;
326   INTERNAL_SIZE_T nb, oldsize;
327   Void_t* newmem = 0;
328
329   if (oldmem == 0) return malloc_check(bytes, NULL);
330   (void)mutex_lock(&main_arena.mutex);
331   oldp = mem2chunk_check(oldmem);
332   (void)mutex_unlock(&main_arena.mutex);
333   if(!oldp) {
334     if(check_action & 1)
335       fprintf(stderr, "realloc(): invalid pointer %p!\n", oldmem);
336     if(check_action & 2)
337       abort();
338     return malloc_check(bytes, NULL);
339   }
340   oldsize = chunksize(oldp);
341
342   checked_request2size(bytes+1, nb);
343   (void)mutex_lock(&main_arena.mutex);
344
345 #if HAVE_MMAP
346   if (chunk_is_mmapped(oldp)) {
347 #if HAVE_MREMAP
348     newp = mremap_chunk(oldp, nb);
349     if(!newp) {
350 #endif
351       /* Note the extra SIZE_SZ overhead. */
352       if(oldsize - SIZE_SZ >= nb)
353         newmem = oldmem; /* do nothing */
354       else {
355         /* Must alloc, copy, free. */
356         if (top_check() >= 0)
357           newmem = _int_malloc(&main_arena, bytes+1);
358         if (newmem) {
359           MALLOC_COPY(BOUNDED_N(newmem, bytes+1), oldmem, oldsize - 2*SIZE_SZ);
360           munmap_chunk(oldp);
361         }
362       }
363 #if HAVE_MREMAP
364     }
365 #endif
366   } else {
367 #endif /* HAVE_MMAP */
368     if (top_check() >= 0)
369       newmem = _int_realloc(&main_arena, oldmem, bytes+1);
370 #if 0 /* Erase freed memory. */
371     if(newmem)
372       newp = mem2chunk(newmem);
373     nb = chunksize(newp);
374     if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
375       memset((char*)oldmem + 2*sizeof(mbinptr), 0,
376              oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
377     } else if(nb > oldsize+SIZE_SZ) {
378       memset((char*)BOUNDED_N(chunk2mem(newp), bytes) + oldsize,
379              0, nb - (oldsize+SIZE_SZ));
380     }
381 #endif
382 #if HAVE_MMAP
383   }
384 #endif
385   (void)mutex_unlock(&main_arena.mutex);
386
387   return mem2mem_check(newmem, bytes);
388 }
389
390 static Void_t*
391 #if __STD_C
392 memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
393 #else
394 memalign_check(alignment, bytes, caller)
395      size_t alignment; size_t bytes; const Void_t *caller;
396 #endif
397 {
398   INTERNAL_SIZE_T nb;
399   Void_t* mem;
400
401   if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
402   if (alignment <  MINSIZE) alignment = MINSIZE;
403
404   checked_request2size(bytes+1, nb);
405   (void)mutex_lock(&main_arena.mutex);
406   mem = (top_check() >= 0) ? _int_memalign(&main_arena, alignment, bytes+1) :
407     NULL;
408   (void)mutex_unlock(&main_arena.mutex);
409   return mem2mem_check(mem, bytes);
410 }
411
412 #ifndef NO_THREADS
413
414 /* The following hooks are used when the global initialization in
415    ptmalloc_init() hasn't completed yet. */
416
417 static Void_t*
418 #if __STD_C
419 malloc_starter(size_t sz, const Void_t *caller)
420 #else
421 malloc_starter(sz, caller) size_t sz; const Void_t *caller;
422 #endif
423 {
424   Void_t* victim;
425
426   victim = _int_malloc(&main_arena, sz);
427
428   return victim ? BOUNDED_N(victim, sz) : 0;
429 }
430
431 static void
432 #if __STD_C
433 free_starter(Void_t* mem, const Void_t *caller)
434 #else
435 free_starter(mem, caller) Void_t* mem; const Void_t *caller;
436 #endif
437 {
438   mchunkptr p;
439
440   if(!mem) return;
441   p = mem2chunk(mem);
442 #if HAVE_MMAP
443   if (chunk_is_mmapped(p)) {
444     munmap_chunk(p);
445     return;
446   }
447 #endif
448   _int_free(&main_arena, mem);
449 }
450
451 #endif /* NO_THREADS */
452
453
454 /* Get/set state: malloc_get_state() records the current state of all
455    malloc variables (_except_ for the actual heap contents and `hook'
456    function pointers) in a system dependent, opaque data structure.
457    This data structure is dynamically allocated and can be free()d
458    after use.  malloc_set_state() restores the state of all malloc
459    variables to the previously obtained state.  This is especially
460    useful when using this malloc as part of a shared library, and when
461    the heap contents are saved/restored via some other method.  The
462    primary example for this is GNU Emacs with its `dumping' procedure.
463    `Hook' function pointers are never saved or restored by these
464    functions, with two exceptions: If malloc checking was in use when
465    malloc_get_state() was called, then malloc_set_state() calls
466    __malloc_check_init() if possible; if malloc checking was not in
467    use in the recorded state but the user requested malloc checking,
468    then the hooks are reset to 0.  */
469
470 #define MALLOC_STATE_MAGIC   0x444c4541l
471 #define MALLOC_STATE_VERSION (0*0x100l + 2l) /* major*0x100 + minor */
472
473 struct malloc_save_state {
474   long          magic;
475   long          version;
476   mbinptr       av[NBINS * 2 + 2];
477   char*         sbrk_base;
478   int           sbrked_mem_bytes;
479   unsigned long trim_threshold;
480   unsigned long top_pad;
481   unsigned int  n_mmaps_max;
482   unsigned long mmap_threshold;
483   int           check_action;
484   unsigned long max_sbrked_mem;
485   unsigned long max_total_mem;
486   unsigned int  n_mmaps;
487   unsigned int  max_n_mmaps;
488   unsigned long mmapped_mem;
489   unsigned long max_mmapped_mem;
490   int           using_malloc_checking;
491 };
492
493 Void_t*
494 public_gET_STATe(void)
495 {
496   struct malloc_save_state* ms;
497   int i;
498   mbinptr b;
499
500   ms = (struct malloc_save_state*)public_mALLOc(sizeof(*ms));
501   if (!ms)
502     return 0;
503   (void)mutex_lock(&main_arena.mutex);
504   malloc_consolidate(&main_arena);
505   ms->magic = MALLOC_STATE_MAGIC;
506   ms->version = MALLOC_STATE_VERSION;
507   ms->av[0] = 0;
508   ms->av[1] = 0; /* used to be binblocks, now no longer used */
509   ms->av[2] = top(&main_arena);
510   ms->av[3] = 0; /* used to be undefined */
511   for(i=1; i<NBINS; i++) {
512     b = bin_at(&main_arena, i);
513     if(first(b) == b)
514       ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin */
515     else {
516       ms->av[2*i+2] = first(b);
517       ms->av[2*i+3] = last(b);
518     }
519   }
520   ms->sbrk_base = mp_.sbrk_base;
521   ms->sbrked_mem_bytes = main_arena.system_mem;
522   ms->trim_threshold = mp_.trim_threshold;
523   ms->top_pad = mp_.top_pad;
524   ms->n_mmaps_max = mp_.n_mmaps_max;
525   ms->mmap_threshold = mp_.mmap_threshold;
526   ms->check_action = check_action;
527   ms->max_sbrked_mem = main_arena.max_system_mem;
528 #ifdef NO_THREADS
529   ms->max_total_mem = max_total_mem;
530 #else
531   ms->max_total_mem = 0;
532 #endif
533   ms->n_mmaps = mp_.n_mmaps;
534   ms->max_n_mmaps = mp_.max_n_mmaps;
535   ms->mmapped_mem = mp_.mmapped_mem;
536   ms->max_mmapped_mem = mp_.max_mmapped_mem;
537   ms->using_malloc_checking = using_malloc_checking;
538   (void)mutex_unlock(&main_arena.mutex);
539   return (Void_t*)ms;
540 }
541
542 int
543 public_sET_STATe(Void_t* msptr)
544 {
545   struct malloc_save_state* ms = (struct malloc_save_state*)msptr;
546   int i;
547   mbinptr b;
548
549   disallow_malloc_check = 1;
550   ptmalloc_init();
551   if(ms->magic != MALLOC_STATE_MAGIC) return -1;
552   /* Must fail if the major version is too high. */
553   if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
554   (void)mutex_lock(&main_arena.mutex);
555   /* There are no fastchunks.  */
556   clear_fastchunks(&main_arena);
557   set_max_fast(&main_arena, DEFAULT_MXFAST);
558   for (i=0; i<NFASTBINS; ++i)
559     main_arena.fastbins[i] = 0;
560   for (i=0; i<BINMAPSIZE; ++i)
561     main_arena.binmap[i] = 0;
562   top(&main_arena) = ms->av[2];
563   main_arena.last_remainder = 0;
564   for(i=1; i<NBINS; i++) {
565     b = bin_at(&main_arena, i);
566     if(ms->av[2*i+2] == 0) {
567       assert(ms->av[2*i+3] == 0);
568       first(b) = last(b) = b;
569     } else {
570       if(i<NSMALLBINS || (largebin_index(chunksize(ms->av[2*i+2]))==i &&
571                           largebin_index(chunksize(ms->av[2*i+3]))==i)) {
572         first(b) = ms->av[2*i+2];
573         last(b) = ms->av[2*i+3];
574         /* Make sure the links to the bins within the heap are correct.  */
575         first(b)->bk = b;
576         last(b)->fd = b;
577         /* Set bit in binblocks.  */
578         mark_bin(&main_arena, i);
579       } else {
580         /* Oops, index computation from chunksize must have changed.
581            Link the whole list into unsorted_chunks.  */
582         first(b) = last(b) = b;
583         b = unsorted_chunks(&main_arena);
584         ms->av[2*i+2]->bk = b;
585         ms->av[2*i+3]->fd = b->fd;
586         b->fd->bk = ms->av[2*i+3];
587         b->fd = ms->av[2*i+2];
588       }
589     }
590   }
591   mp_.sbrk_base = ms->sbrk_base;
592   main_arena.system_mem = ms->sbrked_mem_bytes;
593   mp_.trim_threshold = ms->trim_threshold;
594   mp_.top_pad = ms->top_pad;
595   mp_.n_mmaps_max = ms->n_mmaps_max;
596   mp_.mmap_threshold = ms->mmap_threshold;
597   check_action = ms->check_action;
598   main_arena.max_system_mem = ms->max_sbrked_mem;
599 #ifdef NO_THREADS
600   mp_.max_total_mem = ms->max_total_mem;
601 #endif
602   mp_.n_mmaps = ms->n_mmaps;
603   mp_.max_n_mmaps = ms->max_n_mmaps;
604   mp_.mmapped_mem = ms->mmapped_mem;
605   mp_.max_mmapped_mem = ms->max_mmapped_mem;
606   /* add version-dependent code here */
607   if (ms->version >= 1) {
608     /* Check whether it is safe to enable malloc checking, or whether
609        it is necessary to disable it.  */
610     if (ms->using_malloc_checking && !using_malloc_checking &&
611         !disallow_malloc_check)
612       __malloc_check_init ();
613     else if (!ms->using_malloc_checking && using_malloc_checking) {
614       __malloc_hook = 0;
615       __free_hook = 0;
616       __realloc_hook = 0;
617       __memalign_hook = 0;
618       using_malloc_checking = 0;
619     }
620   }
621   check_malloc_state(&main_arena);
622
623   (void)mutex_unlock(&main_arena.mutex);
624   return 0;
625 }
626
627 /*
628  * Local variables:
629  * c-basic-offset: 2
630  * End:
631  */