(realloc_check): If mremap succeeds actually use the result.
[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 #endif
357       /* Note the extra SIZE_SZ overhead. */
358       if(oldsize - SIZE_SZ >= nb)
359         newmem = oldmem; /* do nothing */
360       else {
361         /* Must alloc, copy, free. */
362         if (top_check() >= 0)
363           newmem = _int_malloc(&main_arena, bytes+1);
364         if (newmem) {
365           MALLOC_COPY(BOUNDED_N(newmem, bytes+1), oldmem, oldsize - 2*SIZE_SZ);
366           munmap_chunk(oldp);
367         }
368       }
369 #if HAVE_MREMAP
370     } else {
371       newmem = chunk2mem(newp);
372     }
373 #endif
374   } else {
375 #endif /* HAVE_MMAP */
376     if (top_check() >= 0)
377       newmem = _int_realloc(&main_arena, oldmem, bytes+1);
378 #if 0 /* Erase freed memory. */
379     if(newmem)
380       newp = mem2chunk(newmem);
381     nb = chunksize(newp);
382     if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
383       memset((char*)oldmem + 2*sizeof(mbinptr), 0,
384              oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
385     } else if(nb > oldsize+SIZE_SZ) {
386       memset((char*)BOUNDED_N(chunk2mem(newp), bytes) + oldsize,
387              0, nb - (oldsize+SIZE_SZ));
388     }
389 #endif
390 #if HAVE_MMAP
391   }
392 #endif
393   (void)mutex_unlock(&main_arena.mutex);
394
395   return mem2mem_check(newmem, bytes);
396 }
397
398 static Void_t*
399 #if __STD_C
400 memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
401 #else
402 memalign_check(alignment, bytes, caller)
403      size_t alignment; size_t bytes; const Void_t *caller;
404 #endif
405 {
406   INTERNAL_SIZE_T nb;
407   Void_t* mem;
408
409   if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
410   if (alignment <  MINSIZE) alignment = MINSIZE;
411
412   checked_request2size(bytes+1, nb);
413   (void)mutex_lock(&main_arena.mutex);
414   mem = (top_check() >= 0) ? _int_memalign(&main_arena, alignment, bytes+1) :
415     NULL;
416   (void)mutex_unlock(&main_arena.mutex);
417   return mem2mem_check(mem, bytes);
418 }
419
420 #ifndef NO_THREADS
421
422 /* The following hooks are used when the global initialization in
423    ptmalloc_init() hasn't completed yet. */
424
425 static Void_t*
426 #if __STD_C
427 malloc_starter(size_t sz, const Void_t *caller)
428 #else
429 malloc_starter(sz, caller) size_t sz; const Void_t *caller;
430 #endif
431 {
432   Void_t* victim;
433
434   victim = _int_malloc(&main_arena, sz);
435
436   return victim ? BOUNDED_N(victim, sz) : 0;
437 }
438
439 static void
440 #if __STD_C
441 free_starter(Void_t* mem, const Void_t *caller)
442 #else
443 free_starter(mem, caller) Void_t* mem; const Void_t *caller;
444 #endif
445 {
446   mchunkptr p;
447
448   if(!mem) return;
449   p = mem2chunk(mem);
450 #if HAVE_MMAP
451   if (chunk_is_mmapped(p)) {
452     munmap_chunk(p);
453     return;
454   }
455 #endif
456   _int_free(&main_arena, mem);
457 }
458
459 #endif /* NO_THREADS */
460
461
462 /* Get/set state: malloc_get_state() records the current state of all
463    malloc variables (_except_ for the actual heap contents and `hook'
464    function pointers) in a system dependent, opaque data structure.
465    This data structure is dynamically allocated and can be free()d
466    after use.  malloc_set_state() restores the state of all malloc
467    variables to the previously obtained state.  This is especially
468    useful when using this malloc as part of a shared library, and when
469    the heap contents are saved/restored via some other method.  The
470    primary example for this is GNU Emacs with its `dumping' procedure.
471    `Hook' function pointers are never saved or restored by these
472    functions, with two exceptions: If malloc checking was in use when
473    malloc_get_state() was called, then malloc_set_state() calls
474    __malloc_check_init() if possible; if malloc checking was not in
475    use in the recorded state but the user requested malloc checking,
476    then the hooks are reset to 0.  */
477
478 #define MALLOC_STATE_MAGIC   0x444c4541l
479 #define MALLOC_STATE_VERSION (0*0x100l + 2l) /* major*0x100 + minor */
480
481 struct malloc_save_state {
482   long          magic;
483   long          version;
484   mbinptr       av[NBINS * 2 + 2];
485   char*         sbrk_base;
486   int           sbrked_mem_bytes;
487   unsigned long trim_threshold;
488   unsigned long top_pad;
489   unsigned int  n_mmaps_max;
490   unsigned long mmap_threshold;
491   int           check_action;
492   unsigned long max_sbrked_mem;
493   unsigned long max_total_mem;
494   unsigned int  n_mmaps;
495   unsigned int  max_n_mmaps;
496   unsigned long mmapped_mem;
497   unsigned long max_mmapped_mem;
498   int           using_malloc_checking;
499 };
500
501 Void_t*
502 public_gET_STATe(void)
503 {
504   struct malloc_save_state* ms;
505   int i;
506   mbinptr b;
507
508   ms = (struct malloc_save_state*)public_mALLOc(sizeof(*ms));
509   if (!ms)
510     return 0;
511   (void)mutex_lock(&main_arena.mutex);
512   malloc_consolidate(&main_arena);
513   ms->magic = MALLOC_STATE_MAGIC;
514   ms->version = MALLOC_STATE_VERSION;
515   ms->av[0] = 0;
516   ms->av[1] = 0; /* used to be binblocks, now no longer used */
517   ms->av[2] = top(&main_arena);
518   ms->av[3] = 0; /* used to be undefined */
519   for(i=1; i<NBINS; i++) {
520     b = bin_at(&main_arena, i);
521     if(first(b) == b)
522       ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin */
523     else {
524       ms->av[2*i+2] = first(b);
525       ms->av[2*i+3] = last(b);
526     }
527   }
528   ms->sbrk_base = mp_.sbrk_base;
529   ms->sbrked_mem_bytes = main_arena.system_mem;
530   ms->trim_threshold = mp_.trim_threshold;
531   ms->top_pad = mp_.top_pad;
532   ms->n_mmaps_max = mp_.n_mmaps_max;
533   ms->mmap_threshold = mp_.mmap_threshold;
534   ms->check_action = check_action;
535   ms->max_sbrked_mem = main_arena.max_system_mem;
536 #ifdef NO_THREADS
537   ms->max_total_mem = max_total_mem;
538 #else
539   ms->max_total_mem = 0;
540 #endif
541   ms->n_mmaps = mp_.n_mmaps;
542   ms->max_n_mmaps = mp_.max_n_mmaps;
543   ms->mmapped_mem = mp_.mmapped_mem;
544   ms->max_mmapped_mem = mp_.max_mmapped_mem;
545   ms->using_malloc_checking = using_malloc_checking;
546   (void)mutex_unlock(&main_arena.mutex);
547   return (Void_t*)ms;
548 }
549
550 int
551 public_sET_STATe(Void_t* msptr)
552 {
553   struct malloc_save_state* ms = (struct malloc_save_state*)msptr;
554   int i;
555   mbinptr b;
556
557   disallow_malloc_check = 1;
558   ptmalloc_init();
559   if(ms->magic != MALLOC_STATE_MAGIC) return -1;
560   /* Must fail if the major version is too high. */
561   if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
562   (void)mutex_lock(&main_arena.mutex);
563   /* There are no fastchunks.  */
564   clear_fastchunks(&main_arena);
565   set_max_fast(&main_arena, DEFAULT_MXFAST);
566   for (i=0; i<NFASTBINS; ++i)
567     main_arena.fastbins[i] = 0;
568   for (i=0; i<BINMAPSIZE; ++i)
569     main_arena.binmap[i] = 0;
570   top(&main_arena) = ms->av[2];
571   main_arena.last_remainder = 0;
572   for(i=1; i<NBINS; i++) {
573     b = bin_at(&main_arena, i);
574     if(ms->av[2*i+2] == 0) {
575       assert(ms->av[2*i+3] == 0);
576       first(b) = last(b) = b;
577     } else {
578       if(i<NSMALLBINS || (largebin_index(chunksize(ms->av[2*i+2]))==i &&
579                           largebin_index(chunksize(ms->av[2*i+3]))==i)) {
580         first(b) = ms->av[2*i+2];
581         last(b) = ms->av[2*i+3];
582         /* Make sure the links to the bins within the heap are correct.  */
583         first(b)->bk = b;
584         last(b)->fd = b;
585         /* Set bit in binblocks.  */
586         mark_bin(&main_arena, i);
587       } else {
588         /* Oops, index computation from chunksize must have changed.
589            Link the whole list into unsorted_chunks.  */
590         first(b) = last(b) = b;
591         b = unsorted_chunks(&main_arena);
592         ms->av[2*i+2]->bk = b;
593         ms->av[2*i+3]->fd = b->fd;
594         b->fd->bk = ms->av[2*i+3];
595         b->fd = ms->av[2*i+2];
596       }
597     }
598   }
599   mp_.sbrk_base = ms->sbrk_base;
600   main_arena.system_mem = ms->sbrked_mem_bytes;
601   mp_.trim_threshold = ms->trim_threshold;
602   mp_.top_pad = ms->top_pad;
603   mp_.n_mmaps_max = ms->n_mmaps_max;
604   mp_.mmap_threshold = ms->mmap_threshold;
605   check_action = ms->check_action;
606   main_arena.max_system_mem = ms->max_sbrked_mem;
607 #ifdef NO_THREADS
608   mp_.max_total_mem = ms->max_total_mem;
609 #endif
610   mp_.n_mmaps = ms->n_mmaps;
611   mp_.max_n_mmaps = ms->max_n_mmaps;
612   mp_.mmapped_mem = ms->mmapped_mem;
613   mp_.max_mmapped_mem = ms->max_mmapped_mem;
614   /* add version-dependent code here */
615   if (ms->version >= 1) {
616     /* Check whether it is safe to enable malloc checking, or whether
617        it is necessary to disable it.  */
618     if (ms->using_malloc_checking && !using_malloc_checking &&
619         !disallow_malloc_check)
620       __malloc_check_init ();
621     else if (!ms->using_malloc_checking && using_malloc_checking) {
622       __malloc_hook = 0;
623       __free_hook = 0;
624       __realloc_hook = 0;
625       __memalign_hook = 0;
626       using_malloc_checking = 0;
627     }
628   }
629   check_malloc_state(&main_arena);
630
631   (void)mutex_unlock(&main_arena.mutex);
632   return 0;
633 }
634
635 /*
636  * Local variables:
637  * c-basic-offset: 2
638  * End:
639  */