]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - malloc/hooks.c
[powerpc] No need to enter "Ignore Exceptions Mode"
[thirdparty/glibc.git] / malloc / hooks.c
index 2dc5cf55e5a9f7fcc234ca12764e0a236ad2e8fe..3cb432b97fe4e458ce0a32c5501000a8eabe03c3 100644 (file)
@@ -1,38 +1,21 @@
 /* Malloc implementation for multiple threads without lock contention.
-   Copyright (C) 2001,02 Free Software Foundation, Inc.
+   Copyright (C) 2001-2019 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
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 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
-#ifndef _LIBC
-#define weak_variable /**/
-#else
-/* In GNU libc we want the hook variables to be weak definitions to
-   avoid a problem with Emacs.  */
-#define weak_variable weak_function
-#endif
-#endif
+   Lesser General Public License for more details.
 
-#ifndef DEFAULT_CHECK_ACTION
-#define DEFAULT_CHECK_ACTION 1
-#endif
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <https://www.gnu.org/licenses/>.  */
 
 /* What to do if the standard debugging hooks are in place and a
    corrupt pointer is detected: do nothing (0), print an error message
 /* 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
+static void *
+malloc_hook_ini (size_t sz, const void *caller)
 {
   __malloc_hook = NULL;
-  ptmalloc_init();
-  return public_mALLOc(sz);
+  ptmalloc_init ();
+  return __libc_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
+static void *
+realloc_hook_ini (void *ptr, size_t sz, const void *caller)
 {
   __malloc_hook = NULL;
   __realloc_hook = NULL;
-  ptmalloc_init();
-  return public_rEALLOc(ptr, sz);
+  ptmalloc_init ();
+  return __libc_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
+static void *
+memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
 {
   __memalign_hook = NULL;
-  ptmalloc_init();
-  return public_mEMALIGn(alignment, sz);
+  ptmalloc_init ();
+  return __libc_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()
+__malloc_check_init (void)
 {
-  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
@@ -137,498 +68,446 @@ __malloc_check_init()
    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 )
+static unsigned char
+magicbyte (const void *p)
+{
+  unsigned char magic;
+
+  magic = (((uintptr_t) p >> 3) ^ ((uintptr_t) p >> 11)) & 0xFF;
+  /* Do not return 1.  See the comment in mem2mem_check().  */
+  if (magic == 1)
+    ++magic;
+  return magic;
+}
+
+
+/* Visualize the chunk as being partitioned into blocks of 255 bytes from the
+   highest address of the chunk, downwards.  The end of each block tells
+   us the size of that block, up to the actual size of the requested
+   memory.  Our magic byte is right at the end of the requested size, so we
+   must reach it with this iteration, otherwise we have witnessed a memory
+   corruption.  */
+static size_t
+malloc_check_get_size (mchunkptr p)
+{
+  size_t size;
+  unsigned char c;
+  unsigned char magic = magicbyte (p);
+
+  assert (using_malloc_checking == 1);
+
+  for (size = chunksize (p) - 1 + (chunk_is_mmapped (p) ? 0 : SIZE_SZ);
+       (c = ((unsigned char *) p)[size]) != magic;
+       size -= c)
+    {
+      if (c <= 0 || size < (c + 2 * SIZE_SZ))
+       malloc_printerr ("malloc_check_get_size: memory corruption");
+    }
+
+  /* chunk2mem size.  */
+  return size - 2 * SIZE_SZ;
+}
 
 /* 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
+   into a user pointer with requested size req_sz. */
+
+static void *
+mem2mem_check (void *ptr, size_t req_sz)
 {
   mchunkptr p;
-  unsigned char* m_ptr = (unsigned char*)BOUNDED_N(ptr, sz);
-  size_t i;
+  unsigned char *m_ptr = ptr;
+  size_t max_sz, block_sz, i;
+  unsigned char magic;
 
   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;
+
+  p = mem2chunk (ptr);
+  magic = magicbyte (p);
+  max_sz = chunksize (p) - 2 * SIZE_SZ;
+  if (!chunk_is_mmapped (p))
+    max_sz += SIZE_SZ;
+  for (i = max_sz - 1; i > req_sz; i -= block_sz)
+    {
+      block_sz = MIN (i - req_sz, 0xff);
+      /* Don't allow the magic byte to appear in the chain of length bytes.
+         For the following to work, magicbyte cannot return 0x01.  */
+      if (block_sz == magic)
+        --block_sz;
+
+      m_ptr[i] = block_sz;
     }
-    m_ptr[i] = 0xFF;
-  }
-  m_ptr[sz] = MAGICBYTE(p);
-  return (Void_t*)m_ptr;
+  m_ptr[req_sz] = magic;
+  return (void *) 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
+mem2chunk_check (void *mem, unsigned char **magic_p)
 {
   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;
+  if (!aligned_OK (mem))
+    return NULL;
+
+  p = mem2chunk (mem);
+  sz = chunksize (p);
+  magic = magicbyte (p);
+  if (!chunk_is_mmapped (p))
+    {
+      /* Must be a chunk in conventional heap memory. */
+      int contig = contiguous (&main_arena);
+      if ((contig &&
+           ((char *) p < mp_.sbrk_base ||
+            ((char *) p + sz) >= (mp_.sbrk_base + main_arena.system_mem))) ||
+          sz < MINSIZE || sz & MALLOC_ALIGN_MASK || !inuse (p) ||
+          (!prev_inuse (p) && ((prev_size (p) & MALLOC_ALIGN_MASK) != 0 ||
+                               (contig && (char *) prev_chunk (p) < mp_.sbrk_base) ||
+                               next_chunk (prev_chunk (p)) != p)))
+        return NULL;
+
+      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;
+  else
+    {
+      unsigned long offset, page_mask = GLRO (dl_pagesize) - 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) || prev_inuse (p) ||
+          ((((unsigned long) p - prev_size (p)) & page_mask) != 0) ||
+          ((prev_size (p) + sz) & page_mask) != 0)
+        return NULL;
+
+      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;
-  }
+  ((unsigned char *) p)[sz] ^= 0xFF;
+  if (magic_p)
+    *magic_p = (unsigned char *) p + sz;
   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
