diff mbox series

[v4,03/10] Move glibc.malloc.check implementation into its own file

Message ID 20210702113845.3367306-4-siddhesh@sourceware.org
State New
Headers show
Series Remove malloc hooks | expand

Commit Message

Siddhesh Poyarekar July 2, 2021, 11:38 a.m. UTC
Separate the malloc check implementation from the malloc hooks.  They
still use the hooks but are now maintained in a separate file.
---
 malloc/hooks.c        | 371 +---------------------------------------
 malloc/malloc-check.c | 390 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 391 insertions(+), 370 deletions(-)
 create mode 100644 malloc/malloc-check.c

Comments

Carlos O'Donell July 2, 2021, 7:06 p.m. UTC | #1
On 7/2/21 7:38 AM, Siddhesh Poyarekar wrote:
> Separate the malloc check implementation from the malloc hooks.  They
> still use the hooks but are now maintained in a separate file.

LGTM.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> ---
>  malloc/hooks.c        | 371 +---------------------------------------
>  malloc/malloc-check.c | 390 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 391 insertions(+), 370 deletions(-)
>  create mode 100644 malloc/malloc-check.c
> 
> diff --git a/malloc/hooks.c b/malloc/hooks.c
> index 8080c3f40e..57a9b55788 100644
> --- a/malloc/hooks.c
> +++ b/malloc/hooks.c
> @@ -49,376 +49,7 @@ memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
>    return __libc_memalign (alignment, sz);
>  }
>  
> -/* Whether we are using malloc checking.  */
> -static int using_malloc_checking;
> -
> -/* Activate a standard set of debugging hooks. */
> -void
> -__malloc_check_init (void)
> -{
> -  using_malloc_checking = 1;
> -  __malloc_hook = malloc_check;
> -  __free_hook = free_check;
> -  __realloc_hook = realloc_check;
> -  __memalign_hook = memalign_check;
> -}
> -
> -/* When memory is tagged, the checking data is stored in the user part
> -   of the chunk.  We can't rely on the user not having modified the
> -   tags, so fetch the tag at each location before dereferencing
> -   it.  */
> -#define SAFE_CHAR_OFFSET(p,offset) \
> -  ((unsigned char *) tag_at (((unsigned char *) p) + offset))
> -
> -/* A simple, standard set of debugging hooks.  Overhead is `only' one
> -   byte per chunk; still this will catch most cases of double frees or
> -   overruns.  The goal here is to avoid obscure crashes due to invalid
> -   usage, unlike in the MALLOC_DEBUG code. */
> -
> -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 = CHUNK_HDR_SZ + memsize (p) - 1;
> -       (c = *SAFE_CHAR_OFFSET (p, size)) != magic;
> -       size -= c)
> -    {
> -      if (c <= 0 || size < (c + CHUNK_HDR_SZ))
> -	malloc_printerr ("malloc_check_get_size: memory corruption");
> -    }
> -
> -  /* chunk2mem size.  */
> -  return size - CHUNK_HDR_SZ;
> -}
> -
> -/* Instrument a chunk with overrun detector byte(s) and convert it
> -   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 = ptr;
> -  size_t max_sz, block_sz, i;
> -  unsigned char magic;
> -
> -  if (!ptr)
> -    return ptr;
> -
> -  p = mem2chunk (ptr);
> -  magic = magicbyte (p);
> -  max_sz = memsize (p);
> -
> -  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;
> -
> -      *SAFE_CHAR_OFFSET (m_ptr, i) = block_sz;
> -    }
> -  *SAFE_CHAR_OFFSET (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
> -mem2chunk_check (void *mem, unsigned char **magic_p)
> -{
> -  mchunkptr p;
> -  INTERNAL_SIZE_T sz, c;
> -  unsigned char magic;
> -
> -  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 = CHUNK_HDR_SZ + memsize (p) - 1;
> -	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
> -	   sz -= c)
> -        {
> -          if (c == 0 || sz < (c + CHUNK_HDR_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 = CHUNK_HDR_SZ + memsize (p) - 1;
> -	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
> -	   sz -= c)
> -        {
> -          if (c == 0 || sz < (c + CHUNK_HDR_SZ))
> -            return NULL;
> -        }
> -    }
> -
> -  unsigned char* safe_p = SAFE_CHAR_OFFSET (p, sz);
> -  *safe_p ^= 0xFF;
> -  if (magic_p)
> -    *magic_p = safe_p;
> -  return p;
> -}
> -
> -/* Check for corruption of the top chunk.  */
> -static void
> -top_check (void)
> -{
> -  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;
> -
> -  malloc_printerr ("malloc: top chunk is corrupt");
> -}
> -
> -static void *
> -malloc_check (size_t sz, const void *caller)
> -{
> -  void *victim;
> -  size_t nb;
> -
> -  if (__builtin_add_overflow (sz, 1, &nb))
> -    {
> -      __set_errno (ENOMEM);
> -      return NULL;
> -    }
> -
> -  __libc_lock_lock (main_arena.mutex);
> -  top_check ();
> -  victim = _int_malloc (&main_arena, nb);
> -  __libc_lock_unlock (main_arena.mutex);
> -  return mem2mem_check (tag_new_usable (victim), sz);
> -}
> -
> -static void
> -free_check (void *mem, const void *caller)
> -{
> -  mchunkptr p;
> -
> -  if (!mem)
> -    return;
> -
> -  int err = errno;
> -
> -  /* Quickly check that the freed pointer matches the tag for the memory.
> -     This gives a useful double-free detection.  */
> -  if (__glibc_unlikely (mtag_enabled))
> -    *(volatile char *)mem;
> -
> -  __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);
> -    }
> -  else
> -    {
> -      /* Mark the chunk as belonging to the library again.  */
> -      (void)tag_region (chunk2mem (p), memsize (p));
> -      _int_free (&main_arena, p, 1);
> -      __libc_lock_unlock (main_arena.mutex);
> -    }
> -  __set_errno (err);
> -}
> -
> -static void *
> -realloc_check (void *oldmem, size_t bytes, const void *caller)
> -{
> -  INTERNAL_SIZE_T chnb;
> -  void *newmem = 0;
> -  unsigned char *magic_p;
> -  size_t rb;
> -
> -  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;
> -    }
> -
> -  /* Quickly check that the freed pointer matches the tag for the memory.
> -     This gives a useful double-free detection.  */
> -  if (__glibc_unlikely (mtag_enabled))
> -    *(volatile char *)oldmem;
> -
> -  __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, &chnb))
> -    {
> -      __set_errno (ENOMEM);
> -      goto invert;
> -    }
> -
> -  __libc_lock_lock (main_arena.mutex);
> -
> -  if (chunk_is_mmapped (oldp))
> -    {
> -#if HAVE_MREMAP
> -      mchunkptr newp = mremap_chunk (oldp, chnb);
> -      if (newp)
> -        newmem = chunk2mem_tag (newp);
> -      else
> -#endif
> -      {
> -	/* Note the extra SIZE_SZ overhead. */
> -        if (oldsize - SIZE_SZ >= chnb)
> -          newmem = oldmem; /* do nothing */
> -        else
> -          {
> -            /* Must alloc, copy, free. */
> -	    top_check ();
> -	    newmem = _int_malloc (&main_arena, rb);
> -            if (newmem)
> -              {
> -                memcpy (newmem, oldmem, oldsize - CHUNK_HDR_SZ);
> -                munmap_chunk (oldp);
> -              }
> -          }
> -      }
> -    }
> -  else
> -    {
> -      top_check ();
> -      newmem = _int_realloc (&main_arena, oldp, oldsize, chnb);
> -    }
> -
> -  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
> -  /* 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;
> -
> -  __libc_lock_unlock (main_arena.mutex);
> -
> -  return mem2mem_check (tag_new_usable (newmem), bytes);
> -}
> -
> -static void *
> -memalign_check (size_t alignment, size_t bytes, const void *caller)
> -{
> -  void *mem;
> -
> -  if (alignment <= MALLOC_ALIGNMENT)
> -    return malloc_check (bytes, NULL);
> -
> -  if (alignment < MINSIZE)
> -    alignment = MINSIZE;
> -
> -  /* 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;
> -    }
> -
> -  /* Check for overflow.  */
> -  if (bytes > SIZE_MAX - alignment - MINSIZE)
> -    {
> -      __set_errno (ENOMEM);
> -      return 0;
> -    }
> -
> -  /* Make sure alignment is power of 2.  */
> -  if (!powerof2 (alignment))
> -    {
> -      size_t a = MALLOC_ALIGNMENT * 2;
> -      while (a < alignment)
> -        a <<= 1;
> -      alignment = a;
> -    }
> -
> -  __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 (tag_new_usable (mem), bytes);
> -}
> +#include "malloc-check.c"
>  
>  #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
>  
> diff --git a/malloc/malloc-check.c b/malloc/malloc-check.c
> new file mode 100644
> index 0000000000..dcab880510
> --- /dev/null
> +++ b/malloc/malloc-check.c
> @@ -0,0 +1,390 @@
> +/* glibc.malloc.check implementation.

OK.

> +   Copyright (C) 2001-2021 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 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
> +   Lesser General Public License for more details.
> +
> +   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/>.  */
> +
> +
> +/* Whether we are using malloc checking.  */
> +static int using_malloc_checking;
> +
> +/* Activate a standard set of debugging hooks. */
> +void
> +__malloc_check_init (void)
> +{
> +  using_malloc_checking = 1;
> +  __malloc_hook = malloc_check;
> +  __free_hook = free_check;
> +  __realloc_hook = realloc_check;
> +  __memalign_hook = memalign_check;
> +}
> +
> +/* When memory is tagged, the checking data is stored in the user part
> +   of the chunk.  We can't rely on the user not having modified the
> +   tags, so fetch the tag at each location before dereferencing
> +   it.  */
> +#define SAFE_CHAR_OFFSET(p,offset) \
> +  ((unsigned char *) tag_at (((unsigned char *) p) + offset))
> +
> +/* A simple, standard set of debugging hooks.  Overhead is `only' one
> +   byte per chunk; still this will catch most cases of double frees or
> +   overruns.  The goal here is to avoid obscure crashes due to invalid
> +   usage, unlike in the MALLOC_DEBUG code. */
> +
> +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 = CHUNK_HDR_SZ + memsize (p) - 1;
> +       (c = *SAFE_CHAR_OFFSET (p, size)) != magic;
> +       size -= c)
> +    {
> +      if (c <= 0 || size < (c + CHUNK_HDR_SZ))
> +	malloc_printerr ("malloc_check_get_size: memory corruption");
> +    }
> +
> +  /* chunk2mem size.  */
> +  return size - CHUNK_HDR_SZ;
> +}
> +
> +/* Instrument a chunk with overrun detector byte(s) and convert it
> +   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 = ptr;
> +  size_t max_sz, block_sz, i;
> +  unsigned char magic;
> +
> +  if (!ptr)
> +    return ptr;
> +
> +  p = mem2chunk (ptr);
> +  magic = magicbyte (p);
> +  max_sz = memsize (p);
> +
> +  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;
> +
> +      *SAFE_CHAR_OFFSET (m_ptr, i) = block_sz;
> +    }
> +  *SAFE_CHAR_OFFSET (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
> +mem2chunk_check (void *mem, unsigned char **magic_p)
> +{
> +  mchunkptr p;
> +  INTERNAL_SIZE_T sz, c;
> +  unsigned char magic;
> +
> +  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 = CHUNK_HDR_SZ + memsize (p) - 1;
> +	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
> +	   sz -= c)
> +        {
> +          if (c == 0 || sz < (c + CHUNK_HDR_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 = CHUNK_HDR_SZ + memsize (p) - 1;
> +	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
> +	   sz -= c)
> +        {
> +          if (c == 0 || sz < (c + CHUNK_HDR_SZ))
> +            return NULL;
> +        }
> +    }
> +
> +  unsigned char* safe_p = SAFE_CHAR_OFFSET (p, sz);
> +  *safe_p ^= 0xFF;
> +  if (magic_p)
> +    *magic_p = safe_p;
> +  return p;
> +}
> +
> +/* Check for corruption of the top chunk.  */
> +static void
> +top_check (void)
> +{
> +  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;
> +
> +  malloc_printerr ("malloc: top chunk is corrupt");
> +}
> +
> +static void *
> +malloc_check (size_t sz, const void *caller)
> +{
> +  void *victim;
> +  size_t nb;
> +
> +  if (__builtin_add_overflow (sz, 1, &nb))
> +    {
> +      __set_errno (ENOMEM);
> +      return NULL;
> +    }
> +
> +  __libc_lock_lock (main_arena.mutex);
> +  top_check ();
> +  victim = _int_malloc (&main_arena, nb);
> +  __libc_lock_unlock (main_arena.mutex);
> +  return mem2mem_check (tag_new_usable (victim), sz);
> +}
> +
> +static void
> +free_check (void *mem, const void *caller)
> +{
> +  mchunkptr p;
> +
> +  if (!mem)
> +    return;
> +
> +  int err = errno;
> +
> +  /* Quickly check that the freed pointer matches the tag for the memory.
> +     This gives a useful double-free detection.  */
> +  if (__glibc_unlikely (mtag_enabled))
> +    *(volatile char *)mem;
> +
> +  __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);
> +    }
> +  else
> +    {
> +      /* Mark the chunk as belonging to the library again.  */
> +      (void)tag_region (chunk2mem (p), memsize (p));
> +      _int_free (&main_arena, p, 1);
> +      __libc_lock_unlock (main_arena.mutex);
> +    }
> +  __set_errno (err);
> +}
> +
> +static void *
> +realloc_check (void *oldmem, size_t bytes, const void *caller)
> +{
> +  INTERNAL_SIZE_T chnb;
> +  void *newmem = 0;
> +  unsigned char *magic_p;
> +  size_t rb;
> +
> +  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;
> +    }
> +
> +  /* Quickly check that the freed pointer matches the tag for the memory.
> +     This gives a useful double-free detection.  */
> +  if (__glibc_unlikely (mtag_enabled))
> +    *(volatile char *)oldmem;
> +
> +  __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, &chnb))
> +    {
> +      __set_errno (ENOMEM);
> +      goto invert;
> +    }
> +
> +  __libc_lock_lock (main_arena.mutex);
> +
> +  if (chunk_is_mmapped (oldp))
> +    {
> +#if HAVE_MREMAP
> +      mchunkptr newp = mremap_chunk (oldp, chnb);
> +      if (newp)
> +        newmem = chunk2mem_tag (newp);
> +      else
> +#endif
> +      {
> +	/* Note the extra SIZE_SZ overhead. */
> +        if (oldsize - SIZE_SZ >= chnb)
> +          newmem = oldmem; /* do nothing */
> +        else
> +          {
> +            /* Must alloc, copy, free. */
> +	    top_check ();
> +	    newmem = _int_malloc (&main_arena, rb);
> +            if (newmem)
> +              {
> +                memcpy (newmem, oldmem, oldsize - CHUNK_HDR_SZ);
> +                munmap_chunk (oldp);
> +              }
> +          }
> +      }
> +    }
> +  else
> +    {
> +      top_check ();
> +      newmem = _int_realloc (&main_arena, oldp, oldsize, chnb);
> +    }
> +
> +  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
> +  /* 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;
> +
> +  __libc_lock_unlock (main_arena.mutex);
> +
> +  return mem2mem_check (tag_new_usable (newmem), bytes);
> +}
> +
> +static void *
> +memalign_check (size_t alignment, size_t bytes, const void *caller)
> +{
> +  void *mem;
> +
> +  if (alignment <= MALLOC_ALIGNMENT)
> +    return malloc_check (bytes, NULL);
> +
> +  if (alignment < MINSIZE)
> +    alignment = MINSIZE;
> +
> +  /* 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;
> +    }
> +
> +  /* Check for overflow.  */
> +  if (bytes > SIZE_MAX - alignment - MINSIZE)
> +    {
> +      __set_errno (ENOMEM);
> +      return 0;
> +    }
> +
> +  /* Make sure alignment is power of 2.  */
> +  if (!powerof2 (alignment))
> +    {
> +      size_t a = MALLOC_ALIGNMENT * 2;
> +      while (a < alignment)
> +        a <<= 1;
> +      alignment = a;
> +    }
> +
> +  __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 (tag_new_usable (mem), bytes);
> +}
>
diff mbox series

