Change prototype of test_in_mod1.
[kopensolaris-gnu/glibc.git] / malloc / mcheck.c
index 7f1112e..8cae506 100644 (file)
@@ -1,36 +1,40 @@
 /* Standard debugging hooks for `malloc'.
-   Copyright (C) 1990,91,92,93,94,95,96 Free Software Foundation, Inc.
+   Copyright (C) 1990-1997,99,2000,01,02 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
    Written May 1989 by Mike Haertel.
 
-   This 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
-   License, or (at your option) any later version.
+   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.
 
-   This library is distributed in the hope that it will be useful,
+   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.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with this 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.
-
-   The author may be reached (Email) at the address mike@ai.mit.edu,
-   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
 
 #ifndef        _MALLOC_INTERNAL
-#define        _MALLOC_INTERNAL
-#include <malloc.h>
-#include <mcheck.h>
-#include <stdio.h>
+# define _MALLOC_INTERNAL
+# include <malloc.h>
+# include <mcheck.h>
+# include <stdint.h>
+# include <stdio.h>
+# include <libintl.h>
 #endif
 
 /* Old hook values.  */
-static void (*old_free_hook) __P ((__ptr_t ptr));
-static __ptr_t (*old_malloc_hook) __P ((__malloc_size_t size));
-static __ptr_t (*old_realloc_hook) __P ((__ptr_t ptr, __malloc_size_t size));
+static void (*old_free_hook) __P ((__ptr_t ptr, __const __ptr_t));
+static __ptr_t (*old_malloc_hook) __P ((__malloc_size_t size, const __ptr_t));
+static __ptr_t (*old_memalign_hook) __P ((__malloc_size_t alignment,
+                                         __malloc_size_t size,
+                                         const __ptr_t));
+static __ptr_t (*old_realloc_hook) __P ((__ptr_t ptr, __malloc_size_t size,
+                                        __const __ptr_t));
 
 /* Function to call when something awful happens.  */
 static void (*abortfunc) __P ((enum mcheck_status));
@@ -44,12 +48,26 @@ static void (*abortfunc) __P ((enum mcheck_status));
 
 struct hdr
   {
-    __malloc_size_t size;              /* Exact size requested by user.  */
+    __malloc_size_t size;      /* Exact size requested by user.  */
     unsigned long int magic;   /* Magic number to check header integrity.  */
+    struct hdr *prev;
+    struct hdr *next;
+    __ptr_t block;             /* Real block allocated, for memalign.  */
+    unsigned long int magic2;  /* Extra, keeps us doubleword aligned.  */
   };
 
-#if    defined(_LIBC) || defined(STDC_HEADERS) || defined(USG)
-#define flood memset
+/* This is the beginning of the list of all memory blocks allocated.
+   It is only constructed if the pedantic testing is requested.  */
+static struct hdr *root;
+
+static int mcheck_used;
+
+/* Nonzero if pedentic checking of all blocks is requested.  */
+static int pedantic;
+
+#if defined _LIBC || defined STDC_HEADERS || defined USG
+# include <string.h>
+# define flood memset
 #else
 static void flood __P ((__ptr_t, int, __malloc_size_t));
 static void
@@ -70,7 +88,13 @@ checkhdr (hdr)
      const struct hdr *hdr;
 {
   enum mcheck_status status;
-  switch (hdr->magic)
+
+  if (!mcheck_used)
+    /* Maybe the mcheck used is disabled?  This happens when we find
+       an error and report it.  */
+    return MCHECK_OK;
+
+  switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
     {
     default:
       status = MCHECK_HEAD;
@@ -81,68 +105,198 @@ checkhdr (hdr)
     case MAGICWORD:
       if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
        status = MCHECK_TAIL;
+      else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
+       status = MCHECK_HEAD;
       else
        status = MCHECK_OK;
       break;
     }
   if (status != MCHECK_OK)
-    (*abortfunc) (status);
+    {
+      mcheck_used = 0;
+      (*abortfunc) (status);
+      mcheck_used = 1;
+    }
   return status;
 }
 
