(root)/
glibc-2.38/
malloc/
mcheck-impl.c
       1  /* mcheck debugging hooks for malloc.
       2     Copyright (C) 1990-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <malloc-internal.h>
      20  #include <mcheck.h>
      21  #include <libintl.h>
      22  #include <stdint.h>
      23  #include <stdio.h>
      24  
      25  /* Arbitrary magical numbers.  */
      26  #define MAGICWORD       0xfedabeeb
      27  #define MAGICFREE       0xd8675309
      28  #define MAGICBYTE       ((char) 0xd7)
      29  #define MALLOCFLOOD     ((char) 0x93)
      30  #define FREEFLOOD       ((char) 0x95)
      31  
      32  /* Function to call when something awful happens.  */
      33  static void (*abortfunc) (enum mcheck_status);
      34  
      35  struct hdr
      36  {
      37    size_t size;                  /* Exact size requested by user.  */
      38    unsigned long int magic;      /* Magic number to check header integrity.  */
      39    struct hdr *prev;
      40    struct hdr *next;
      41    void *block;                  /* Real block allocated, for memalign.  */
      42    unsigned long int magic2;     /* Extra, keeps us doubleword aligned.  */
      43  } __attribute__ ((aligned (MALLOC_ALIGNMENT)));
      44  
      45  /* This is the beginning of the list of all memory blocks allocated.
      46     It is only constructed if the pedantic testing is requested.  */
      47  static struct hdr *root;
      48  
      49  /* Nonzero if pedentic checking of all blocks is requested.  */
      50  static bool pedantic;
      51  
      52  #if defined _LIBC || defined STDC_HEADERS || defined USG
      53  # include <string.h>
      54  # define flood memset
      55  #else
      56  static void flood (void *, int, size_t);
      57  static void
      58  flood (void *ptr, int val, size_t size)
      59  {
      60    char *cp = ptr;
      61    while (size--)
      62      *cp++ = val;
      63  }
      64  #endif
      65  
      66  static enum mcheck_status
      67  checkhdr (const struct hdr *hdr)
      68  {
      69    enum mcheck_status status;
      70    bool mcheck_used = __is_malloc_debug_enabled (MALLOC_MCHECK_HOOK);
      71  
      72    if (!mcheck_used)
      73      /* Maybe the mcheck used is disabled?  This happens when we find
      74         an error and report it.  */
      75      return MCHECK_OK;
      76  
      77    switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
      78      {
      79      default:
      80        status = MCHECK_HEAD;
      81        break;
      82      case MAGICFREE:
      83        status = MCHECK_FREE;
      84        break;
      85      case MAGICWORD:
      86        if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
      87  	status = MCHECK_TAIL;
      88        else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
      89  	status = MCHECK_HEAD;
      90        else
      91  	status = MCHECK_OK;
      92        break;
      93      }
      94    if (status != MCHECK_OK)
      95      {
      96        mcheck_used = 0;
      97        (*abortfunc) (status);
      98        mcheck_used = 1;
      99      }
     100    return status;
     101  }
     102  
     103  static enum mcheck_status
     104  __mcheck_checkptr (const void *ptr)
     105  {
     106    if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
     107        return MCHECK_DISABLED;
     108  
     109    if (ptr != NULL)
     110      return checkhdr (((struct hdr *) ptr) - 1);
     111  
     112    /* Walk through all the active blocks and test whether they were tampered
     113       with.  */
     114    struct hdr *runp = root;
     115  
     116    /* Temporarily turn off the checks.  */
     117    pedantic = false;
     118  
     119    while (runp != NULL)
     120      {
     121        (void) checkhdr (runp);
     122  
     123        runp = runp->next;
     124      }
     125  
     126    /* Turn checks on again.  */
     127    pedantic = true;
     128  
     129    return MCHECK_OK;
     130  }
     131  
     132  static void
     133  unlink_blk (struct hdr *ptr)
     134  {
     135    if (ptr->next != NULL)
     136      {
     137        ptr->next->prev = ptr->prev;
     138        ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
     139                                        + (uintptr_t) ptr->next->next);
     140      }
     141    if (ptr->prev != NULL)
     142      {
     143        ptr->prev->next = ptr->next;
     144        ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
     145                                        + (uintptr_t) ptr->prev->next);
     146      }
     147    else
     148      root = ptr->next;
     149  }
     150  
     151  static void
     152  link_blk (struct hdr *hdr)
     153  {
     154    hdr->prev = NULL;
     155    hdr->next = root;
     156    root = hdr;
     157    hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
     158  
     159    /* And the next block.  */
     160    if (hdr->next != NULL)
     161      {
     162        hdr->next->prev = hdr;
     163        hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
     164                                        + (uintptr_t) hdr->next->next);
     165      }
     166  }
     167  
     168  static void *
     169  free_mcheck (void *ptr)
     170  {
     171    if (pedantic)
     172      __mcheck_checkptr (NULL);
     173    if (ptr)
     174      {
     175        struct hdr *hdr = ((struct hdr *) ptr) - 1;
     176        checkhdr (hdr);
     177        hdr->magic = MAGICFREE;
     178        hdr->magic2 = MAGICFREE;
     179        unlink_blk (hdr);
     180        hdr->prev = hdr->next = NULL;
     181        flood (ptr, FREEFLOOD, hdr->size);
     182        ptr = hdr->block;
     183      }
     184    return ptr;
     185  }
     186  
     187  static bool
     188  malloc_mcheck_before (size_t *sizep, void **victimp)
     189  {
     190    size_t size = *sizep;
     191  
     192    if (pedantic)
     193      __mcheck_checkptr (NULL);
     194  
     195    if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
     196      {
     197        __set_errno (ENOMEM);
     198        *victimp = NULL;
     199        return true;
     200      }
     201  
     202    *sizep = sizeof (struct hdr) + size + 1;
     203    return false;
     204  }
     205  
     206  static void *
     207  malloc_mcheck_after (void *mem, size_t size)
     208  {
     209    struct hdr *hdr = mem;
     210  
     211    if (hdr == NULL)
     212      return NULL;
     213  
     214    hdr->size = size;
     215    link_blk (hdr);
     216    hdr->block = hdr;
     217    hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
     218    ((char *) &hdr[1])[size] = MAGICBYTE;
     219    flood ((void *) (hdr + 1), MALLOCFLOOD, size);
     220    return (void *) (hdr + 1);
     221  }
     222  
     223  static bool
     224  memalign_mcheck_before (size_t alignment, size_t *sizep, void **victimp)
     225  {
     226    struct hdr *hdr;
     227    size_t slop, size = *sizep;
     228  
     229    /* Punt to malloc to avoid double headers.  */
     230    if (alignment <= MALLOC_ALIGNMENT)
     231      {
     232        *victimp = __debug_malloc (size);
     233        return true;
     234      }
     235  
     236    if (pedantic)
     237      __mcheck_checkptr (NULL);
     238  
     239    slop = (sizeof *hdr + alignment - 1) & - alignment;
     240  
     241    if (size > ~((size_t) 0) - (slop + 1))
     242      {
     243        __set_errno (ENOMEM);
     244        *victimp = NULL;
     245        return true;
     246      }
     247  
     248    *sizep = slop + size + 1;
     249    return false;
     250  }
     251  
     252  static void *
     253  memalign_mcheck_after (void *block, size_t alignment, size_t size)
     254  {
     255    if (block == NULL)
     256      return NULL;
     257  
     258    /* This was served by __debug_malloc, so return as is.  */
     259    if (alignment <= MALLOC_ALIGNMENT)
     260      return block;
     261  
     262    size_t slop = (sizeof (struct hdr) + alignment - 1) & - alignment;
     263    struct hdr *hdr = ((struct hdr *) (block + slop)) - 1;
     264  
     265    hdr->size = size;
     266    link_blk (hdr);
     267    hdr->block = (void *) block;
     268    hdr->magic2 = (uintptr_t) block ^ MAGICWORD;
     269    ((char *) &hdr[1])[size] = MAGICBYTE;
     270    flood ((void *) (hdr + 1), MALLOCFLOOD, size);
     271    return (void *) (hdr + 1);
     272  }
     273  
     274  static bool
     275  realloc_mcheck_before (void **ptrp, size_t *sizep, size_t *oldsize,
     276  		       void **victimp)
     277  {
     278    size_t size = *sizep;
     279    void *ptr = *ptrp;
     280  
     281    if (ptr == NULL)
     282      {
     283        *victimp = __debug_malloc (size);
     284        *oldsize = 0;
     285        return true;
     286      }
     287  
     288    if (size == 0)
     289      {
     290        __debug_free (ptr);
     291        *victimp = NULL;
     292        return true;
     293      }
     294  
     295    if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
     296      {
     297        __set_errno (ENOMEM);
     298        *victimp = NULL;
     299        *oldsize = 0;
     300        return true;
     301      }
     302  
     303    if (pedantic)
     304      __mcheck_checkptr (NULL);
     305  
     306    struct hdr *hdr;
     307    size_t osize;
     308  
     309    /* Update the oldptr for glibc realloc.  */
     310    *ptrp = hdr = ((struct hdr *) ptr) - 1;
     311  
     312    osize = hdr->size;
     313  
     314    checkhdr (hdr);
     315    unlink_blk (hdr);
     316    if (size < osize)
     317      flood ((char *) ptr + size, FREEFLOOD, osize - size);
     318  
     319    *oldsize = osize;
     320    *sizep = sizeof (struct hdr) + size + 1;
     321    return false;
     322  }
     323  
     324  static void *
     325  realloc_mcheck_after (void *ptr, void *oldptr, size_t size, size_t osize)
     326  {
     327    struct hdr *hdr = ptr;
     328  
     329    if (hdr == NULL)
     330      return NULL;
     331  
     332    /* Malloc already added the header so don't tamper with it.  */
     333    if (oldptr == NULL)
     334      return ptr;
     335  
     336    hdr->size = size;
     337    link_blk (hdr);
     338    hdr->block = hdr;
     339    hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
     340    ((char *) &hdr[1])[size] = MAGICBYTE;
     341    if (size > osize)
     342      flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
     343    return (void *) (hdr + 1);
     344  }
     345  
     346  __attribute__ ((noreturn))
     347  static void
     348  mabort (enum mcheck_status status)
     349  {
     350    const char *msg;
     351    switch (status)
     352      {
     353      case MCHECK_OK:
     354        msg = _ ("memory is consistent, library is buggy\n");
     355        break;
     356      case MCHECK_HEAD:
     357        msg = _ ("memory clobbered before allocated block\n");
     358        break;
     359      case MCHECK_TAIL:
     360        msg = _ ("memory clobbered past end of allocated block\n");
     361        break;
     362      case MCHECK_FREE:
     363        msg = _ ("block freed twice\n");
     364        break;
     365      default:
     366        msg = _ ("bogus mcheck_status, library is buggy\n");
     367        break;
     368      }
     369  #ifdef _LIBC
     370    __libc_fatal (msg);
     371  #else
     372    fprintf (stderr, "mcheck: %s", msg);
     373    fflush (stderr);
     374    abort ();
     375  #endif
     376  }
     377  
     378  /* Memory barrier so that GCC does not optimize out the argument.  */
     379  #define malloc_opt_barrier(x) \
     380    ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; })
     381  
     382  static int
     383  __mcheck_initialize (void (*func) (enum mcheck_status), bool in_pedantic)
     384  {
     385    abortfunc = (func != NULL) ? func : &mabort;
     386  
     387    switch (debug_initialized)
     388      {
     389      case -1:
     390        /* Called before the first malloc was called.  */
     391        __debug_free (__debug_malloc (0));
     392        /* FALLTHROUGH */
     393      case 0:
     394        /* Called through the initializer hook.  */
     395        __malloc_debug_enable (MALLOC_MCHECK_HOOK);
     396        break;
     397      case 1:
     398      default:
     399        /* Malloc was already called.  Fail.  */
     400        return -1;
     401      }
     402  
     403    pedantic = in_pedantic;
     404    return 0;
     405  }
     406  
     407  static int
     408  mcheck_usable_size (struct hdr *h)
     409  {
     410    return (h - 1)->size;
     411  }