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
137 static void unlink_blk __P ((struct hdr *ptr));
138 static void
139 unlink_blk (ptr)
140      struct hdr *ptr;
141 {
142   if (ptr->next != NULL)
143     {
144       ptr->next->prev = ptr->prev;
145       ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
146                                       + (uintptr_t) ptr->next->next);
147     }
148   if (ptr->prev != NULL)
149     {
150       ptr->prev->next = ptr->next;
151       ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
152                                       + (uintptr_t) ptr->prev->next);
153     }
154   else
155     root = ptr->next;
156 }
157
158 static void link_blk  __P ((struct hdr *ptr));
159 static void
160 link_blk (hdr)
161      struct hdr *hdr;
162 {
163   hdr->prev = NULL;
164   hdr->next = root;
165   root = hdr;
166   hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
167
168   /* And the next block.  */
169   if (hdr->next != NULL)
170     {
171       hdr->next->prev = hdr;
172       hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
173                                       + (uintptr_t) hdr->next->next);
174     }
175 }
176
177 static void freehook __P ((__ptr_t, const __ptr_t));
178 static void
179 freehook (ptr, caller)
180      __ptr_t ptr;
181      const __ptr_t caller;
182 {
183   if (pedantic)
184     mcheck_check_all ();
185   if (ptr)
186     {
187       struct hdr *hdr = ((struct hdr *) ptr) - 1;
188       checkhdr (hdr);
189       hdr->magic = MAGICFREE;
190       unlink_blk (hdr);
191       hdr->prev = hdr->next = NULL;
192       flood (ptr, FREEFLOOD, hdr->size);
193       ptr = (__ptr_t) hdr;
194     }
195   __free_hook = old_free_hook;
196   if (old_free_hook != NULL)
197     (*old_free_hook) (ptr, caller);
198   else
199     free (ptr);
200   __free_hook = freehook;
201 }
202
203 static __ptr_t mallochook __P ((__malloc_size_t, const __ptr_t));
204 static __ptr_t
205 mallochook (size, caller)
206      __malloc_size_t size;
207      const __ptr_t caller;
208 {
209   struct hdr *hdr;
210
211   if (pedantic)
212     mcheck_check_all ();
213
214   __malloc_hook = old_malloc_hook;
215   if (old_malloc_hook != NULL)
216     hdr = (struct hdr *) (*old_malloc_hook) (sizeof (struct hdr) + size + 1,
217                                              caller);
218   else
219     hdr = (struct hdr *) malloc (sizeof (struct hdr) + size + 1);
220   __malloc_hook = mallochook;
221   if (hdr == NULL)
222     return NULL;
223
224   hdr->size = size;
225   link_blk (hdr);
226   ((char *) &hdr[1])[size] = MAGICBYTE;
227   flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
228   return (__ptr_t) (hdr + 1);
229 }
230
231 static __ptr_t reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
232 static __ptr_t
233 reallochook (ptr, size, caller)
234      __ptr_t ptr;
235      __malloc_size_t size;
236      const __ptr_t caller;
237 {
238   struct hdr *hdr;
239   __malloc_size_t osize;
240
241   if (pedantic)
242     mcheck_check_all ();
243
244   if (ptr)
245     {
246       hdr = ((struct hdr *) ptr) - 1;
247       osize = hdr->size;
248
249       checkhdr (hdr);
250       unlink_blk (hdr);
251       if (size < osize)
252         flood ((char *) ptr + size, FREEFLOOD, osize - size);
253     }
254   else
255     {
256       osize = 0;
257       hdr = NULL;
258     }
259   __free_hook = old_free_hook;
260   __malloc_hook = old_malloc_hook;
261   __realloc_hook = old_realloc_hook;
262   if (old_realloc_hook != NULL)
263     hdr = (struct hdr *) (*old_realloc_hook) ((__ptr_t) hdr,
264                                               sizeof (struct hdr) + size + 1,
265                                               caller);
266   else
267     hdr = (struct hdr *) realloc ((__ptr_t) hdr,
268                                   sizeof (struct hdr) + size + 1);
269   __free_hook = freehook;
270   __malloc_hook = mallochook;
271   __realloc_hook = reallochook;
272   if (hdr == NULL)
273     return NULL;
274
275   hdr->size = size;
276   link_blk (hdr);
277   ((char *) &hdr[1])[size] = MAGICBYTE;
278   if (size > osize)
279     flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
280   return (__ptr_t) (hdr + 1);
281 }
282
283 static void mabort __P ((enum mcheck_status status))
284      __attribute__ ((noreturn));
285 static void
286 mabort (status)
287      enum mcheck_status status;
288 {
289   const char *msg;
290   switch (status)
291     {
292     case MCHECK_OK:
293       msg = _("memory is consistent, library is buggy\n");
294       break;
295     case MCHECK_HEAD:
296       msg = _("memory clobbered before allocated block\n");
297       break;
298     case MCHECK_TAIL:
299       msg = _("memory clobbered past end of allocated block\n");
300       break;
301     case MCHECK_FREE:
302       msg = _("block freed twice\n");
303       break;
304     default:
305       msg = _("bogus mcheck_status, library is buggy\n");
306       break;
307     }
308 #ifdef _LIBC
309   __libc_fatal (msg);
310 #else
311   fprintf (stderr, "mcheck: %s", msg);
312   fflush (stderr);
313   abort ();
314 #endif
315 }
316
317 int
318 mcheck (func)
319      void (*func) __P ((enum mcheck_status));
320 {
321   abortfunc = (func != NULL) ? func : &mabort;
322
323   /* These hooks may not be safely inserted if malloc is already in use.  */
324   if (__malloc_initialized <= 0 && !mcheck_used)
325     {
326       /* We call malloc() once here to ensure it is initialized.  */
327       void *p = malloc (0);
328       free (p);
329
330       old_free_hook = __free_hook;
331       __free_hook = freehook;
332       old_malloc_hook = __malloc_hook;
333       __malloc_hook = mallochook;
334       old_realloc_hook = __realloc_hook;
335       __realloc_hook = reallochook;
336       mcheck_used = 1;
337     }
338
339   return mcheck_used ? 0 : -1;
340 }
341 #ifdef _LIBC
342 libc_hidden_def (mcheck)
343 #endif
344
345 int
346 mcheck_pedantic (func)
347       void (*func) __P ((enum mcheck_status));
348 {
349   int res = mcheck (func);
350   if (res == 0)
351     pedantic = 1;
352   return res;
353 }
354
355 enum mcheck_status
356 mprobe (__ptr_t ptr)
357 {
358   return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
359 }