2002-11-13 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_memalign_hook) __P ((__malloc_size_t alignment,
34                                           __malloc_size_t size,
35                                           const __ptr_t));
36 static __ptr_t (*old_realloc_hook) __P ((__ptr_t ptr, __malloc_size_t size,
37                                          __const __ptr_t));
38
39 /* Function to call when something awful happens.  */
40 static void (*abortfunc) __P ((enum mcheck_status));
41
42 /* Arbitrary magical numbers.  */
43 #define MAGICWORD       0xfedabeeb
44 #define MAGICFREE       0xd8675309
45 #define MAGICBYTE       ((char) 0xd7)
46 #define MALLOCFLOOD     ((char) 0x93)
47 #define FREEFLOOD       ((char) 0x95)
48
49 struct hdr
50   {
51     __malloc_size_t size;       /* Exact size requested by user.  */
52     unsigned long int magic;    /* Magic number to check header integrity.  */
53     struct hdr *prev;
54     struct hdr *next;
55     __ptr_t block;              /* Real block allocated, for memalign.  */
56     unsigned long int magic2;   /* Extra, keeps us doubleword aligned.  */
57   };
58
59 /* This is the beginning of the list of all memory blocks allocated.
60    It is only constructed if the pedantic testing is requested.  */
61 static struct hdr *root;
62
63 static int mcheck_used;
64
65 /* Nonzero if pedentic checking of all blocks is requested.  */
66 static int pedantic;
67
68 #if defined _LIBC || defined STDC_HEADERS || defined USG
69 # include <string.h>
70 # define flood memset
71 #else
72 static void flood __P ((__ptr_t, int, __malloc_size_t));
73 static void
74 flood (ptr, val, size)
75      __ptr_t ptr;
76      int val;
77      __malloc_size_t size;
78 {
79   char *cp = ptr;
80   while (size--)
81     *cp++ = val;
82 }
83 #endif
84
85 static enum mcheck_status checkhdr __P ((const struct hdr *));
86 static enum mcheck_status
87 checkhdr (hdr)
88      const struct hdr *hdr;
89 {
90   enum mcheck_status status;
91
92   if (!mcheck_used)
93     /* Maybe the mcheck used is disabled?  This happens when we find
94        an error and report it.  */
95     return MCHECK_OK;
96
97   switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
98     {
99     default:
100       status = MCHECK_HEAD;
101       break;
102     case MAGICFREE:
103       status = MCHECK_FREE;
104       break;
105     case MAGICWORD:
106       if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
107         status = MCHECK_TAIL;
108       else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
109         status = MCHECK_HEAD;
110       else
111         status = MCHECK_OK;
112       break;
113     }
114   if (status != MCHECK_OK)
115     {
116       mcheck_used = 0;
117       (*abortfunc) (status);
118       mcheck_used = 1;
119     }
120   return status;
121 }
122
123 void
124 mcheck_check_all ()
125 {
126   /* Walk through all the active blocks and test whether they were tempered
127      with.  */
128   struct hdr *runp = root;
129
130   /* Temporarily turn off the checks.  */
131   pedantic = 0;
132
133   while (runp != NULL)
134     {
135       (void) checkhdr (runp);
136
137       runp = runp->next;
138     }
139
140   /* Turn checks on again.  */
141   pedantic = 1;
142 }
143 #ifdef _LIBC
144 libc_hidden_def (mcheck_check_all)
145 #endif
146
147 static void unlink_blk __P ((struct hdr *ptr));
148 static void
149 unlink_blk (ptr)
150      struct hdr *ptr;
151 {
152   if (ptr->next != NULL)
153     {
154       ptr->next->prev = ptr->prev;
155       ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
156                                       + (uintptr_t) ptr->next->next);
157     }
158   if (ptr->prev != NULL)
159     {
160       ptr->prev->next = ptr->next;
161       ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
162                                       + (uintptr_t) ptr->prev->next);
163     }
164   else
165     root = ptr->next;
166 }
167
168 static void link_blk  __P ((struct hdr *ptr));
169 static void
170 link_blk (hdr)
171      struct hdr *hdr;
172 {
173   hdr->prev = NULL;
174   hdr->next = root;
175   root = hdr;
176   hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
177
178   /* And the next block.  */
179   if (hdr->next != NULL)
180     {
181       hdr->next->prev = hdr;
182       hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
183                                       + (uintptr_t) hdr->next->next);
184     }
185 }
186
187 static void freehook __P ((__ptr_t, const __ptr_t));
188 static void
189 freehook (ptr, caller)
190      __ptr_t ptr;
191      const __ptr_t caller;
192 {
193   if (pedantic)
194     mcheck_check_all ();
195   if (ptr)
196     {
197       struct hdr *hdr = ((struct hdr *) ptr) - 1;
198       checkhdr (hdr);
199       hdr->magic = MAGICFREE;
200       hdr->magic2 = MAGICFREE;
201       unlink_blk (hdr);
202       hdr->prev = hdr->next = NULL;
203       flood (ptr, FREEFLOOD, hdr->size);
204       ptr = hdr->block;
205     }
206   __free_hook = old_free_hook;
207   if (old_free_hook != NULL)
208     (*old_free_hook) (ptr, caller);
209   else
210     free (ptr);
211   __free_hook = freehook;
212 }
213
214 static __ptr_t mallochook __P ((__malloc_size_t, const __ptr_t));
215 static __ptr_t
216 mallochook (size, caller)
217      __malloc_size_t size;
218      const __ptr_t caller;
219 {
220   struct hdr *hdr;
221
222   if (pedantic)
223     mcheck_check_all ();
224
225   __malloc_hook = old_malloc_hook;
226   if (old_malloc_hook != NULL)
227     hdr = (struct hdr *) (*old_malloc_hook) (sizeof (struct hdr) + size + 1,
228                                              caller);
229   else
230     hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1);
231   __malloc_hook = mallochook;
232   if (hdr == NULL)
233     return NULL;
234
235   hdr->size = size;
236   link_blk (hdr);
237   hdr->block = hdr;
238   hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
239   ((char *) &hdr[1])[size] = MAGICBYTE;
240   flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
241   return (__ptr_t) (hdr + 1);
242 }
243
244 static __ptr_t memalignhook __P ((__malloc_size_t, __malloc_size_t,
245                                   const __ptr_t));
246 static __ptr_t
247 memalignhook (alignment, size, caller)
248      __malloc_size_t alignment, size;
249      const __ptr_t caller;
250 {
251   struct hdr *hdr;
252   __malloc_size_t slop;
253   char *block;
254
255   if (pedantic)
256     mcheck_check_all ();
257
258   slop = (sizeof *hdr + alignment - 1) & -alignment;
259
260   __memalign_hook = old_memalign_hook;
261   if (old_memalign_hook != NULL)
262     block = (*old_memalign_hook) (alignment, slop + size + 1, caller);
263   else
264     block = memalign (alignment, slop + size + 1);
265   __memalign_hook = memalignhook;
266   if (block == NULL)
267     return NULL;
268
269   hdr = ((struct hdr *) (block + slop)) - 1;
270
271   hdr->size = size;
272   link_blk (hdr);
273   hdr->block = (__ptr_t) block;
274   hdr->magic2 = (uintptr_t) block ^ MAGICWORD;
275   ((char *) &hdr[1])[size] = MAGICBYTE;
276   flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
277   return (__ptr_t) (hdr + 1);
278 }
279
280 static __ptr_t reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
281 static __ptr_t
282 reallochook (ptr, size, caller)
283      __ptr_t ptr;
284      __malloc_size_t size;
285      const __ptr_t caller;
286 {
287   struct hdr *hdr;
288   __malloc_size_t osize;
289
290   if (pedantic)
291     mcheck_check_all ();
292
293   if (ptr)
294     {
295       hdr = ((struct hdr *) ptr) - 1;
296       osize = hdr->size;
297
298       checkhdr (hdr);
299       unlink_blk (hdr);
300       if (size < osize)
301         flood ((char *) ptr + size, FREEFLOOD, osize - size);
302     }
303   else
304     {
305       osize = 0;
306       hdr = NULL;
307     }
308   __free_hook = old_free_hook;
309   __malloc_hook = old_malloc_hook;
310   __memalign_hook = old_memalign_hook;
311   __realloc_hook = old_realloc_hook;
312   if (old_realloc_hook != NULL)
313     hdr = (struct hdr *) (*old_realloc_hook) ((__ptr_t) hdr,
314                                               sizeof (struct hdr) + size + 1,
315                                               caller);
316   else
317     hdr = (struct hdr *) realloc ((__ptr_t) hdr,
318                                   sizeof (struct hdr) + size + 1);
319   __free_hook = freehook;
320   __malloc_hook = mallochook;
321   __memalign_hook = memalignhook;
322   __realloc_hook = reallochook;
323   if (hdr == NULL)
324     return NULL;
325
326   hdr->size = size;
327   link_blk (hdr);
328   hdr->block = hdr;
329   hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
330   ((char *) &hdr[1])[size] = MAGICBYTE;
331   if (size > osize)
332     flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
333   return (__ptr_t) (hdr + 1);
334 }
335
336 static void mabort __P ((enum mcheck_status status))
337      __attribute__ ((noreturn));
338 static void
339 mabort (status)
340      enum mcheck_status status;
341 {
342   const char *msg;
343   switch (status)
344     {
345     case MCHECK_OK:
346       msg = _("memory is consistent, library is buggy\n");
347       break;
348     case MCHECK_HEAD:
349       msg = _("memory clobbered before allocated block\n");
350       break;
351     case MCHECK_TAIL:
352       msg = _("memory clobbered past end of allocated block\n");
353       break;
354     case MCHECK_FREE:
355       msg = _("block freed twice\n");
356       break;
357     default:
358       msg = _("bogus mcheck_status, library is buggy\n");
359       break;
360     }
361 #ifdef _LIBC
362   __libc_fatal (msg);
363 #else
364   fprintf (stderr, "mcheck: %s", msg);
365   fflush (stderr);
366   abort ();
367 #endif
368 }
369
370 int
371 mcheck (func)
372      void (*func) __P ((enum mcheck_status));
373 {
374   abortfunc = (func != NULL) ? func : &mabort;
375
376   /* These hooks may not be safely inserted if malloc is already in use.  */
377   if (__malloc_initialized <= 0 && !mcheck_used)
378     {
379       /* We call malloc() once here to ensure it is initialized.  */
380       void *p = malloc (0);
381       free (p);
382
383       old_free_hook = __free_hook;
384       __free_hook = freehook;
385       old_malloc_hook = __malloc_hook;
386       __malloc_hook = mallochook;
387       old_memalign_hook = __memalign_hook;
388       __memalign_hook = memalignhook;
389       old_realloc_hook = __realloc_hook;
390       __realloc_hook = reallochook;
391       mcheck_used = 1;
392     }
393
394   return mcheck_used ? 0 : -1;
395 }
396 #ifdef _LIBC
397 libc_hidden_def (mcheck)
398 #endif
399
400 int
401 mcheck_pedantic (func)
402       void (*func) __P ((enum mcheck_status));
403 {
404   int res = mcheck (func);
405   if (res == 0)
406     pedantic = 1;
407   return res;
408 }
409
410 enum mcheck_status
411 mprobe (__ptr_t ptr)
412 {
413   return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
414 }