Patch

diff --git a/malloc/hooks.c b/malloc/hooks.c
index 8080c3f40e..57a9b55788 100644
--- a/malloc/hooks.c
+++ b/malloc/hooks.c
@@ -49,376 +49,7 @@  memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
   return __libc_memalign (alignment, sz);
 }
 
-/* Whether we are using malloc checking.  */
-static int using_malloc_checking;
-
-/* Activate a standard set of debugging hooks. */
-void
-__malloc_check_init (void)
-{
-  using_malloc_checking = 1;
-  __malloc_hook = malloc_check;
-  __free_hook = free_check;
-  __realloc_hook = realloc_check;
-  __memalign_hook = memalign_check;
-}
-
-/* When memory is tagged, the checking data is stored in the user part
-   of the chunk.  We can't rely on the user not having modified the
-   tags, so fetch the tag at each location before dereferencing
-   it.  */
-#define SAFE_CHAR_OFFSET(p,offset) \
-  ((unsigned char *) tag_at (((unsigned char *) p) + offset))
-
-/* A simple, standard set of debugging hooks.  Overhead is `only' one
-   byte per chunk; still this will catch most cases of double frees or
-   overruns.  The goal here is to avoid obscure crashes due to invalid
-   usage, unlike in the MALLOC_DEBUG code. */
-
-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 = CHUNK_HDR_SZ + memsize (p) - 1;
-       (c = *SAFE_CHAR_OFFSET (p, size)) != magic;
-       size -= c)
-    {
-      if (c <= 0 || size < (c + CHUNK_HDR_SZ))
-	malloc_printerr ("malloc_check_get_size: memory corruption");
-    }
-
-  /* chunk2mem size.  */
-  return size - CHUNK_HDR_SZ;
-}
-
-/* Instrument a chunk with overrun detector byte(s) and convert it
-   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 = ptr;
-  size_t max_sz, block_sz, i;
-  unsigned char magic;
-
-  if (!ptr)
-    return ptr;
-
-  p = mem2chunk (ptr);
-  magic = magicbyte (p);
-  max_sz = memsize (p);
-
-  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;
-
-      *SAFE_CHAR_OFFSET (m_ptr, i) = block_sz;
-    }
-  *SAFE_CHAR_OFFSET (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
-mem2chunk_check (void *mem, unsigned char **magic_p)
-{
-  mchunkptr p;
-  INTERNAL_SIZE_T sz, c;
-  unsigned char magic;
-
-  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 = CHUNK_HDR_SZ + memsize (p) - 1;
-	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
-	   sz -= c)
-        {
-          if (c == 0 || sz < (c + CHUNK_HDR_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 = CHUNK_HDR_SZ + memsize (p) - 1;
-	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
-	   sz -= c)
-        {
-          if (c == 0 || sz < (c + CHUNK_HDR_SZ))
-            return NULL;
-        }
-    }
-
-  unsigned char* safe_p = SAFE_CHAR_OFFSET (p, sz);
-  *safe_p ^= 0xFF;
-  if (magic_p)
-    *magic_p = safe_p;
-  return p;
-}
-
-/* Check for corruption of the top chunk.  */
-static void
-top_check (void)
-{
-  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;
-
-  malloc_printerr ("malloc: top chunk is corrupt");
-}
-
-static void *
-malloc_check (size_t sz, const void *caller)
-{
-  void *victim;
-  size_t nb;
-
-  if (__builtin_add_overflow (sz, 1, &nb))
-    {
-      __set_errno (ENOMEM);
-      return NULL;
-    }
-
-  __libc_lock_lock (main_arena.mutex);
-  top_check ();
-  victim = _int_malloc (&main_arena, nb);
-  __libc_lock_unlock (main_arena.mutex);
-  return mem2mem_check (tag_new_usable (victim), sz);
-}
-
-static void
-free_check (void *mem, const void *caller)
-{
-  mchunkptr p;
-
-  if (!mem)
-    return;
-
-  int err = errno;
-
-  /* Quickly check that the freed pointer matches the tag for the memory.
-     This gives a useful double-free detection.  */
-  if (__glibc_unlikely (mtag_enabled))
-    *(volatile char *)mem;
-
-  __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);
-    }
-  else
-    {
-      /* Mark the chunk as belonging to the library again.  */
-      (void)tag_region (chunk2mem (p), memsize (p));
-      _int_free (&main_arena, p, 1);
-      __libc_lock_unlock (main_arena.mutex);
-    }
-  __set_errno (err);
-}
-
-static void *
-realloc_check (void *oldmem, size_t bytes, const void *caller)
-{
-  INTERNAL_SIZE_T chnb;
-  void *newmem = 0;
-  unsigned char *magic_p;
-  size_t rb;
-
-  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;
-    }
-
-  /* Quickly check that the freed pointer matches the tag for the memory.
-     This gives a useful double-free detection.  */
-  if (__glibc_unlikely (mtag_enabled))
-    *(volatile char *)oldmem;
-
-  __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, &chnb))
-    {
-      __set_errno (ENOMEM);
-      goto invert;
-    }
-
-  __libc_lock_lock (main_arena.mutex);
-
-  if (chunk_is_mmapped (oldp))
-    {
-#if HAVE_MREMAP
-      mchunkptr newp = mremap_chunk (oldp, chnb);
-      if (newp)
-        newmem = chunk2mem_tag (newp);
-      else
-#endif
-      {
-	/* Note the extra SIZE_SZ overhead. */
-        if (oldsize - SIZE_SZ >= chnb)
-          newmem = oldmem; /* do nothing */
-        else
-          {
-            /* Must alloc, copy, free. */
-	    top_check ();
-	    newmem = _int_malloc (&main_arena, rb);
-            if (newmem)
-              {
-                memcpy (newmem, oldmem, oldsize - CHUNK_HDR_SZ);
-                munmap_chunk (oldp);
-              }
-          }
-      }
-    }
-  else
-    {
-      top_check ();
-      newmem = _int_realloc (&main_arena, oldp, oldsize, chnb);
-    }
-
-  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
-  /* 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;
-
-  __libc_lock_unlock (main_arena.mutex);
-
-  return mem2mem_check (tag_new_usable (newmem), bytes);
-}
-
-static void *
-memalign_check (size_t alignment, size_t bytes, const void *caller)
-{
-  void *mem;
-
-  if (alignment <= MALLOC_ALIGNMENT)
-    return malloc_check (bytes, NULL);
-
-  if (alignment < MINSIZE)
-    alignment = MINSIZE;
-
-  /* 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;
-    }
-
-  /* Check for overflow.  */
-  if (bytes > SIZE_MAX - alignment - MINSIZE)
-    {
-      __set_errno (ENOMEM);
-      return 0;
-    }
-
-  /* Make sure alignment is power of 2.  */
-  if (!powerof2 (alignment))
-    {
-      size_t a = MALLOC_ALIGNMENT * 2;
-      while (a < alignment)
-        a <<= 1;
-      alignment = a;
-    }
-
-  __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 (tag_new_usable (mem), bytes);
-}
+#include "malloc-check.c"
 
 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
 