-static void freehook __P ((__ptr_t));
+void
+mcheck_check_all ()
+{
+  /* Walk through all the active blocks and test whether they were tempered
+     with.  */
+  struct hdr *runp = root;
+
+  /* Temporarily turn off the checks.  */
+  pedantic = 0;
+
+  while (runp != NULL)
+    {
+      (void) checkhdr (runp);
+
+      runp = runp->next;
+    }
+
+  /* Turn checks on again.  */
+  pedantic = 1;
+}
+#ifdef _LIBC
+libc_hidden_def (mcheck_check_all)
+#endif
+
+static void unlink_blk __P ((struct hdr *ptr));
+static void
+unlink_blk (ptr)
+     struct hdr *ptr;
+{
+  if (ptr->next != NULL)
+    {
+      ptr->next->prev = ptr->prev;
+      ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
+                                     + (uintptr_t) ptr->next->next);
+    }
+  if (ptr->prev != NULL)
+    {
+      ptr->prev->next = ptr->next;
+      ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
+                                     + (uintptr_t) ptr->prev->next);
+    }
+  else
+    root = ptr->next;
+}
+
+static void link_blk  __P ((struct hdr *ptr));
 static void
-freehook (ptr)
+link_blk (hdr)
+     struct hdr *hdr;
+{
+  hdr->prev = NULL;
+  hdr->next = root;
+  root = hdr;
+  hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
+
+  /* And the next block.  */
+  if (hdr->next != NULL)
+    {
+      hdr->next->prev = hdr;
+      hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
+                                     + (uintptr_t) hdr->next->next);
+    }
+}
+
+static void freehook __P ((__ptr_t, const __ptr_t));
+static void
+freehook (ptr, caller)
      __ptr_t ptr;
+     const __ptr_t caller;
 {
+  if (pedantic)
+    mcheck_check_all ();
   if (ptr)
     {
       struct hdr *hdr = ((struct hdr *) ptr) - 1;
       checkhdr (hdr);
       hdr->magic = MAGICFREE;
+      hdr->magic2 = MAGICFREE;
+      unlink_blk (hdr);
+      hdr->prev = hdr->next = NULL;
       flood (ptr, FREEFLOOD, hdr->size);
-      ptr = (__ptr_t) hdr;
+      ptr = hdr->block;
     }
   __free_hook = old_free_hook;
-  free (ptr);
+  if (old_free_hook != NULL)
+    (*old_free_hook) (ptr, caller);
+  else
+    free (ptr);
   __free_hook = freehook;
 }
 
-static __ptr_t mallochook __P ((__malloc_size_t));
+static __ptr_t mallochook __P ((__malloc_size_t, const __ptr_t));
 static __ptr_t
-mallochook (size)
+mallochook (size, caller)
      __malloc_size_t size;
+     const __ptr_t caller;
 {
   struct hdr *hdr;
 
+  if (pedantic)
+    mcheck_check_all ();
+
   __malloc_hook = old_malloc_hook;
-  hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1);
+  if (old_malloc_hook != NULL)
+    hdr = (struct hdr *) (*old_malloc_hook) (sizeof (struct hdr) + size + 1,
+                                            caller);
+  else
+    hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1);
   __malloc_hook = mallochook;
   if (hdr == NULL)
     return NULL;
 
   hdr->size = size;
-  hdr->magic = MAGICWORD;
+  link_blk (hdr);
+  hdr->block = hdr;
+  hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
+  ((char *) &hdr[1])[size] = MAGICBYTE;
+  flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
+  return (__ptr_t) (hdr + 1);
+}
+
+static __ptr_t memalignhook __P ((__malloc_size_t, __malloc_size_t,
+                                 const __ptr_t));
+static __ptr_t
+memalignhook (alignment, size, caller)
+     __malloc_size_t alignment, size;
+     const __ptr_t caller;
+{
+  struct hdr *hdr;
+  __malloc_size_t slop;
+  char *block;
+
+  if (pedantic)
+    mcheck_check_all ();
+
+  slop = (sizeof *hdr + alignment - 1) & -alignment;
+
+  __memalign_hook = old_memalign_hook;
+  if (old_memalign_hook != NULL)
+    block = (*old_memalign_hook) (alignment, slop + size + 1, caller);
+  else
+    block = memalign (alignment, slop + size + 1);
+  __memalign_hook = memalignhook;
+  if (block == NULL)
+    return NULL;
+
+  hdr = ((struct hdr *) (block + slop)) - 1;
+
+  hdr->size = size;
+  link_blk (hdr);
+  hdr->block = (__ptr_t) block;
+  hdr->magic2 = (uintptr_t) block ^ MAGICWORD;
   ((char *) &hdr[1])[size] = MAGICBYTE;
   flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
   return (__ptr_t) (hdr + 1);
 }
 
-static __ptr_t reallochook __P ((__ptr_t, __malloc_size_t));
+static __ptr_t reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
 static __ptr_t
-reallochook (ptr, size)
+reallochook (ptr, size, caller)
      __ptr_t ptr;
      __malloc_size_t size;
