tst-mallocstate.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /* Emulate Emacs heap dumping to test malloc_set_state.
  2. Copyright (C) 2001-2019 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
  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.
  9. The GNU C Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with the GNU C Library; if not, see
  15. <http://www.gnu.org/licenses/>. */
  16. #include <errno.h>
  17. #include <stdbool.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <libc-symbols.h>
  21. #include <shlib-compat.h>
  22. #include <support/check.h>
  23. #include <support/support.h>
  24. #include <support/test-driver.h>
  25. #include "malloc.h"
  26. #if TEST_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
  27. /* Make the compatibility symbols availabile to this test case. */
  28. void *malloc_get_state (void);
  29. compat_symbol_reference (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
  30. int malloc_set_state (void *);
  31. compat_symbol_reference (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
  32. /* Maximum object size in the fake heap. */
  33. enum { max_size = 64 };
  34. /* Allocation actions. These are randomized actions executed on the
  35. dumped heap (see allocation_tasks below). They are interspersed
  36. with operations on the new heap (see heap_activity). */
  37. enum allocation_action
  38. {
  39. action_free, /* Dumped and freed. */
  40. action_realloc, /* Dumped and realloc'ed. */
  41. action_realloc_same, /* Dumped and realloc'ed, same size. */
  42. action_realloc_smaller, /* Dumped and realloc'ed, shrinked. */
  43. action_count
  44. };
  45. /* Dumped heap. Initialize it, so that the object is placed into the
  46. .data section, for increased realism. The size is an upper bound;
  47. we use about half of the space. */
  48. static size_t dumped_heap[action_count * max_size * max_size
  49. / sizeof (size_t)] = {1};
  50. /* Next free space in the dumped heap. Also top of the heap at the
  51. end of the initialization procedure. */
  52. static size_t *next_heap_chunk;
  53. /* Copied from malloc.c and hooks.c. The version is deliberately
  54. lower than the final version of malloc_set_state. */
  55. # define NBINS 128
  56. # define MALLOC_STATE_MAGIC 0x444c4541l
  57. # define MALLOC_STATE_VERSION (0 * 0x100l + 4l)
  58. static struct
  59. {
  60. long magic;
  61. long version;
  62. void *av[NBINS * 2 + 2];
  63. char *sbrk_base;
  64. int sbrked_mem_bytes;
  65. unsigned long trim_threshold;
  66. unsigned long top_pad;
  67. unsigned int n_mmaps_max;
  68. unsigned long mmap_threshold;
  69. int check_action;
  70. unsigned long max_sbrked_mem;
  71. unsigned long max_total_mem;
  72. unsigned int n_mmaps;
  73. unsigned int max_n_mmaps;
  74. unsigned long mmapped_mem;
  75. unsigned long max_mmapped_mem;
  76. int using_malloc_checking;
  77. unsigned long max_fast;
  78. unsigned long arena_test;
  79. unsigned long arena_max;
  80. unsigned long narenas;
  81. } save_state =
  82. {
  83. .magic = MALLOC_STATE_MAGIC,
  84. .version = MALLOC_STATE_VERSION,
  85. };
  86. /* Allocate a blob in the fake heap. */
  87. static void *
  88. dumped_heap_alloc (size_t length)
  89. {
  90. /* malloc needs three state bits in the size field, so the minimum
  91. alignment is 8 even on 32-bit architectures. malloc_set_state
  92. should be compatible with such heaps even if it currently
  93. provides more alignment to applications. */
  94. enum
  95. {
  96. heap_alignment = 8,
  97. heap_alignment_mask = heap_alignment - 1
  98. };
  99. _Static_assert (sizeof (size_t) <= heap_alignment,
  100. "size_t compatible with heap alignment");
  101. /* Need at least this many bytes for metadata and application
  102. data. */
  103. size_t chunk_size = sizeof (size_t) + length;
  104. /* Round up the allocation size to the heap alignment. */
  105. chunk_size += heap_alignment_mask;
  106. chunk_size &= ~heap_alignment_mask;
  107. TEST_VERIFY_EXIT ((chunk_size & 3) == 0);
  108. if (next_heap_chunk == NULL)
  109. /* Initialize the top of the heap. Add one word of zero padding,
  110. to match existing practice. */
  111. {
  112. dumped_heap[0] = 0;
  113. next_heap_chunk = dumped_heap + 1;
  114. }
  115. else
  116. /* The previous chunk is allocated. */
  117. chunk_size |= 1;
  118. *next_heap_chunk = chunk_size;
  119. /* User data starts after the chunk header. */
  120. void *result = next_heap_chunk + 1;
  121. next_heap_chunk += chunk_size / sizeof (size_t);
  122. /* Mark the previous chunk as used. */
  123. *next_heap_chunk = 1;
  124. return result;
  125. }
  126. /* Global seed variable for the random number generator. */
  127. static unsigned long long global_seed;
  128. /* Simple random number generator. The numbers are in the range from
  129. 0 to UINT_MAX (inclusive). */
  130. static unsigned int
  131. rand_next (unsigned long long *seed)
  132. {
  133. /* Linear congruential generated as used for MMIX. */
  134. *seed = *seed * 6364136223846793005ULL + 1442695040888963407ULL;
  135. return *seed >> 32;
  136. }
  137. /* Fill LENGTH bytes at BUFFER with random contents, as determined by
  138. SEED. */
  139. static void
  140. randomize_buffer (unsigned char *buffer, size_t length,
  141. unsigned long long seed)
  142. {
  143. for (size_t i = 0; i < length; ++i)
  144. buffer[i] = rand_next (&seed);
  145. }
  146. /* Dumps the buffer to standard output, in hexadecimal. */
  147. static void
  148. dump_hex (unsigned char *buffer, size_t length)
  149. {
  150. for (int i = 0; i < length; ++i)
  151. printf (" %02X", buffer[i]);
  152. }
  153. /* Set to true if an error is encountered. */
  154. static bool errors = false;
  155. /* Keep track of object allocations. */
  156. struct allocation
  157. {
  158. unsigned char *data;
  159. unsigned int size;
  160. unsigned int seed;
  161. };
  162. /* Check that the allocation task allocation has the expected
  163. contents. */
  164. static void
  165. check_allocation (const struct allocation *alloc, int index)
  166. {
  167. size_t size = alloc->size;
  168. if (alloc->data == NULL)
  169. {
  170. printf ("error: NULL pointer for allocation of size %zu at %d, seed %u\n",
  171. size, index, alloc->seed);
  172. errors = true;
  173. return;
  174. }
  175. unsigned char expected[4096];
  176. if (size > sizeof (expected))
  177. {
  178. printf ("error: invalid allocation size %zu at %d, seed %u\n",
  179. size, index, alloc->seed);
  180. errors = true;
  181. return;
  182. }
  183. randomize_buffer (expected, size, alloc->seed);
  184. if (memcmp (alloc->data, expected, size) != 0)
  185. {
  186. printf ("error: allocation %d data mismatch, size %zu, seed %u\n",
  187. index, size, alloc->seed);
  188. printf (" expected:");
  189. dump_hex (expected, size);
  190. putc ('\n', stdout);
  191. printf (" actual:");
  192. dump_hex (alloc->data, size);
  193. putc ('\n', stdout);
  194. errors = true;
  195. }
  196. }
  197. /* A heap allocation combined with pending actions on it. */
  198. struct allocation_task
  199. {
  200. struct allocation allocation;
  201. enum allocation_action action;
  202. };
  203. /* Allocation tasks. Initialized by init_allocation_tasks and used by
  204. perform_allocations. */
  205. enum { allocation_task_count = action_count * max_size };
  206. static struct allocation_task allocation_tasks[allocation_task_count];
  207. /* Fisher-Yates shuffle of allocation_tasks. */
  208. static void
  209. shuffle_allocation_tasks (void)
  210. {
  211. for (int i = 0; i < allocation_task_count - 1; ++i)
  212. {
  213. /* Pick pair in the tail of the array. */
  214. int j = i + (rand_next (&global_seed)
  215. % ((unsigned) (allocation_task_count - i)));
  216. TEST_VERIFY_EXIT (j >= 0 && j < allocation_task_count);
  217. /* Exchange. */
  218. struct allocation_task tmp = allocation_tasks[i];
  219. allocation_tasks[i] = allocation_tasks[j];
  220. allocation_tasks[j] = tmp;
  221. }
  222. }
  223. /* Set up the allocation tasks and the dumped heap. */
  224. static void
  225. initial_allocations (void)
  226. {
  227. /* Initialize in a position-dependent way. */
  228. for (int i = 0; i < allocation_task_count; ++i)
  229. allocation_tasks[i] = (struct allocation_task)
  230. {
  231. .allocation =
  232. {
  233. .size = 1 + (i / action_count),
  234. .seed = i,
  235. },
  236. .action = i % action_count
  237. };
  238. /* Execute the tasks in a random order. */
  239. shuffle_allocation_tasks ();
  240. /* Initialize the contents of the dumped heap. */
  241. for (int i = 0; i < allocation_task_count; ++i)
  242. {
  243. struct allocation_task *task = allocation_tasks + i;
  244. task->allocation.data = dumped_heap_alloc (task->allocation.size);
  245. randomize_buffer (task->allocation.data, task->allocation.size,
  246. task->allocation.seed);
  247. }
  248. for (int i = 0; i < allocation_task_count; ++i)
  249. check_allocation (&allocation_tasks[i].allocation, i);
  250. }
  251. /* Indicates whether init_heap has run. This variable needs to be
  252. volatile because malloc is declared __THROW, which implies it is a
  253. leaf function, but we expect it to run our hooks. */
  254. static volatile bool heap_initialized;
  255. /* Executed by glibc malloc, through __malloc_initialize_hook
  256. below. */
  257. static void
  258. init_heap (void)
  259. {
  260. if (test_verbose)
  261. printf ("info: performing heap initialization\n");
  262. heap_initialized = true;
  263. /* Populate the dumped heap. */
  264. initial_allocations ();
  265. /* Complete initialization of the saved heap data structure. */
  266. save_state.sbrk_base = (void *) dumped_heap;
  267. save_state.sbrked_mem_bytes = sizeof (dumped_heap);
  268. /* Top pointer. Adjust so that it points to the start of struct
  269. malloc_chunk. */
  270. save_state.av[2] = (void *) (next_heap_chunk - 1);
  271. /* Integrate the dumped heap into the process heap. */
  272. TEST_VERIFY_EXIT (malloc_set_state (&save_state) == 0);
  273. }
  274. /* Interpose the initialization callback. */
  275. void (*volatile __malloc_initialize_hook) (void) = init_heap;
  276. /* Simulate occasional unrelated heap activity in the non-dumped
  277. heap. */
  278. enum { heap_activity_allocations_count = 32 };
  279. static struct allocation heap_activity_allocations
  280. [heap_activity_allocations_count] = {};
  281. static int heap_activity_seed_counter = 1000 * 1000;
  282. static void
  283. heap_activity (void)
  284. {
  285. /* Only do this from time to time. */
  286. if ((rand_next (&global_seed) % 4) == 0)
  287. {
  288. int slot = rand_next (&global_seed) % heap_activity_allocations_count;
  289. struct allocation *alloc = heap_activity_allocations + slot;
  290. if (alloc->data == NULL)
  291. {
  292. alloc->size = rand_next (&global_seed) % (4096U + 1);
  293. alloc->data = xmalloc (alloc->size);
  294. alloc->seed = heap_activity_seed_counter++;
  295. randomize_buffer (alloc->data, alloc->size, alloc->seed);
  296. check_allocation (alloc, 1000 + slot);
  297. }
  298. else
  299. {
  300. check_allocation (alloc, 1000 + slot);
  301. free (alloc->data);
  302. alloc->data = NULL;
  303. }
  304. }
  305. }
  306. static void
  307. heap_activity_deallocate (void)
  308. {
  309. for (int i = 0; i < heap_activity_allocations_count; ++i)
  310. free (heap_activity_allocations[i].data);
  311. }
  312. /* Perform a full heap check across the dumped heap allocation tasks,
  313. and the simulated heap activity directly above. */
  314. static void
  315. full_heap_check (void)
  316. {
  317. /* Dumped heap. */
  318. for (int i = 0; i < allocation_task_count; ++i)
  319. if (allocation_tasks[i].allocation.data != NULL)
  320. check_allocation (&allocation_tasks[i].allocation, i);
  321. /* Heap activity allocations. */
  322. for (int i = 0; i < heap_activity_allocations_count; ++i)
  323. if (heap_activity_allocations[i].data != NULL)
  324. check_allocation (heap_activity_allocations + i, i);
  325. }
  326. /* Used as an optimization barrier to force a heap allocation. */
  327. __attribute__ ((noinline, noclone))
  328. static void
  329. my_free (void *ptr)
  330. {
  331. free (ptr);
  332. }
  333. static int
  334. do_test (void)
  335. {
  336. my_free (malloc (1));
  337. TEST_VERIFY_EXIT (heap_initialized);
  338. /* The first pass performs the randomly generated allocation
  339. tasks. */
  340. if (test_verbose)
  341. printf ("info: first pass through allocation tasks\n");
  342. full_heap_check ();
  343. /* Execute the post-undump tasks in a random order. */
  344. shuffle_allocation_tasks ();
  345. for (int i = 0; i < allocation_task_count; ++i)
  346. {
  347. heap_activity ();
  348. struct allocation_task *task = allocation_tasks + i;
  349. switch (task->action)
  350. {
  351. case action_free:
  352. check_allocation (&task->allocation, i);
  353. free (task->allocation.data);
  354. task->allocation.data = NULL;
  355. break;
  356. case action_realloc:
  357. check_allocation (&task->allocation, i);
  358. task->allocation.data = xrealloc
  359. (task->allocation.data, task->allocation.size + max_size);
  360. check_allocation (&task->allocation, i);
  361. break;
  362. case action_realloc_same:
  363. check_allocation (&task->allocation, i);
  364. task->allocation.data = xrealloc
  365. (task->allocation.data, task->allocation.size);
  366. check_allocation (&task->allocation, i);
  367. break;
  368. case action_realloc_smaller:
  369. check_allocation (&task->allocation, i);
  370. size_t new_size = task->allocation.size - 1;
  371. task->allocation.data = xrealloc (task->allocation.data, new_size);
  372. if (new_size == 0)
  373. {
  374. if (task->allocation.data != NULL)
  375. {
  376. printf ("error: realloc with size zero did not deallocate\n");
  377. errors = true;
  378. }
  379. /* No further action on this task. */
  380. task->action = action_free;
  381. }
  382. else
  383. {
  384. task->allocation.size = new_size;
  385. check_allocation (&task->allocation, i);
  386. }
  387. break;
  388. case action_count:
  389. FAIL_EXIT1 ("task->action should never be action_count");
  390. }
  391. full_heap_check ();
  392. }
  393. /* The second pass frees the objects which were allocated during the
  394. first pass. */
  395. if (test_verbose)
  396. printf ("info: second pass through allocation tasks\n");
  397. shuffle_allocation_tasks ();
  398. for (int i = 0; i < allocation_task_count; ++i)
  399. {
  400. heap_activity ();
  401. struct allocation_task *task = allocation_tasks + i;
  402. switch (task->action)
  403. {
  404. case action_free:
  405. /* Already freed, nothing to do. */
  406. break;
  407. case action_realloc:
  408. case action_realloc_same:
  409. case action_realloc_smaller:
  410. check_allocation (&task->allocation, i);
  411. free (task->allocation.data);
  412. task->allocation.data = NULL;
  413. break;
  414. case action_count:
  415. FAIL_EXIT1 ("task->action should never be action_count");
  416. }
  417. full_heap_check ();
  418. }
  419. heap_activity_deallocate ();
  420. /* Check that the malloc_get_state stub behaves in the intended
  421. way. */
  422. errno = 0;
  423. if (malloc_get_state () != NULL)
  424. {
  425. printf ("error: malloc_get_state succeeded\n");
  426. errors = true;
  427. }
  428. if (errno != ENOSYS)
  429. {
  430. printf ("error: malloc_get_state: %m\n");
  431. errors = true;
  432. }
  433. return errors;
  434. }
  435. #else
  436. static int
  437. do_test (void)
  438. {
  439. return 77;
  440. }
  441. #endif
  442. #include <support/test-driver.c>