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