tst-dynarray-fail.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /* Test allocation failures with dynamic arrays.
  2. Copyright (C) 2017-2019 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. <http://www.gnu.org/licenses/>. */
  15. /* This test is separate from tst-dynarray because it cannot run under
  16. valgrind. */
  17. #include "tst-dynarray-shared.h"
  18. #include <mcheck.h>
  19. #include <stdio.h>
  20. #include <support/check.h>
  21. #include <support/support.h>
  22. #include <support/xunistd.h>
  23. #include <sys/mman.h>
  24. #include <sys/resource.h>
  25. #include <unistd.h>
  26. /* Data structure to fill up the heap. */
  27. struct heap_filler
  28. {
  29. struct heap_filler *next;
  30. };
  31. /* Allocate objects until the heap is full. */
  32. static struct heap_filler *
  33. fill_heap (void)
  34. {
  35. size_t pad = 4096;
  36. struct heap_filler *head = NULL;
  37. while (true)
  38. {
  39. struct heap_filler *new_head = malloc (sizeof (*new_head) + pad);
  40. if (new_head == NULL)
  41. {
  42. if (pad > 0)
  43. {
  44. /* Try again with smaller allocations. */
  45. pad = 0;
  46. continue;
  47. }
  48. else
  49. break;
  50. }
  51. new_head->next = head;
  52. head = new_head;
  53. }
  54. return head;
  55. }
  56. /* Free the heap-filling allocations, so that we can continue testing
  57. and detect memory leaks elsewhere. */
  58. static void
  59. free_fill_heap (struct heap_filler *head)
  60. {
  61. while (head != NULL)
  62. {
  63. struct heap_filler *next = head->next;
  64. free (head);
  65. head = next;
  66. }
  67. }
  68. /* Check allocation failures for int arrays (without an element free
  69. function). */
  70. static void
  71. test_int_fail (void)
  72. {
  73. /* Exercise failure in add/emplace.
  74. do_add: Use emplace (false) or add (true) to add elements.
  75. do_finalize: Perform finalization at the end (instead of free). */
  76. for (int do_add = 0; do_add < 2; ++do_add)
  77. for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
  78. {
  79. struct dynarray_int dyn;
  80. dynarray_int_init (&dyn);
  81. size_t count = 0;
  82. while (true)
  83. {
  84. if (do_add)
  85. {
  86. dynarray_int_add (&dyn, 0);
  87. if (dynarray_int_has_failed (&dyn))
  88. break;
  89. }
  90. else
  91. {
  92. int *place = dynarray_int_emplace (&dyn);
  93. if (place == NULL)
  94. break;
  95. TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
  96. *place = 0;
  97. }
  98. ++count;
  99. }
  100. printf ("info: %s: failure after %zu elements\n", __func__, count);
  101. TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn));
  102. if (do_finalize)
  103. {
  104. struct int_array result = { (int *) (uintptr_t) -1, -1 };
  105. TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
  106. TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
  107. TEST_VERIFY_EXIT (result.length == (size_t) -1);
  108. }
  109. else
  110. dynarray_int_free (&dyn);
  111. CHECK_INIT_STATE (int, &dyn);
  112. }
  113. /* Exercise failure in finalize. */
  114. for (int do_add = 0; do_add < 2; ++do_add)
  115. {
  116. struct dynarray_int dyn;
  117. dynarray_int_init (&dyn);
  118. for (unsigned int i = 0; i < 10000; ++i)
  119. {
  120. if (do_add)
  121. {
  122. dynarray_int_add (&dyn, i);
  123. TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
  124. }
  125. else
  126. {
  127. int *place = dynarray_int_emplace (&dyn);
  128. TEST_VERIFY_EXIT (place != NULL);
  129. *place = i;
  130. }
  131. }
  132. TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
  133. struct heap_filler *heap_filler = fill_heap ();
  134. struct int_array result = { (int *) (uintptr_t) -1, -1 };
  135. TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
  136. TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
  137. TEST_VERIFY_EXIT (result.length == (size_t) -1);
  138. CHECK_INIT_STATE (int, &dyn);
  139. free_fill_heap (heap_filler);
  140. }
  141. /* Exercise failure in resize. */
  142. {
  143. struct dynarray_int dyn;
  144. dynarray_int_init (&dyn);
  145. struct heap_filler *heap_filler = fill_heap ();
  146. TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
  147. TEST_VERIFY (dynarray_int_has_failed (&dyn));
  148. free_fill_heap (heap_filler);
  149. dynarray_int_init (&dyn);
  150. TEST_VERIFY (dynarray_int_resize (&dyn, 1));
  151. heap_filler = fill_heap ();
  152. TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
  153. TEST_VERIFY (dynarray_int_has_failed (&dyn));
  154. free_fill_heap (heap_filler);
  155. dynarray_int_init (&dyn);
  156. TEST_VERIFY (dynarray_int_resize (&dyn, 1000));
  157. heap_filler = fill_heap ();
  158. TEST_VERIFY (!dynarray_int_resize (&dyn, 2000));
  159. TEST_VERIFY (dynarray_int_has_failed (&dyn));
  160. free_fill_heap (heap_filler);
  161. }
  162. }
  163. /* Check allocation failures for char * arrays (which automatically
  164. free the pointed-to strings). */
  165. static void
  166. test_str_fail (void)
  167. {
  168. /* Exercise failure in add/emplace.
  169. do_add: Use emplace (false) or add (true) to add elements.
  170. do_finalize: Perform finalization at the end (instead of free). */
  171. for (int do_add = 0; do_add < 2; ++do_add)
  172. for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
  173. {
  174. struct dynarray_str dyn;
  175. dynarray_str_init (&dyn);
  176. size_t count = 0;
  177. while (true)
  178. {
  179. char **place;
  180. if (do_add)
  181. {
  182. dynarray_str_add (&dyn, NULL);
  183. if (dynarray_str_has_failed (&dyn))
  184. break;
  185. else
  186. place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1);
  187. }
  188. else
  189. {
  190. place = dynarray_str_emplace (&dyn);
  191. if (place == NULL)
  192. break;
  193. }
  194. TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
  195. TEST_VERIFY_EXIT (*place == NULL);
  196. *place = strdup ("placeholder");
  197. if (*place == NULL)
  198. {
  199. /* Second loop to wait for failure of
  200. dynarray_str_emplace. */
  201. while (true)
  202. {
  203. if (do_add)
  204. {
  205. dynarray_str_add (&dyn, NULL);
  206. if (dynarray_str_has_failed (&dyn))
  207. break;
  208. }
  209. else
  210. {
  211. char **place = dynarray_str_emplace (&dyn);
  212. if (place == NULL)
  213. break;
  214. TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
  215. *place = NULL;
  216. }
  217. ++count;
  218. }
  219. break;
  220. }
  221. ++count;
  222. }
  223. printf ("info: %s: failure after %zu elements\n", __func__, count);
  224. TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn));
  225. if (do_finalize)
  226. {
  227. struct str_array result = { (char **) (uintptr_t) -1, -1 };
  228. TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
  229. TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
  230. TEST_VERIFY_EXIT (result.length == (size_t) -1);
  231. }
  232. else
  233. dynarray_str_free (&dyn);
  234. TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
  235. TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
  236. TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
  237. TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
  238. }
  239. /* Exercise failure in finalize. */
  240. for (int do_add = 0; do_add < 2; ++do_add)
  241. {
  242. struct dynarray_str dyn;
  243. dynarray_str_init (&dyn);
  244. for (unsigned int i = 0; i < 1000; ++i)
  245. {
  246. if (do_add)
  247. dynarray_str_add (&dyn, xstrdup ("placeholder"));
  248. else
  249. {
  250. char **place = dynarray_str_emplace (&dyn);
  251. TEST_VERIFY_EXIT (place != NULL);
  252. TEST_VERIFY_EXIT (*place == NULL);
  253. *place = xstrdup ("placeholder");
  254. }
  255. }
  256. TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
  257. struct heap_filler *heap_filler = fill_heap ();
  258. struct str_array result = { (char **) (uintptr_t) -1, -1 };
  259. TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
  260. TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
  261. TEST_VERIFY_EXIT (result.length == (size_t) -1);
  262. TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
  263. TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
  264. TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
  265. TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
  266. free_fill_heap (heap_filler);
  267. }
  268. /* Exercise failure in resize. */
  269. {
  270. struct dynarray_str dyn;
  271. dynarray_str_init (&dyn);
  272. struct heap_filler *heap_filler = fill_heap ();
  273. TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
  274. TEST_VERIFY (dynarray_str_has_failed (&dyn));
  275. free_fill_heap (heap_filler);
  276. dynarray_str_init (&dyn);
  277. TEST_VERIFY (dynarray_str_resize (&dyn, 1));
  278. *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
  279. heap_filler = fill_heap ();
  280. TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
  281. TEST_VERIFY (dynarray_str_has_failed (&dyn));
  282. free_fill_heap (heap_filler);
  283. dynarray_str_init (&dyn);
  284. TEST_VERIFY (dynarray_str_resize (&dyn, 1000));
  285. *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
  286. heap_filler = fill_heap ();
  287. TEST_VERIFY (!dynarray_str_resize (&dyn, 2000));
  288. TEST_VERIFY (dynarray_str_has_failed (&dyn));
  289. free_fill_heap (heap_filler);
  290. }
  291. }
  292. /* Test if mmap can allocate a page. This is necessary because
  293. setrlimit does not fail even if it reduces the RLIMIT_AS limit
  294. below what is currently needed by the process. */
  295. static bool
  296. mmap_works (void)
  297. {
  298. void *ptr = mmap (NULL, 1, PROT_READ | PROT_WRITE,
  299. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  300. if (ptr == MAP_FAILED)
  301. return false;
  302. xmunmap (ptr, 1);
  303. return true;
  304. }
  305. /* Set the RLIMIT_AS limit to the value in *LIMIT. */
  306. static void
  307. xsetrlimit_as (const struct rlimit *limit)
  308. {
  309. if (setrlimit (RLIMIT_AS, limit) != 0)
  310. FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m",
  311. (unsigned long) limit->rlim_cur);
  312. }
  313. /* Approximately this many bytes can be allocated after
  314. reduce_rlimit_as has run. */
  315. enum { as_limit_reserve = 2 * 1024 * 1024 };
  316. /* Limit the size of the process, so that memory allocation in
  317. allocate_thread will eventually fail, without impacting the entire
  318. system. By default, a dynamic limit which leaves room for 2 MiB is
  319. activated. The TEST_RLIMIT_AS environment variable overrides
  320. it. */
  321. static void
  322. reduce_rlimit_as (void)
  323. {
  324. struct rlimit limit;
  325. if (getrlimit (RLIMIT_AS, &limit) != 0)
  326. FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
  327. /* Use the TEST_RLIMIT_AS setting if available. */
  328. {
  329. long target = 0;
  330. const char *variable = "TEST_RLIMIT_AS";
  331. const char *target_str = getenv (variable);
  332. if (target_str != NULL)
  333. {
  334. target = atoi (target_str);
  335. if (target <= 0)
  336. FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str);
  337. printf ("info: setting RLIMIT_AS to %ld MiB\n", target);
  338. target *= 1024 * 1024; /* Convert to megabytes. */
  339. limit.rlim_cur = target;
  340. xsetrlimit_as (&limit);
  341. return;
  342. }
  343. }
  344. /* Otherwise, try to find the limit with a binary search. */
  345. unsigned long low = 1 << 20;
  346. limit.rlim_cur = low;
  347. xsetrlimit_as (&limit);
  348. /* Find working upper limit. */
  349. unsigned long high = 1 << 30;
  350. while (true)
  351. {
  352. limit.rlim_cur = high;
  353. xsetrlimit_as (&limit);
  354. if (mmap_works ())
  355. break;
  356. if (2 * high < high)
  357. FAIL_EXIT1 ("cannot find upper AS limit");
  358. high *= 2;
  359. }
  360. /* Perform binary search. */
  361. while ((high - low) > 128 * 1024)
  362. {
  363. unsigned long middle = (low + high) / 2;
  364. limit.rlim_cur = middle;
  365. xsetrlimit_as (&limit);
  366. if (mmap_works ())
  367. high = middle;
  368. else
  369. low = middle;
  370. }
  371. unsigned long target = high + as_limit_reserve;
  372. limit.rlim_cur = target;
  373. xsetrlimit_as (&limit);
  374. printf ("info: RLIMIT_AS limit: %lu bytes\n", target);
  375. }
  376. static int
  377. do_test (void)
  378. {
  379. mtrace ();
  380. reduce_rlimit_as ();
  381. test_int_fail ();
  382. test_str_fail ();
  383. return 0;
  384. }
  385. #define TIMEOUT 90
  386. #include <support/test-driver.c>