2002-08-03 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / malloc / mcheck.c
1 /* Standard debugging hooks for `malloc'.
2    Copyright (C) 1990-1997,99,2000,01,02 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Written May 1989 by Mike Haertel.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #ifndef _MALLOC_INTERNAL
22 # define _MALLOC_INTERNAL
23 # include <malloc.h>
24 # include <mcheck.h>
25 # include <stdint.h>
26 # include <stdio.h>
27 # include <libintl.h>
28 #endif
29
30 /* Old hook values.  */
31 static void (*old_free_hook) __P ((__ptr_t ptr, __const __ptr_t));
32 static __ptr_t (*old_malloc_hook) __P ((__malloc_size_t size, const __ptr_t));
33 static __ptr_t (*old_realloc_hook) __P ((__ptr_t ptr, __malloc_size_t size,
34                                          __const __ptr_t));
35
36 /* Function to call when something awful happens.  */
37 static void (*abortfunc) __P ((enum mcheck_status));
38
39 /* Arbitrary magical numbers.  */
40 #define MAGICWORD       0xfedabeeb
41 #define MAGICFREE       0xd8675309
42 #define MAGICBYTE       ((char) 0xd7)
43 #define MALLOCFLOOD     ((char) 0x93)
44 #define FREEFLOOD       ((char) 0x95)
45
46 struct hdr
47   {
48     __malloc_size_t size;       /* Exact size requested by user.  */
49     unsigned long int magic;    /* Magic number to check header integrity.  */
50     struct hdr *prev;
51     struct hdr *next;
52   };
53
54 /* This is the beginning of the list of all memory blocks allocated.
55    It is only constructed if the pedantic testing is requested.  */
56 static struct hdr *root;
57
58 static int mcheck_used;
59
60 /* Nonzero if pedentic checking of all blocks is requested.  */
61 static int pedantic;
62
63 #if defined _LIBC || defined STDC_HEADERS || defined USG
64 # include <string.h>
65 # define flood memset
66 #else
67 static void flood __P ((__ptr_t, int, __malloc_size_t));
68 static void
69 flood (ptr, val, size)
70      __ptr_t ptr;
71      int val;
72      __malloc_size_t size;
73 {
74   char *cp = ptr;
75   while (size--)
76     *cp++ = val;
77 }
78 #endif
79
80 static enum mcheck_status checkhdr __P ((const struct hdr *));
81 static enum mcheck_status
82 checkhdr (hdr)
83      const struct hdr *hdr;
84 {
85   enum mcheck_status status;
86
87   if (!mcheck_used)
88     /* Maybe the mcheck used is disabled?  This happens when we find
89        an error and report it.  */
90     return MCHECK_OK;
91
92   switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
93     {
94     default:
95       status = MCHECK_HEAD;
96       break;
97     case MAGICFREE:
98       status = MCHECK_FREE;
99       break;
100     case MAGICWORD:
101       if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
102         status = MCHECK_TAIL;
103       else
104         status = MCHECK_OK;
105       break;
106     }
107   if (status != MCHECK_OK)
108     {
109       mcheck_used = 0;
110       (*abortfunc) (status);
111       mcheck_used = 1;
112     }
113   return status;
114 }
115
116 void
117 mcheck_check_all ()
118 {
119   /* Walk through all the active blocks and test whether they were tempered
120      with.  */
121   struct hdr *runp = root;
122
123   /* Temporarily turn off the checks.  */
124   pedantic = 0;
125
126   while (runp != NULL)
127     {
128       (void) checkhdr (runp);
129
130       runp = runp->next;
131     }
132
133   /* Turn checks on again.  */
134   pedantic = 1;
135 }
136 #ifdef _LIBC
137 libc_hidden_def (mcheck_check_all)
138 #endif
139
140 static void unlink_blk __P ((struct hdr *ptr));
141 static void
142 unlink_blk (ptr)
143      struct hdr *ptr;
144 {
145   if (ptr->next != NULL)
146     {
147       ptr->next->prev = ptr->prev;
148       ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
149                                       + (uintptr_t) ptr->next->next);
150     }
151   if (ptr->prev != NULL)
152     {
153       ptr->prev->next = ptr->next;
154       ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
155                                       + (uintptr_t) ptr->prev->next);
156     }
157   else
158     root = ptr->next;
159 }
160
161 static void link_blk  __P ((struct hdr *ptr));
162 static void
163 link_blk (hdr)
164      struct hdr *hdr;
165 {
166   hdr->prev = NULL;
167   hdr->next = root;
168   root = hdr;
169   hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
170
171   /* And the next block.  */
172   if (hdr->next != NULL)
173     {
174       hdr->next->prev = hdr;
175       hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
176                                       + (uintptr_t) hdr->next->next);
177     }
178 }
179
180 static void freehook __P ((__ptr_t, const __ptr_t));
181 static void
182 freehook (ptr, caller)
183      __ptr_t ptr;
184      const __ptr_t caller;
185 {
186   if (pedantic)
187     mcheck_check_all ();
188   if (ptr)
189     {
190       struct hdr *hdr = ((struct hdr *) ptr) - 1;
191       checkhdr (hdr);
192       hdr->magic = MAGICFREE;
193       unlink_blk (hdr);
194       hdr->prev = hdr->next = NULL;
195       flood (ptr, FREEFLOOD, hdr->size);
196       ptr = (__ptr_t) hdr;
197     }
198   __free_hook = old_free_hook;
199   if (old_free_hook != NULL)
200     (*old_free_hook) (ptr, caller);
201   else
202     free (ptr);
203   __free_hook = freehook;
204 }
205
206 static __ptr_t mallochook __P ((__malloc_size_t, const __ptr_t));
207 static __ptr_t
208 mallochook (size, caller)
209      __malloc_size_t size;
210      const __ptr_t caller;
211 {
212   struct hdr *hdr;
213
214   if (pedantic)
215     mcheck_check_all ();
216
217   __malloc_hook = old_malloc_hook;
218   if (old_malloc_hook != NULL)
219     hdr = (struct hdr *) (*old_malloc_hook) (sizeof (struct hdr) + size + 1,
220                                              caller);
221   else
222     hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1);
223   __malloc_hook = mallochook;
224   if (hdr == NULL)
225     return NULL;
226
227   hdr->size = size;
228   link_blk (hdr);
229   ((char *) &hdr[1])[size] = MAGICBYTE;
230   flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
231   return (__ptr_t) (hdr + 1);
232 }
233
234 static __ptr_t reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
235 static __ptr_t
236 reallochook (ptr, size, caller)
237      __ptr_t ptr;
238      __malloc_size_t size;
239      const __ptr_t caller;
240 {
241   struct hdr *hdr;
242   __malloc_size_t osize;
243
244   if (pedantic)
245     mcheck_check_all ();
246
247   if (ptr)
248     {
249       hdr = ((struct hdr *) ptr) - 1;
250       osize = hdr->size;
251
252       checkhdr (hdr);
253       unlink_blk (hdr);
254       if (size < osize)
255         flood ((char *) ptr + size, FREEFLOOD, osize - size);
256     }
257   else
258     {
259       osize = 0;
260       hdr = NULL;
261     }
262   __free_hook = old_free_hook;
263   __malloc_hook = old_malloc_hook;
264   __realloc_hook = old_realloc_hook;
265   if (old_realloc_hook != NULL)
266     hdr = (struct hdr *) (*old_realloc_hook) ((__ptr_t) hdr,
267                                               sizeof (struct hdr) + size + 1,
268                                               caller);
269   else
270     hdr = (struct hdr *) realloc ((__ptr_t) hdr,
271                                   sizeof (struct hdr) + size + 1);
272   __free_hook = freehook;
273   __malloc_hook = mallochook;
274   __realloc_hook = reallochook;
275   if (hdr == NULL)
276     return NULL;
277
278   hdr->size = size;
279   link_blk (hdr);
280   ((char *) &hdr[1])[size] = MAGICBYTE;
281   if (size > osize)
282     flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
283   return (__ptr_t) (hdr + 1);
284 }
285
286 static void mabort __P ((enum mcheck_status status))
287      __attribute__ ((noreturn));
288 static void
289 mabort (status)
290      enum mcheck_status status;
291 {
292   const char *msg;
293   switch (status)
294     {
295     case MCHECK_OK:
296       msg = _("memory is consistent, library is buggy\n");
297       break;
298     case MCHECK_HEAD:
299       msg = _("memory clobbered before allocated block\n");
300       break;
301     case MCHECK_TAIL:
302       msg = _("memory clobbered past end of allocated block\n");
303       break;
304     case MCHECK_FREE:
305       msg = _("block freed twice\n");
306       break;
307     default:
308       msg = _("bogus mcheck_status, library is buggy\n");
309       break;
310     }
311 #ifdef _LIBC
312   __libc_fatal (msg);
313 #else
314   fprintf (stderr, "mcheck: %s", msg);
315   fflush (stderr);
316   abort ();
317 #endif
318 }
319
320 int
321 mcheck (func)
322      void (*func) __P ((enum mcheck_status));
323 {
324   abortfunc = (func != NULL) ? func : &mabort;
325
326   /* These hooks may not be safely inserted if malloc is already in use.  */
327   if (__malloc_initialized <= 0 && !mcheck_used)
328     {
329       /* We call malloc() once here to ensure it is initialized.  */
330       void *p = malloc (0);
331       free (p);
332
333       old_free_hook = __free_hook;
334       __free_hook = freehook;
335       old_malloc_hook = __malloc_hook;
336       __malloc_hook = mallochook;
337       old_realloc_hook = __realloc_hook;
338       __realloc_hook = reallochook;
339       mcheck_used = 1;
340     }
341
342   return mcheck_used ? 0 : -1;
343 }
344 #ifdef _LIBC
345 libc_hidden_def (mcheck)
346 #endif
347
348 int
349 mcheck_pedantic (func)
350       void (*func) __P ((enum mcheck_status));
351 {
352   int res = mcheck (func);
353   if (res == 0)
354     pedantic = 1;
355   return res;
356 }
357
358 enum mcheck_status
359 mprobe (__ptr_t ptr)
360 {
361   return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
362 }