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