diff --git a/malloc/malloc-check.c b/malloc/malloc-check.c
new file mode 100644
index 0000000000..dcab880510
--- /dev/null
+++ b/malloc/malloc-check.c
@@ -0,0 +1,390 @@ 
+/* glibc.malloc.check implementation.
+   Copyright (C) 2001-2021 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 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
+   Lesser General Public License for more details.
+
+   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/>.  */
+
+
+/* Whether we are using malloc checking.  */
+static int using_malloc_checking;
+
+/* Activate a standard set of debugging hooks. */
+void
+__malloc_check_init (void)
+{
+  using_malloc_checking = 1;
+  __malloc_hook = malloc_check;
+  __free_hook = free_check;
+  __realloc_hook = realloc_check;
+  __memalign_hook = memalign_check;
+}
+
+/* When memory is tagged, the checking data is stored in the user part
+   of the chunk.  We can't rely on the user not having modified the
+   tags, so fetch the tag at each location before dereferencing
+   it.  */
+#define SAFE_CHAR_OFFSET(p,offset) \
+  ((unsigned char *) tag_at (((unsigned char *) p) + offset))
+
+/* A simple, standard set of debugging hooks.  Overhead is `only' one
+   byte per chunk; still this will catch most cases of double frees or
+   overruns.  The goal here is to avoid obscure crashes due to invalid
+   usage, unlike in the MALLOC_DEBUG code. */
+
+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 = CHUNK_HDR_SZ + memsize (p) - 1;
+       (c = *SAFE_CHAR_OFFSET (p, size)) != magic;
+       size -= c)
+    {
+      if (c <= 0 || size < (c + CHUNK_HDR_SZ))
+	malloc_printerr ("malloc_check_get_size: memory corruption");
+    }
+
+  /* chunk2mem size.  */
+  return size - CHUNK_HDR_SZ;
+}
+
+/* Instrument a chunk with overrun detector byte(s) and convert it
+   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 = ptr;
+  size_t max_sz, block_sz, i;
+  unsigned char magic;
+
+  if (!ptr)
+    return ptr;
+
+  p = mem2chunk (ptr);
+  magic = magicbyte (p);
+  max_sz = memsize (p);
+
+  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;
+
+      *SAFE_CHAR_OFFSET (m_ptr, i) = block_sz;
+    }
+  *SAFE_CHAR_OFFSET (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
+mem2chunk_check (void *mem, unsigned char **magic_p)
+{
+  mchunkptr p;
+  INTERNAL_SIZE_T sz, c;
+  unsigned char magic;
+
+  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 = CHUNK_HDR_SZ + memsize (p) - 1;
+	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
+	   sz -= c)
+        {
+          if (c == 0 || sz < (c + CHUNK_HDR_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 = CHUNK_HDR_SZ + memsize (p) - 1;
+	   (c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
+	   sz -= c)
+        {
+          if (c == 0 || sz < (c + CHUNK_HDR_SZ))
+            return NULL;
+        }
+    }
+
+  unsigned char* safe_p = SAFE_CHAR_OFFSET (p, sz);
+  *safe_p ^= 0xFF;
+  if (magic_p)
+    *magic_p = safe_p;
+  return p;
+}
+
+/* Check for corruption of the top chunk.  */
+static void
+top_check (void)
+{
+  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;
+
+  malloc_printerr ("malloc: top chunk is corrupt");
+}
+
+static void *
+malloc_check (size_t sz, const void *caller)
+{
+  void *victim;
+  size_t nb;
+
+  if (__builtin_add_overflow (sz, 1, &nb))
+    {
+      __set_errno (ENOMEM);
+      return NULL;
+    }
+
+  __libc_lock_lock (main_arena.mutex);
+  top_check ();
+  victim = _int_malloc (&main_arena, nb);
+  __libc_lock_unlock (main_arena.mutex);
+  return mem2mem_check (tag_new_usable (victim), sz);
+}
+
+static void
+free_check (void *mem, const void *caller)
+{
+  mchunkptr p;
+
+  if (!mem)
+    return;
+
+  int err = errno;
+
+  /* Quickly check that the freed pointer matches the tag for the memory.
+     This gives a useful double-free detection.  */
+  if (__glibc_unlikely (mtag_enabled))
+    *(volatile char *)mem;
+
+  __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);
+    }
+  else
+    {
+      /* Mark the chunk as belonging to the library again.  */
+      (void)tag_region (chunk2mem (p), memsize (p));
+      _int_free (&main_arena, p, 1);
+      __libc_lock_unlock (main_arena.mutex);
+    }
+  __set_errno (err);
+}
+
+static void *
+realloc_check (void *oldmem, size_t bytes, const void *caller)
+{
+  INTERNAL_SIZE_T chnb;
+  void *newmem = 0;
+  unsigned char *magic_p;
+  size_t rb;
+
+  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;
+    }
+
+  /* Quickly check that the freed pointer matches the tag for the memory.
+     This gives a useful double-free detection.  */
+  if (__glibc_unlikely (mtag_enabled))
+    *(volatile char *)oldmem;
+
+  __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, &chnb))
+    {
+      __set_errno (ENOMEM);
+      goto invert;
+    }
+
+  __libc_lock_lock (main_arena.mutex);
+
+  if (chunk_is_mmapped (oldp))
+    {
+#if HAVE_MREMAP
+      mchunkptr newp = mremap_chunk (oldp, chnb);
+      if (newp)
+        newmem = chunk2mem_tag (newp);
+      else
+#endif
+      {
+	/* Note the extra SIZE_SZ overhead. */
+        if (oldsize - SIZE_SZ >= chnb)
+          newmem = oldmem; /* do nothing */
+        else
+          {
+            /* Must alloc, copy, free. */
+	    top_check ();
+	    newmem = _int_malloc (&main_arena, rb);
+            if (newmem)
+              {
+                memcpy (newmem, oldmem, oldsize - CHUNK_HDR_SZ);
+                munmap_chunk (oldp);
+              }
+          }
+      }
+    }
+  else
+    {
+      top_check ();
+      newmem = _int_realloc (&main_arena, oldp, oldsize, chnb);
+    }
+
+  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
+  /* 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;
+
+  __libc_lock_unlock (main_arena.mutex);
+
+  return mem2mem_check (tag_new_usable (newmem), bytes);
+}
+
+static void *
+memalign_check (size_t alignment, size_t bytes, const void *caller)
+{
+  void *mem;
+
+  if (alignment <= MALLOC_ALIGNMENT)
+    return malloc_check (bytes, NULL);
+
+  if (alignment < MINSIZE)
+    alignment = MINSIZE;
+
+  /* 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;
+    }
+
+  /* Check for overflow.  */
+  if (bytes > SIZE_MAX - alignment - MINSIZE)
+    {
+      __set_errno (ENOMEM);
+      return 0;
+    }
+
+  /* Make sure alignment is power of 2.  */
+  if (!powerof2 (alignment))
+    {
+      size_t a = MALLOC_ALIGNMENT * 2;
+      while (a < alignment)
+        a <<= 1;
+      alignment = a;
+    }
+
+  __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 (tag_new_usable (mem), bytes);
+}