mcheck-impl.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /* mcheck debugging hooks for malloc.
  2. Copyright (C) 1990-2026 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <https://www.gnu.org/licenses/>. */
  15. #include <malloc-internal.h>
  16. #include <mcheck.h>
  17. #include <libintl.h>
  18. #include <stdint.h>
  19. #include <stdio.h>
  20. /* Arbitrary magical numbers. */
  21. #define MAGICWORD 0xfedabeeb
  22. #define MAGICFREE 0xd8675309
  23. #define MAGICBYTE ((char) 0xd7)
  24. #define MALLOCFLOOD ((char) 0x93)
  25. #define FREEFLOOD ((char) 0x95)
  26. /* Function to call when something awful happens. */
  27. static void (*abortfunc) (enum mcheck_status);
  28. struct hdr
  29. {
  30. size_t size; /* Exact size requested by user. */
  31. unsigned long int magic; /* Magic number to check header integrity. */
  32. struct hdr *prev;
  33. struct hdr *next;
  34. void *block; /* Real block allocated, for memalign. */
  35. unsigned long int magic2; /* Extra, keeps us doubleword aligned. */
  36. } __attribute__ ((aligned (MALLOC_ALIGNMENT)));
  37. /* This is the beginning of the list of all memory blocks allocated.
  38. It is only constructed if the pedantic testing is requested. */
  39. static struct hdr *root;
  40. /* Nonzero if pedentic checking of all blocks is requested. */
  41. static bool pedantic;
  42. #if defined _LIBC || defined STDC_HEADERS || defined USG
  43. # include <string.h>
  44. # define flood memset
  45. #else
  46. static void flood (void *, int, size_t);
  47. static void
  48. flood (void *ptr, int val, size_t size)
  49. {
  50. char *cp = ptr;
  51. while (size--)
  52. *cp++ = val;
  53. }
  54. #endif
  55. static enum mcheck_status
  56. checkhdr (const struct hdr *hdr)
  57. {
  58. enum mcheck_status status;
  59. bool mcheck_used = __is_malloc_debug_enabled (MALLOC_MCHECK_HOOK);
  60. if (!mcheck_used)
  61. /* Maybe the mcheck used is disabled? This happens when we find
  62. an error and report it. */
  63. return MCHECK_OK;
  64. switch (hdr->magic ^ ((uintptr_t) hdr->prev + (uintptr_t) hdr->next))
  65. {
  66. default:
  67. status = MCHECK_HEAD;
  68. break;
  69. case MAGICFREE:
  70. status = MCHECK_FREE;
  71. break;
  72. case MAGICWORD:
  73. if (((char *) &hdr[1])[hdr->size] != MAGICBYTE)
  74. status = MCHECK_TAIL;
  75. else if ((hdr->magic2 ^ (uintptr_t) hdr->block) != MAGICWORD)
  76. status = MCHECK_HEAD;
  77. else
  78. status = MCHECK_OK;
  79. break;
  80. }
  81. if (status != MCHECK_OK)
  82. {
  83. mcheck_used = 0;
  84. (*abortfunc) (status);
  85. mcheck_used = 1;
  86. }
  87. return status;
  88. }
  89. static enum mcheck_status
  90. __mcheck_checkptr (const void *ptr)
  91. {
  92. if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK))
  93. return MCHECK_DISABLED;
  94. if (ptr != NULL)
  95. return checkhdr (((struct hdr *) ptr) - 1);
  96. /* Walk through all the active blocks and test whether they were tampered
  97. with. */
  98. struct hdr *runp = root;
  99. /* Temporarily turn off the checks. */
  100. pedantic = false;
  101. while (runp != NULL)
  102. {
  103. (void) checkhdr (runp);
  104. runp = runp->next;
  105. }
  106. /* Turn checks on again. */
  107. pedantic = true;
  108. return MCHECK_OK;
  109. }
  110. static void
  111. unlink_blk (struct hdr *ptr)
  112. {
  113. if (ptr->next != NULL)
  114. {
  115. ptr->next->prev = ptr->prev;
  116. ptr->next->magic = MAGICWORD ^ ((uintptr_t) ptr->next->prev
  117. + (uintptr_t) ptr->next->next);
  118. }
  119. if (ptr->prev != NULL)
  120. {
  121. ptr->prev->next = ptr->next;
  122. ptr->prev->magic = MAGICWORD ^ ((uintptr_t) ptr->prev->prev
  123. + (uintptr_t) ptr->prev->next);
  124. }
  125. else
  126. root = ptr->next;
  127. }
  128. static void
  129. link_blk (struct hdr *hdr)
  130. {
  131. hdr->prev = NULL;
  132. hdr->next = root;
  133. root = hdr;
  134. hdr->magic = MAGICWORD ^ (uintptr_t) hdr->next;
  135. /* And the next block. */
  136. if (hdr->next != NULL)
  137. {
  138. hdr->next->prev = hdr;
  139. hdr->next->magic = MAGICWORD ^ ((uintptr_t) hdr
  140. + (uintptr_t) hdr->next->next);
  141. }
  142. }
  143. static void *
  144. free_mcheck (void *ptr)
  145. {
  146. if (pedantic)
  147. __mcheck_checkptr (NULL);
  148. if (ptr)
  149. {
  150. struct hdr *hdr = ((struct hdr *) ptr) - 1;
  151. checkhdr (hdr);
  152. hdr->magic = MAGICFREE;
  153. hdr->magic2 = MAGICFREE;
  154. unlink_blk (hdr);
  155. hdr->prev = hdr->next = NULL;
  156. flood (ptr, FREEFLOOD, hdr->size);
  157. ptr = hdr->block;
  158. }
  159. return ptr;
  160. }
  161. static bool
  162. malloc_mcheck_before (size_t *sizep, void **victimp)
  163. {
  164. size_t size = *sizep;
  165. if (pedantic)
  166. __mcheck_checkptr (NULL);
  167. if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
  168. {
  169. __set_errno (ENOMEM);
  170. *victimp = NULL;
  171. return true;
  172. }
  173. *sizep = sizeof (struct hdr) + size + 1;
  174. return false;
  175. }
  176. static void *
  177. malloc_mcheck_after (void *mem, size_t size)
  178. {
  179. struct hdr *hdr = mem;
  180. if (hdr == NULL)
  181. return NULL;
  182. hdr->size = size;
  183. link_blk (hdr);
  184. hdr->block = hdr;
  185. hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
  186. ((char *) &hdr[1])[size] = MAGICBYTE;
  187. flood ((void *) (hdr + 1), MALLOCFLOOD, size);
  188. return (void *) (hdr + 1);
  189. }
  190. static bool
  191. memalign_mcheck_before (size_t alignment, size_t *sizep, void **victimp)
  192. {
  193. struct hdr *hdr;
  194. size_t slop, size = *sizep;
  195. /* Punt to malloc to avoid double headers. */
  196. if (alignment <= MALLOC_ALIGNMENT)
  197. {
  198. *victimp = __debug_malloc (size);
  199. return true;
  200. }
  201. if (pedantic)
  202. __mcheck_checkptr (NULL);
  203. slop = (sizeof *hdr + alignment - 1) & - alignment;
  204. if (size > ~((size_t) 0) - (slop + 1))
  205. {
  206. __set_errno (ENOMEM);
  207. *victimp = NULL;
  208. return true;
  209. }
  210. *sizep = slop + size + 1;
  211. return false;
  212. }
  213. static void *
  214. memalign_mcheck_after (void *block, size_t alignment, size_t size)
  215. {
  216. if (block == NULL)
  217. return NULL;
  218. /* This was served by __debug_malloc, so return as is. */
  219. if (alignment <= MALLOC_ALIGNMENT)
  220. return block;
  221. size_t slop = (sizeof (struct hdr) + alignment - 1) & - alignment;
  222. struct hdr *hdr = ((struct hdr *) (block + slop)) - 1;
  223. hdr->size = size;
  224. link_blk (hdr);
  225. hdr->block = (void *) block;
  226. hdr->magic2 = (uintptr_t) block ^ MAGICWORD;
  227. ((char *) &hdr[1])[size] = MAGICBYTE;
  228. flood ((void *) (hdr + 1), MALLOCFLOOD, size);
  229. return (void *) (hdr + 1);
  230. }
  231. static bool
  232. realloc_mcheck_before (void **ptrp, size_t *sizep, size_t *oldsize,
  233. void **victimp)
  234. {
  235. size_t size = *sizep;
  236. void *ptr = *ptrp;
  237. if (ptr == NULL)
  238. {
  239. *victimp = __debug_malloc (size);
  240. *oldsize = 0;
  241. return true;
  242. }
  243. if (size == 0)
  244. {
  245. __debug_free (ptr);
  246. *victimp = NULL;
  247. return true;
  248. }
  249. if (size > ~((size_t) 0) - (sizeof (struct hdr) + 1))
  250. {
  251. __set_errno (ENOMEM);
  252. *victimp = NULL;
  253. *oldsize = 0;
  254. return true;
  255. }
  256. if (pedantic)
  257. __mcheck_checkptr (NULL);
  258. struct hdr *hdr;
  259. size_t osize;
  260. /* Update the oldptr for glibc realloc. */
  261. *ptrp = hdr = ((struct hdr *) ptr) - 1;
  262. osize = hdr->size;
  263. checkhdr (hdr);
  264. unlink_blk (hdr);
  265. if (size < osize)
  266. flood ((char *) ptr + size, FREEFLOOD, osize - size);
  267. *oldsize = osize;
  268. *sizep = sizeof (struct hdr) + size + 1;
  269. return false;
  270. }
  271. static void *
  272. realloc_mcheck_after (void *ptr, void *oldptr, size_t size, size_t osize)
  273. {
  274. struct hdr *hdr = ptr;
  275. if (hdr == NULL)
  276. return NULL;
  277. /* Malloc already added the header so don't tamper with it. */
  278. if (oldptr == NULL)
  279. return ptr;
  280. hdr->size = size;
  281. link_blk (hdr);
  282. hdr->block = hdr;
  283. hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
  284. ((char *) &hdr[1])[size] = MAGICBYTE;
  285. if (size > osize)
  286. flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
  287. return (void *) (hdr + 1);
  288. }
  289. __attribute__ ((noreturn))
  290. static void
  291. mabort (enum mcheck_status status)
  292. {
  293. const char *msg;
  294. switch (status)
  295. {
  296. case MCHECK_OK:
  297. msg = _ ("memory is consistent, library is buggy\n");
  298. break;
  299. case MCHECK_HEAD:
  300. msg = _ ("memory clobbered before allocated block\n");
  301. break;
  302. case MCHECK_TAIL:
  303. msg = _ ("memory clobbered past end of allocated block\n");
  304. break;
  305. case MCHECK_FREE:
  306. msg = _ ("block freed twice\n");
  307. break;
  308. default:
  309. msg = _ ("bogus mcheck_status, library is buggy\n");
  310. break;
  311. }
  312. #ifdef _LIBC
  313. __libc_fatal (msg);
  314. #else
  315. fprintf (stderr, "mcheck: %s", msg);
  316. fflush (stderr);
  317. abort ();
  318. #endif
  319. }
  320. /* Memory barrier so that GCC does not optimize out the argument. */
  321. #define malloc_opt_barrier(x) \
  322. ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; })
  323. static int
  324. __mcheck_initialize (void (*func) (enum mcheck_status), bool in_pedantic)
  325. {
  326. abortfunc = (func != NULL) ? func : &mabort;
  327. switch (debug_initialized)
  328. {
  329. case -1:
  330. /* Called before the first malloc was called. */
  331. __debug_free (__debug_malloc (0));
  332. [[fallthrough]];
  333. case 0:
  334. /* Called through the initializer hook. */
  335. __malloc_debug_enable (MALLOC_MCHECK_HOOK);
  336. break;
  337. case 1:
  338. default:
  339. /* Malloc was already called. Fail. */
  340. return -1;
  341. }
  342. pedantic = in_pedantic;
  343. return 0;
  344. }
  345. static int
  346. mcheck_usable_size (struct hdr *h)
  347. {
  348. return (h - 1)->size;
  349. }