+/* Check for corruption of the top chunk.  */
+static void
+top_check (void)
 {
-  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);
+  mchunkptr t = top (&main_arena);
+
+  if (t == initial_top (&main_arena) ||
+      (!chunk_is_mmapped (t) &&
+       chunksize (t) >= MINSIZE &&
+       prev_inuse (t) &&
+       (!contiguous (&main_arena) ||
+        (char *) t + chunksize (t) == mp_.sbrk_base + main_arena.system_mem)))
+    return;
 
-  return 0;
+  malloc_printerr ("malloc: top chunk is corrupt");
 }
 
-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
+static void *
+malloc_check (size_t sz, const void *caller)
 {
-  Void_t *victim;
+  void *victim;
+  size_t nb;
+
+  if (__builtin_add_overflow (sz, 1, &nb))
+    {
+      __set_errno (ENOMEM);
+      return NULL;
+    }
 
-  (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);
+  __libc_lock_lock (main_arena.mutex);
+  top_check ();
+  victim = _int_malloc (&main_arena, nb);
+  __libc_lock_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
+free_check (void *mem, const void *caller)
 {
   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();
+  if (!mem)
     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);
+
+  __libc_lock_lock (main_arena.mutex);
+  p = mem2chunk_check (mem, NULL);
+  if (!p)
+    malloc_printerr ("free(): invalid pointer");
+  if (chunk_is_mmapped (p))
+    {
+      __libc_lock_unlock (main_arena.mutex);
+      munmap_chunk (p);
+      return;
+    }
+  _int_free (&main_arena, p, 1);
+  __libc_lock_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
+static void *
+realloc_check (void *oldmem, size_t bytes, const void *caller)
 {
-  mchunkptr oldp;
-  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);
+  INTERNAL_SIZE_T nb;
+  void *newmem = 0;
+  unsigned char *magic_p;
+  size_t rb;
 
-  checked_request2size(bytes+1, nb);
-  (void)mutex_lock(&main_arena.mutex);
+  if (__builtin_add_overflow (bytes, 1, &rb))
+    {
+      __set_errno (ENOMEM);
+      return NULL;
+    }
+  if (oldmem == 0)
+    return malloc_check (bytes, NULL);
+
+  if (bytes == 0)
+    {
+      free_check (oldmem, NULL);
+      return NULL;
+    }
+  __libc_lock_lock (main_arena.mutex);
+  const mchunkptr oldp = mem2chunk_check (oldmem, &magic_p);
+  __libc_lock_unlock (main_arena.mutex);
+  if (!oldp)
+    malloc_printerr ("realloc(): invalid pointer");
+  const INTERNAL_SIZE_T oldsize = chunksize (oldp);
+
+  if (!checked_request2size (rb, &nb))
+    goto invert;
+
+  __libc_lock_lock (main_arena.mutex);
 
-#if HAVE_MMAP
-  if (chunk_is_mmapped(oldp)) {
+  if (chunk_is_mmapped (oldp))
+    {
 #if HAVE_MREMAP
-    mchunkptr newp = mremap_chunk(oldp, nb);
-    if(!newp) {
+      mchunkptr newp = mremap_chunk (oldp, nb);
+      if (newp)
+        newmem = chunk2mem (newp);
+      else
 #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);
-        }
+      {
+        /* Note the extra SIZE_SZ overhead. */
+        if (oldsize - SIZE_SZ >= nb)
+          newmem = oldmem; /* do nothing */
+        else
+          {
+            /* Must alloc, copy, free. */
+           top_check ();
+           newmem = _int_malloc (&main_arena, rb);
+            if (newmem)
+              {
+                memcpy (newmem, 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));
+  else
+    {
+      top_check ();
+      newmem = _int_realloc (&main_arena, oldp, oldsize, nb);
     }
-#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;
+  DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+  /* GCC 7 warns about magic_p may be used uninitialized.  But we never
+     reach here if magic_p is uninitialized.  */
+  DIAG_IGNORE_NEEDS_COMMENT (7, "-Wmaybe-uninitialized");
 #endif
-{
-  INTERNAL_SIZE_T nb;
-  Void_t* mem;
+  /* mem2chunk_check changed the magic byte in the old chunk.
+     If newmem is NULL, then the old chunk will still be used though,
+     so we need to invert that change here.  */
+invert:
+  if (newmem == NULL)
+    *magic_p ^= 0xFF;
+  DIAG_POP_NEEDS_COMMENT;
 
-  if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
-  if (alignment <  MINSIZE) alignment = MINSIZE;
+  __libc_lock_unlock (main_arena.mutex);
 
-  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);
+  return mem2mem_check (newmem, bytes);
 }
 
-#ifndef NO_THREADS
+static void *
+memalign_check (size_t alignment, size_t bytes, const void *caller)
+{
+  void *mem;
 
-/* The following hooks are used when the global initialization in
-   ptmalloc_init() hasn't completed yet. */
+  if (alignment <= MALLOC_ALIGNMENT)
+    return malloc_check (bytes, NULL);
 
-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;
+  if (alignment < MINSIZE)
+    alignment = MINSIZE;
 
-  victim = _int_malloc(&main_arena, sz);
+  /* If the alignment is greater than SIZE_MAX / 2 + 1 it cannot be a
+     power of 2 and will cause overflow in the check below.  */
+  if (alignment > SIZE_MAX / 2 + 1)
+    {
+      __set_errno (EINVAL);
+      return 0;
+    }
 
-  return victim ? BOUNDED_N(victim, sz) : 0;
-}
+  /* Check for overflow.  */
+  if (bytes > SIZE_MAX - alignment - MINSIZE)
+    {
+      __set_errno (ENOMEM);
+      return 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;
+  /* Make sure alignment is power of 2.  */
+  if (!powerof2 (alignment))
+    {
+      size_t a = MALLOC_ALIGNMENT * 2;
+      while (a < alignment)
+        a <<= 1;
+      alignment = a;
+    }
 
-  if(!mem) return;
-  p = mem2chunk(mem);
-#if HAVE_MMAP
-  if (chunk_is_mmapped(p)) {
-    munmap_chunk(p);
-    return;
-  }
-#endif
-  _int_free(&main_arena, mem);
+  __libc_lock_lock (main_arena.mutex);
+  top_check ();
+  mem = _int_memalign (&main_arena, alignment, bytes + 1);
+  __libc_lock_unlock (main_arena.mutex);
+  return mem2mem_check (mem, bytes);
 }
 
-#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.  */
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
+
+/* Support for restoring dumped heaps contained in historic Emacs
+   executables.  The heap saving feature (malloc_get_state) is no
+   longer implemented in this version of glibc, but we have a heap
+   rewriter in malloc_set_state which transforms the heap into a
+   version compatible with current malloc.  */
 
 #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;
+#define MALLOC_STATE_VERSION (0 * 0x100l + 5l) /* 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 int n_mmaps_max;
   unsigned long mmap_threshold;
-  int           check_action;
+  int check_action;
   unsigned long max_sbrked_mem;
-  unsigned long max_total_mem;
-  unsigned int  n_mmaps;
-  unsigned int  max_n_mmaps;
+  unsigned long max_total_mem; /* Always 0, for backwards compatibility.  */
+  unsigned int n_mmaps;
+  unsigned int max_n_mmaps;
   unsigned long mmapped_mem;
   unsigned long max_mmapped_mem;
-  int           using_malloc_checking;
+  int using_malloc_checking;
+  unsigned long max_fast;
+  unsigned long arena_test;
+  unsigned long arena_max;
+  unsigned long narenas;
 };
 
-Void_t*
-public_gET_STATe(void)
+/* Dummy implementation which always fails.  We need to provide this
+   symbol so that existing Emacs binaries continue to work with
+   BIND_NOW.  */
+void *
+attribute_compat_text_section
+malloc_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;
+  __set_errno (ENOSYS);
+  return NULL;
 }
+compat_symbol (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
 
 int
-public_sET_STATe(Void_t* msptr)
+attribute_compat_text_section
+malloc_set_state (void *msptr)
 {
-  struct malloc_save_state* ms = (struct malloc_save_state*)msptr;
-  int i;
-  mbinptr b;
+  struct malloc_save_state *ms = (struct malloc_save_state *) msptr;
+
+  if (ms->magic != MALLOC_STATE_MAGIC)
+    return -1;
 
-  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];
-      }
-    }
+  if ((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl))
+    return -2;
+
+  /* We do not need to perform locking here because malloc_set_state
+     must be called before the first call into the malloc subsytem
+     (usually via __malloc_initialize_hook).  pthread_create always
+     calls calloc and thus must be called only afterwards, so there
+     cannot be more than one thread when we reach this point.  */
+
+  /* Disable the malloc hooks (and malloc checking).  */
+  __malloc_hook = NULL;
+  __realloc_hook = NULL;
+  __free_hook = NULL;
+  __memalign_hook = NULL;
+  using_malloc_checking = 0;
+
+  /* Patch the dumped heap.  We no longer try to integrate into the
+     existing heap.  Instead, we mark the existing chunks as mmapped.
+     Together with the update to dumped_main_arena_start and
+     dumped_main_arena_end, realloc and free will recognize these
+     chunks as dumped fake mmapped chunks and never free them.  */
+
+  /* Find the chunk with the lowest address with the heap.  */
+  mchunkptr chunk = NULL;
+  {
+    size_t *candidate = (size_t *) ms->sbrk_base;
+    size_t *end = (size_t *) (ms->sbrk_base + ms->sbrked_mem_bytes);
+    while (candidate < end)
+      if (*candidate != 0)
+       {
+         chunk = mem2chunk ((void *) (candidate + 1));
+         break;
+       }
+      else
+       ++candidate;
   }
-  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;
+  if (chunk == NULL)
+    return 0;
+
+  /* Iterate over the dumped heap and patch the chunks so that they
+     are treated as fake mmapped chunks.  */
+  mchunkptr top = ms->av[2];
+  while (chunk < top)
+    {
+      if (inuse (chunk))
+       {
+         /* Mark chunk as mmapped, to trigger the fallback path.  */
+         size_t size = chunksize (chunk);
+         set_head (chunk, size | IS_MMAPPED);
+       }
+      chunk = next_chunk (chunk);
     }
-  }
-  check_malloc_state(&main_arena);
 
-  (void)mutex_unlock(&main_arena.mutex);
+  /* The dumped fake mmapped chunks all lie in this address range.  */
+  dumped_main_arena_start = (mchunkptr) ms->sbrk_base;
+  dumped_main_arena_end = top;
+
   return 0;
 }
+compat_symbol (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
+
+#endif /* SHLIB_COMPAT */
 
 /*
  * Local variables: