1 /* mcheck debugging hooks for malloc.
2 Copyright (C) 1990-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <malloc-internal.h>
25 /* Arbitrary magical numbers. */
26 #define MAGICWORD 0xfedabeeb
27 #define MAGICFREE 0xd8675309
28 #define MAGICBYTE ((char) 0xd7)
29 #define MALLOCFLOOD ((char) 0x93)
30 #define FREEFLOOD ((char) 0x95)
32 /* Function to call when something awful happens. */
33 static void (*abortfunc
) (enum mcheck_status
);
37 size_t size
; /* Exact size requested by user. */
38 unsigned long int magic
; /* Magic number to check header integrity. */
41 void *block
; /* Real block allocated, for memalign. */
42 unsigned long int magic2
; /* Extra, keeps us doubleword aligned. */
43 } __attribute__ ((aligned (MALLOC_ALIGNMENT
)));
45 /* This is the beginning of the list of all memory blocks allocated.
46 It is only constructed if the pedantic testing is requested. */
47 static struct hdr
*root
;
49 /* Nonzero if pedentic checking of all blocks is requested. */
52 #if defined _LIBC || defined STDC_HEADERS || defined USG
56 static void flood (void *, int, size_t);
58 flood (void *ptr
, int val
, size_t size
)
66 static enum mcheck_status
67 checkhdr (const struct hdr
*hdr
)
69 enum mcheck_status status
;
70 bool mcheck_used
= __is_malloc_debug_enabled (MALLOC_MCHECK_HOOK
);
73 /* Maybe the mcheck used is disabled? This happens when we find
74 an error and report it. */
77 switch (hdr
->magic
^ ((uintptr_t) hdr
->prev
+ (uintptr_t) hdr
->next
))
86 if (((char *) &hdr
[1])[hdr
->size
] != MAGICBYTE
)
88 else if ((hdr
->magic2
^ (uintptr_t) hdr
->block
) != MAGICWORD
)
94 if (status
!= MCHECK_OK
)
97 (*abortfunc
) (status
);
103 static enum mcheck_status
104 __mcheck_checkptr (const void *ptr
)
106 if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK
))
107 return MCHECK_DISABLED
;
110 return checkhdr (((struct hdr
*) ptr
) - 1);
112 /* Walk through all the active blocks and test whether they were tampered
114 struct hdr
*runp
= root
;
116 /* Temporarily turn off the checks. */
121 (void) checkhdr (runp
);
126 /* Turn checks on again. */
133 unlink_blk (struct hdr
*ptr
)
135 if (ptr
->next
!= NULL
)
137 ptr
->next
->prev
= ptr
->prev
;
138 ptr
->next
->magic
= MAGICWORD
^ ((uintptr_t) ptr
->next
->prev
139 + (uintptr_t) ptr
->next
->next
);
141 if (ptr
->prev
!= NULL
)
143 ptr
->prev
->next
= ptr
->next
;
144 ptr
->prev
->magic
= MAGICWORD
^ ((uintptr_t) ptr
->prev
->prev
145 + (uintptr_t) ptr
->prev
->next
);
152 link_blk (struct hdr
*hdr
)
157 hdr
->magic
= MAGICWORD
^ (uintptr_t) hdr
->next
;
159 /* And the next block. */
160 if (hdr
->next
!= NULL
)
162 hdr
->next
->prev
= hdr
;
163 hdr
->next
->magic
= MAGICWORD
^ ((uintptr_t) hdr
164 + (uintptr_t) hdr
->next
->next
);
169 free_mcheck (void *ptr
)
172 __mcheck_checkptr (NULL
);
175 struct hdr
*hdr
= ((struct hdr
*) ptr
) - 1;
177 hdr
->magic
= MAGICFREE
;
178 hdr
->magic2
= MAGICFREE
;
180 hdr
->prev
= hdr
->next
= NULL
;
181 flood (ptr
, FREEFLOOD
, hdr
->size
);
188 malloc_mcheck_before (size_t *sizep
, void **victimp
)
190 size_t size
= *sizep
;
193 __mcheck_checkptr (NULL
);
195 if (size
> ~((size_t) 0) - (sizeof (struct hdr
) + 1))
197 __set_errno (ENOMEM
);
202 *sizep
= sizeof (struct hdr
) + size
+ 1;
207 malloc_mcheck_after (void *mem
, size_t size
)
209 struct hdr
*hdr
= mem
;
217 hdr
->magic2
= (uintptr_t) hdr
^ MAGICWORD
;
218 ((char *) &hdr
[1])[size
] = MAGICBYTE
;
219 flood ((void *) (hdr
+ 1), MALLOCFLOOD
, size
);
220 return (void *) (hdr
+ 1);
224 memalign_mcheck_before (size_t alignment
, size_t *sizep
, void **victimp
)
227 size_t slop
, size
= *sizep
;
229 /* Punt to malloc to avoid double headers. */
230 if (alignment
<= MALLOC_ALIGNMENT
)
232 *victimp
= __debug_malloc (size
);
237 __mcheck_checkptr (NULL
);
239 slop
= (sizeof *hdr
+ alignment
- 1) & - alignment
;
241 if (size
> ~((size_t) 0) - (slop
+ 1))
243 __set_errno (ENOMEM
);
248 *sizep
= slop
+ size
+ 1;
253 memalign_mcheck_after (void *block
, size_t alignment
, size_t size
)
258 /* This was served by __debug_malloc, so return as is. */
259 if (alignment
<= MALLOC_ALIGNMENT
)
262 size_t slop
= (sizeof (struct hdr
) + alignment
- 1) & - alignment
;
263 struct hdr
*hdr
= ((struct hdr
*) (block
+ slop
)) - 1;
267 hdr
->block
= (void *) block
;
268 hdr
->magic2
= (uintptr_t) block
^ MAGICWORD
;
269 ((char *) &hdr
[1])[size
] = MAGICBYTE
;
270 flood ((void *) (hdr
+ 1), MALLOCFLOOD
, size
);
271 return (void *) (hdr
+ 1);
275 realloc_mcheck_before (void **ptrp
, size_t *sizep
, size_t *oldsize
,
278 size_t size
= *sizep
;
283 *victimp
= __debug_malloc (size
);
295 if (size
> ~((size_t) 0) - (sizeof (struct hdr
) + 1))
297 __set_errno (ENOMEM
);
304 __mcheck_checkptr (NULL
);
309 /* Update the oldptr for glibc realloc. */
310 *ptrp
= hdr
= ((struct hdr
*) ptr
) - 1;
317 flood ((char *) ptr
+ size
, FREEFLOOD
, osize
- size
);
320 *sizep
= sizeof (struct hdr
) + size
+ 1;
325 realloc_mcheck_after (void *ptr
, void *oldptr
, size_t size
, size_t osize
)
327 struct hdr
*hdr
= ptr
;
332 /* Malloc already added the header so don't tamper with it. */
339 hdr
->magic2
= (uintptr_t) hdr
^ MAGICWORD
;
340 ((char *) &hdr
[1])[size
] = MAGICBYTE
;
342 flood ((char *) (hdr
+ 1) + osize
, MALLOCFLOOD
, size
- osize
);
343 return (void *) (hdr
+ 1);
346 __attribute__ ((noreturn
))
348 mabort (enum mcheck_status status
)
354 msg
= _ ("memory is consistent, library is buggy\n");
357 msg
= _ ("memory clobbered before allocated block\n");
360 msg
= _ ("memory clobbered past end of allocated block\n");
363 msg
= _ ("block freed twice\n");
366 msg
= _ ("bogus mcheck_status, library is buggy\n");
372 fprintf (stderr
, "mcheck: %s", msg
);
378 /* Memory barrier so that GCC does not optimize out the argument. */
379 #define malloc_opt_barrier(x) \
380 ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; })
383 __mcheck_initialize (void (*func
) (enum mcheck_status
), bool in_pedantic
)
385 abortfunc
= (func
!= NULL
) ? func
: &mabort
;
387 switch (debug_initialized
)
390 /* Called before the first malloc was called. */
391 __debug_free (__debug_malloc (0));
394 /* Called through the initializer hook. */
395 __malloc_debug_enable (MALLOC_MCHECK_HOOK
);
399 /* Malloc was already called. Fail. */
403 pedantic
= in_pedantic
;
408 mcheck_usable_size (struct hdr
*h
)
410 return (h
- 1)->size
;