stdlib: Undo post review change to 16adc58e73f3 [BZ #27749]
[glibc.git] / malloc / mcheck-impl.c
blob1625e63f857740dec0fcfe4e5f9c98f0808a9aa0
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>
20 #include <mcheck.h>
21 #include <libintl.h>
22 #include <stdint.h>
23 #include <stdio.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);
35 struct hdr
37 size_t size; /* Exact size requested by user. */
38 unsigned long int magic; /* Magic number to check header integrity. */
39 struct hdr *prev;
40 struct hdr *next;
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. */
50 static bool pedantic;
52 #if defined _LIBC || defined STDC_HEADERS || defined USG
53 # include <string.h>
54 # define flood memset
55 #else
56 static void flood (void *, int, size_t);
57 static void
58 flood (void *ptr, int val, size_t size)
60 char *cp = ptr;
61 while (size--)
62 *cp++ = val;
64 #endif
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);
72 if (!mcheck_used)
73 /* Maybe the mcheck used is disabled? This happens when we find
74 an error and report it. */
75 return MCHECK_OK;
77 switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
79 default:
80 status = MCHECK_HEAD;
81 break;
82 case MAGICFREE:
83 status = MCHECK_FREE;
84 break;
85 case MAGICWORD:
86 if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
87 status = MCHECK_TAIL;
88 else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
89 status = MCHECK_HEAD;
90 else
91 status = MCHECK_OK;
92 break;
94 if (status != MCHECK_OK)
96 mcheck_used = 0;
97 (*abortfunc) (status);
98 mcheck_used = 1;
100 return 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;
109 if (ptr != NULL)
110 return checkhdr (((struct hdr *) ptr) - 1);
112 /* Walk through all the active blocks and test whether they were tampered
113 with. */
114 struct hdr *runp = root;
116 /* Temporarily turn off the checks. */
117 pedantic = false;
119 while (runp != NULL)
121 (void) checkhdr (runp);
123 runp = runp->next;
126 /* Turn checks on again. */
127 pedantic = true;
129 return MCHECK_OK;
132 static void
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);
147 else
148 root = ptr->next;
151 static void
152 link_blk (struct hdr *hdr)
154 hdr->prev = NULL;
155 hdr->next = root;
156 root = 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);
168 static void *
169 free_mcheck (void *ptr)
171 if (pedantic)
172 __mcheck_checkptr (NULL);
173 if (ptr)
175 struct hdr *hdr = ((struct hdr *) ptr) - 1;
176 checkhdr (hdr);
177 hdr->magic = MAGICFREE;
178 hdr->magic2 = MAGICFREE;
179 unlink_blk (hdr);
180 hdr->prev = hdr->next = NULL;
181 flood (ptr, FREEFLOOD, hdr->size);
182 ptr = hdr->block;
184 return ptr;
187 static bool
188 malloc_mcheck_before (size_t *sizep, void **victimp)
190 size_t size = *sizep;
192 if (pedantic)
193 __mcheck_checkptr (NULL);
195 if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
197 __set_errno (ENOMEM);
198 *victimp = NULL;
199 return true;
202 *sizep = sizeof (struct hdr) + size + 1;
203 return false;
206 static void *
207 malloc_mcheck_after (void *mem, size_t size)
209 struct hdr *hdr = mem;
211 if (hdr == NULL)
212 return NULL;
214 hdr->size = size;
215 link_blk (hdr);
216 hdr->block = hdr;
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);
223 static bool
224 memalign_mcheck_before (size_t alignment, size_t *sizep, void **victimp)
226 struct hdr *hdr;
227 size_t slop, size = *sizep;
229 /* Punt to malloc to avoid double headers. */
230 if (alignment <= MALLOC_ALIGNMENT)
232 *victimp = __debug_malloc (size);
233 return true;
236 if (pedantic)
237 __mcheck_checkptr (NULL);
239 slop = (sizeof *hdr + alignment - 1) & - alignment;
241 if (size > ~((size_t) 0) - (slop + 1))
243 __set_errno (ENOMEM);
244 *victimp = NULL;
245 return true;
248 *sizep = slop + size + 1;
249 return false;
252 static void *
253 memalign_mcheck_after (void *block, size_t alignment, size_t size)
255 if (block == NULL)
256 return NULL;
258 /* This was served by __debug_malloc, so return as is. */
259 if (alignment <= MALLOC_ALIGNMENT)
260 return block;
262 size_t slop = (sizeof (struct hdr) + alignment - 1) & - alignment;
263 struct hdr *hdr = ((struct hdr *) (block + slop)) - 1;
265 hdr->size = size;
266 link_blk (hdr);
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);
274 static bool
275 realloc_mcheck_before (void **ptrp, size_t *sizep, size_t *oldsize,
276 void **victimp)
278 size_t size = *sizep;
279 void *ptr = *ptrp;
281 if (ptr == NULL)
283 *victimp = __debug_malloc (size);
284 *oldsize = 0;
285 return true;
288 if (size == 0)
290 __debug_free (ptr);
291 *victimp = NULL;
292 return true;
295 if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
297 __set_errno (ENOMEM);
298 *victimp = NULL;
299 *oldsize = 0;
300 return true;
303 if (pedantic)
304 __mcheck_checkptr (NULL);
306 struct hdr *hdr;
307 size_t osize;
309 /* Update the oldptr for glibc realloc. */
310 *ptrp = hdr = ((struct hdr *) ptr) - 1;
312 osize = hdr->size;
314 checkhdr (hdr);
315 unlink_blk (hdr);
316 if (size < osize)
317 flood ((char *) ptr + size, FREEFLOOD, osize - size);
319 *oldsize = osize;
320 *sizep = sizeof (struct hdr) + size + 1;
321 return false;
324 static void *
325 realloc_mcheck_after (void *ptr, void *oldptr, size_t size, size_t osize)
327 struct hdr *hdr = ptr;
329 if (hdr == NULL)
330 return NULL;
332 /* Malloc already added the header so don't tamper with it. */
333 if (oldptr == NULL)
334 return ptr;
336 hdr->size = size;
337 link_blk (hdr);
338 hdr->block = hdr;
339 hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
340 ((char *) &hdr[1])[size] = MAGICBYTE;
341 if (size > osize)
342 flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
343 return (void *) (hdr + 1);
346 __attribute__ ((noreturn))
347 static void
348 mabort (enum mcheck_status status)
350 const char *msg;
351 switch (status)
353 case MCHECK_OK:
354 msg = _ ("memory is consistent, library is buggy\n");
355 break;
356 case MCHECK_HEAD:
357 msg = _ ("memory clobbered before allocated block\n");
358 break;
359 case MCHECK_TAIL:
360 msg = _ ("memory clobbered past end of allocated block\n");
361 break;
362 case MCHECK_FREE:
363 msg = _ ("block freed twice\n");
364 break;
365 default:
366 msg = _ ("bogus mcheck_status, library is buggy\n");
367 break;
369 #ifdef _LIBC
370 __libc_fatal (msg);
371 #else
372 fprintf (stderr, "mcheck: %s", msg);
373 fflush (stderr);
374 abort ();
375 #endif
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; })
382 static int
383 __mcheck_initialize (void (*func) (enum mcheck_status), bool in_pedantic)
385 abortfunc = (func != NULL) ? func : &mabort;
387 switch (debug_initialized)
389 case -1:
390 /* Called before the first malloc was called. */
391 __debug_free (__debug_malloc (0));
392 /* FALLTHROUGH */
393 case 0:
394 /* Called through the initializer hook. */
395 __malloc_debug_enable (MALLOC_MCHECK_HOOK);
396 break;
397 case 1:
398 default:
399 /* Malloc was already called. Fail. */
400 return -1;
403 pedantic = in_pedantic;
404 return 0;
407 static int
408 mcheck_usable_size (struct hdr *h)
410 return (h - 1)->size;