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