+     const __ptr_t caller;
 {
   struct hdr *hdr;
   __malloc_size_t osize;
 
+  if (pedantic)
+    mcheck_check_all ();
+
   if (ptr)
     {
       hdr = ((struct hdr *) ptr) - 1;
       osize = hdr->size;
 
       checkhdr (hdr);
+      unlink_blk (hdr);
       if (size < osize)
        flood ((char *) ptr + size, FREEFLOOD, osize - size);
     }
@@ -153,23 +307,34 @@ reallochook (ptr, size)
     }
   __free_hook = old_free_hook;
   __malloc_hook = old_malloc_hook;
+  __memalign_hook = old_memalign_hook;
   __realloc_hook = old_realloc_hook;
-  hdr = (struct hdr *) realloc ((__ptr_t) hdr, sizeof (struct hdr) + size + 1);
+  if (old_realloc_hook != NULL)
+    hdr = (struct hdr *) (*old_realloc_hook) ((__ptr_t) hdr,
+                                             sizeof (struct hdr) + size + 1,
+                                             caller);
+  else
+    hdr = (struct hdr *) realloc ((__ptr_t) hdr,
+                                 sizeof (struct hdr) + size + 1);
   __free_hook = freehook;
   __malloc_hook = mallochook;
+  __memalign_hook = memalignhook;
   __realloc_hook = reallochook;
   if (hdr == NULL)
     return NULL;
 
   hdr->size = size;
-  hdr->magic = MAGICWORD;
+  link_blk (hdr);
+  hdr->block = hdr;
+  hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
   ((char *) &hdr[1])[size] = MAGICBYTE;
   if (size > osize)
     flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
   return (__ptr_t) (hdr + 1);
 }
 
-static void mabort __P ((enum mcheck_status status));
+static void mabort __P ((enum mcheck_status status))
+     __attribute__ ((noreturn));
 static void
 mabort (status)
      enum mcheck_status status;
@@ -178,32 +343,30 @@ mabort (status)
   switch (status)
     {
     case MCHECK_OK:
-      msg = _("memory is consistent, library is buggy");
+      msg = _("memory is consistent, library is buggy\n");
       break;
     case MCHECK_HEAD:
-      msg = _("memory clobbered before allocated block");
+      msg = _("memory clobbered before allocated block\n");
       break;
     case MCHECK_TAIL:
-      msg = _("memory clobbered past end of allocated block");
+      msg = _("memory clobbered past end of allocated block\n");
       break;
     case MCHECK_FREE:
-      msg = _("block freed twice");
+      msg = _("block freed twice\n");
       break;
     default:
-      msg = _("bogus mcheck_status, library is buggy");
+      msg = _("bogus mcheck_status, library is buggy\n");
       break;
     }
 #ifdef _LIBC
   __libc_fatal (msg);
 #else
-  fprintf (stderr, "mcheck: %s\n", msg);
+  fprintf (stderr, "mcheck: %s", msg);
   fflush (stderr);
   abort ();
 #endif
 }
 
-static int mcheck_used = 0;
-
 int
 mcheck (func)
      void (*func) __P ((enum mcheck_status));
@@ -211,12 +374,18 @@ mcheck (func)
   abortfunc = (func != NULL) ? func : &mabort;
 
   /* These hooks may not be safely inserted if malloc is already in use.  */
-  if (!__malloc_initialized && !mcheck_used)
+  if (__malloc_initialized <= 0 && !mcheck_used)
     {
+      /* We call malloc() once here to ensure it is initialized.  */
+      void *p = malloc (0);
+      free (p);
+
       old_free_hook = __free_hook;
       __free_hook = freehook;
       old_malloc_hook = __malloc_hook;
       __malloc_hook = mallochook;
+      old_memalign_hook = __memalign_hook;
+      __memalign_hook = memalignhook;
       old_realloc_hook = __realloc_hook;
       __realloc_hook = reallochook;
       mcheck_used = 1;
@@ -224,9 +393,22 @@ mcheck (func)
 
   return mcheck_used ? 0 : -1;
 }
+#ifdef _LIBC
+libc_hidden_def (mcheck)
+#endif
+
+int
+mcheck_pedantic (func)
+      void (*func) __P ((enum mcheck_status));
+{
+  int res = mcheck (func);
+  if (res == 0)
+    pedantic = 1;
+  return res;
+}
 
 enum mcheck_status
 mprobe (__ptr_t ptr)
 {
-  return mcheck_used ? checkhdr (ptr) : MCHECK_DISABLED;
+  return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
 }