sparc: Use existing macros to avoid code duplication
[glibc.git] / malloc / tst-dynarray-fail.c
blobf76f7a9ff2c33c92edee60b6c45450ad7dc40999
1 /* Test allocation failures with dynamic arrays.
2 Copyright (C) 2017-2024 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 /* This test is separate from tst-dynarray because it cannot run under
20 valgrind. */
22 #include "tst-dynarray-shared.h"
24 #include <mcheck.h>
25 #include <stdio.h>
26 #include <support/check.h>
27 #include <support/support.h>
28 #include <support/xunistd.h>
29 #include <sys/mman.h>
30 #include <sys/resource.h>
31 #include <unistd.h>
33 /* Data structure to fill up the heap. */
34 struct heap_filler
36 struct heap_filler *next;
39 /* Allocate objects until the heap is full. */
40 static struct heap_filler *
41 fill_heap (void)
43 size_t pad = 4096;
44 struct heap_filler *head = NULL;
45 while (true)
47 struct heap_filler *new_head = malloc (sizeof (*new_head) + pad);
48 if (new_head == NULL)
50 if (pad > 0)
52 /* Try again with smaller allocations. */
53 pad = 0;
54 continue;
56 else
57 break;
59 new_head->next = head;
60 head = new_head;
62 return head;
65 /* Free the heap-filling allocations, so that we can continue testing
66 and detect memory leaks elsewhere. */
67 static void
68 free_fill_heap (struct heap_filler *head)
70 while (head != NULL)
72 struct heap_filler *next = head->next;
73 free (head);
74 head = next;
78 /* Check allocation failures for int arrays (without an element free
79 function). */
80 static void
81 test_int_fail (void)
83 /* Exercise failure in add/emplace.
85 do_add: Use emplace (false) or add (true) to add elements.
86 do_finalize: Perform finalization at the end (instead of free). */
87 for (int do_add = 0; do_add < 2; ++do_add)
88 for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
90 struct dynarray_int dyn;
91 dynarray_int_init (&dyn);
92 size_t count = 0;
93 while (true)
95 if (do_add)
97 dynarray_int_add (&dyn, 0);
98 if (dynarray_int_has_failed (&dyn))
99 break;
101 else
103 int *place = dynarray_int_emplace (&dyn);
104 if (place == NULL)
105 break;
106 TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
107 *place = 0;
109 ++count;
111 printf ("info: %s: failure after %zu elements\n", __func__, count);
112 TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn));
113 if (do_finalize)
115 struct int_array result = { (int *) (uintptr_t) -1, -1 };
116 TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
117 TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
118 TEST_VERIFY_EXIT (result.length == (size_t) -1);
120 else
121 dynarray_int_free (&dyn);
122 CHECK_INIT_STATE (int, &dyn);
125 /* Exercise failure in finalize. */
126 for (int do_add = 0; do_add < 2; ++do_add)
128 struct dynarray_int dyn;
129 dynarray_int_init (&dyn);
130 for (unsigned int i = 0; i < 10000; ++i)
132 if (do_add)
134 dynarray_int_add (&dyn, i);
135 TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
137 else
139 int *place = dynarray_int_emplace (&dyn);
140 TEST_VERIFY_EXIT (place != NULL);
141 *place = i;
144 TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
145 struct heap_filler *heap_filler = fill_heap ();
146 struct int_array result = { (int *) (uintptr_t) -1, -1 };
147 TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
148 TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
149 TEST_VERIFY_EXIT (result.length == (size_t) -1);
150 CHECK_INIT_STATE (int, &dyn);
151 free_fill_heap (heap_filler);
154 /* Exercise failure in resize. */
156 struct dynarray_int dyn;
157 dynarray_int_init (&dyn);
158 struct heap_filler *heap_filler = fill_heap ();
159 TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
160 TEST_VERIFY (dynarray_int_has_failed (&dyn));
161 free_fill_heap (heap_filler);
163 dynarray_int_init (&dyn);
164 TEST_VERIFY (dynarray_int_resize (&dyn, 1));
165 heap_filler = fill_heap ();
166 TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
167 TEST_VERIFY (dynarray_int_has_failed (&dyn));
168 free_fill_heap (heap_filler);
170 dynarray_int_init (&dyn);
171 TEST_VERIFY (dynarray_int_resize (&dyn, 1000));
172 heap_filler = fill_heap ();
173 TEST_VERIFY (!dynarray_int_resize (&dyn, 2000));
174 TEST_VERIFY (dynarray_int_has_failed (&dyn));
175 free_fill_heap (heap_filler);
179 /* Check allocation failures for char * arrays (which automatically
180 free the pointed-to strings). */
181 static void
182 test_str_fail (void)
184 /* Exercise failure in add/emplace.
186 do_add: Use emplace (false) or add (true) to add elements.
187 do_finalize: Perform finalization at the end (instead of free). */
188 for (int do_add = 0; do_add < 2; ++do_add)
189 for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
191 struct dynarray_str dyn;
192 dynarray_str_init (&dyn);
193 size_t count = 0;
194 while (true)
196 char **place;
197 if (do_add)
199 dynarray_str_add (&dyn, NULL);
200 if (dynarray_str_has_failed (&dyn))
201 break;
202 else
203 place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1);
205 else
207 place = dynarray_str_emplace (&dyn);
208 if (place == NULL)
209 break;
211 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
212 TEST_VERIFY_EXIT (*place == NULL);
213 *place = strdup ("placeholder");
214 if (*place == NULL)
216 /* Second loop to wait for failure of
217 dynarray_str_emplace. */
218 while (true)
220 if (do_add)
222 dynarray_str_add (&dyn, NULL);
223 if (dynarray_str_has_failed (&dyn))
224 break;
226 else
228 char **place = dynarray_str_emplace (&dyn);
229 if (place == NULL)
230 break;
231 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
232 *place = NULL;
234 ++count;
236 break;
238 ++count;
240 printf ("info: %s: failure after %zu elements\n", __func__, count);
241 TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn));
242 if (do_finalize)
244 struct str_array result = { (char **) (uintptr_t) -1, -1 };
245 TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
246 TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
247 TEST_VERIFY_EXIT (result.length == (size_t) -1);
249 else
250 dynarray_str_free (&dyn);
251 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
252 TEST_VERIFY_EXIT (dyn.u.dynarray_header.array == dyn.scratch);
253 TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
254 TEST_VERIFY_EXIT (dyn.u.dynarray_header.allocated > 0);
257 /* Exercise failure in finalize. */
258 for (int do_add = 0; do_add < 2; ++do_add)
260 struct dynarray_str dyn;
261 dynarray_str_init (&dyn);
262 for (unsigned int i = 0; i < 1000; ++i)
264 if (do_add)
265 dynarray_str_add (&dyn, xstrdup ("placeholder"));
266 else
268 char **place = dynarray_str_emplace (&dyn);
269 TEST_VERIFY_EXIT (place != NULL);
270 TEST_VERIFY_EXIT (*place == NULL);
271 *place = xstrdup ("placeholder");
274 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
275 struct heap_filler *heap_filler = fill_heap ();
276 struct str_array result = { (char **) (uintptr_t) -1, -1 };
277 TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
278 TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
279 TEST_VERIFY_EXIT (result.length == (size_t) -1);
280 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
281 TEST_VERIFY_EXIT (dyn.u.dynarray_header.array == dyn.scratch);
282 TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
283 TEST_VERIFY_EXIT (dyn.u.dynarray_header.allocated > 0);
284 free_fill_heap (heap_filler);
287 /* Exercise failure in resize. */
289 struct dynarray_str dyn;
290 dynarray_str_init (&dyn);
291 struct heap_filler *heap_filler = fill_heap ();
292 TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
293 TEST_VERIFY (dynarray_str_has_failed (&dyn));
294 free_fill_heap (heap_filler);
296 dynarray_str_init (&dyn);
297 TEST_VERIFY (dynarray_str_resize (&dyn, 1));
298 *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
299 heap_filler = fill_heap ();
300 TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
301 TEST_VERIFY (dynarray_str_has_failed (&dyn));
302 free_fill_heap (heap_filler);
304 dynarray_str_init (&dyn);
305 TEST_VERIFY (dynarray_str_resize (&dyn, 1000));
306 *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
307 heap_filler = fill_heap ();
308 TEST_VERIFY (!dynarray_str_resize (&dyn, 2000));
309 TEST_VERIFY (dynarray_str_has_failed (&dyn));
310 free_fill_heap (heap_filler);
314 /* Test if mmap can allocate a page. This is necessary because
315 setrlimit does not fail even if it reduces the RLIMIT_AS limit
316 below what is currently needed by the process. */
317 static bool
318 mmap_works (void)
320 void *ptr = mmap (NULL, 1, PROT_READ | PROT_WRITE,
321 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
322 if (ptr == MAP_FAILED)
323 return false;
324 xmunmap (ptr, 1);
325 return true;
328 /* Set the RLIMIT_AS limit to the value in *LIMIT. */
329 static void
330 xsetrlimit_as (const struct rlimit *limit)
332 if (setrlimit (RLIMIT_AS, limit) != 0)
333 FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m",
334 (unsigned long) limit->rlim_cur);
337 /* Approximately this many bytes can be allocated after
338 reduce_rlimit_as has run. */
339 enum { as_limit_reserve = 2 * 1024 * 1024 };
341 /* Limit the size of the process, so that memory allocation in
342 allocate_thread will eventually fail, without impacting the entire
343 system. By default, a dynamic limit which leaves room for 2 MiB is
344 activated. The TEST_RLIMIT_AS environment variable overrides
345 it. */
346 static void
347 reduce_rlimit_as (void)
349 struct rlimit limit;
350 if (getrlimit (RLIMIT_AS, &limit) != 0)
351 FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
353 /* Use the TEST_RLIMIT_AS setting if available. */
355 long target = 0;
356 const char *variable = "TEST_RLIMIT_AS";
357 const char *target_str = getenv (variable);
358 if (target_str != NULL)
360 target = atoi (target_str);
361 if (target <= 0)
362 FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str);
363 printf ("info: setting RLIMIT_AS to %ld MiB\n", target);
364 target *= 1024 * 1024; /* Convert to megabytes. */
365 limit.rlim_cur = target;
366 xsetrlimit_as (&limit);
367 return;
371 /* Otherwise, try to find the limit with a binary search. */
372 unsigned long low = 1 << 20;
373 limit.rlim_cur = low;
374 xsetrlimit_as (&limit);
376 /* Find working upper limit. */
377 unsigned long high = 1 << 30;
378 while (true)
380 limit.rlim_cur = high;
381 xsetrlimit_as (&limit);
382 if (mmap_works ())
383 break;
384 if (2 * high < high)
385 FAIL_EXIT1 ("cannot find upper AS limit");
386 high *= 2;
389 /* Perform binary search. */
390 while ((high - low) > 128 * 1024)
392 unsigned long middle = (low + high) / 2;
393 limit.rlim_cur = middle;
394 xsetrlimit_as (&limit);
395 if (mmap_works ())
396 high = middle;
397 else
398 low = middle;
401 unsigned long target = high + as_limit_reserve;
402 limit.rlim_cur = target;
403 xsetrlimit_as (&limit);
404 printf ("info: RLIMIT_AS limit: %lu bytes\n", target);
407 static int
408 do_test (void)
410 mtrace ();
411 reduce_rlimit_as ();
412 test_int_fail ();
413 test_str_fail ();
414 return 0;
417 #define TIMEOUT 90
418 #include <support/test-driver.c>