1 /* Test allocation failures with dynamic arrays.
2 Copyright (C) 2017-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 /* This test is separate from tst-dynarray because it cannot run under
22 #include "tst-dynarray-shared.h"
26 #include <support/check.h>
27 #include <support/support.h>
28 #include <support/xunistd.h>
30 #include <sys/resource.h>
33 /* Data structure to fill up the heap. */
36 struct heap_filler
*next
;
39 /* Allocate objects until the heap is full. */
40 static struct heap_filler
*
44 struct heap_filler
*head
= NULL
;
47 struct heap_filler
*new_head
= malloc (sizeof (*new_head
) + pad
);
52 /* Try again with smaller allocations. */
59 new_head
->next
= head
;
65 /* Free the heap-filling allocations, so that we can continue testing
66 and detect memory leaks elsewhere. */
68 free_fill_heap (struct heap_filler
*head
)
72 struct heap_filler
*next
= head
->next
;
78 /* Check allocation failures for int arrays (without an element free
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
);
97 dynarray_int_add (&dyn
, 0);
98 if (dynarray_int_has_failed (&dyn
))
103 int *place
= dynarray_int_emplace (&dyn
);
106 TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn
));
111 printf ("info: %s: failure after %zu elements\n", __func__
, count
);
112 TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn
));
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);
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
)
134 dynarray_int_add (&dyn
, i
);
135 TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn
));
139 int *place
= dynarray_int_emplace (&dyn
);
140 TEST_VERIFY_EXIT (place
!= NULL
);
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). */
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
);
199 dynarray_str_add (&dyn
, NULL
);
200 if (dynarray_str_has_failed (&dyn
))
203 place
= dynarray_str_at (&dyn
, dynarray_str_size (&dyn
) - 1);
207 place
= dynarray_str_emplace (&dyn
);
211 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn
));
212 TEST_VERIFY_EXIT (*place
== NULL
);
213 *place
= strdup ("placeholder");
216 /* Second loop to wait for failure of
217 dynarray_str_emplace. */
222 dynarray_str_add (&dyn
, NULL
);
223 if (dynarray_str_has_failed (&dyn
))
228 char **place
= dynarray_str_emplace (&dyn
);
231 TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn
));
240 printf ("info: %s: failure after %zu elements\n", __func__
, count
);
241 TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn
));
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);
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
)
265 dynarray_str_add (&dyn
, xstrdup ("placeholder"));
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. */
320 void *ptr
= mmap (NULL
, 1, PROT_READ
| PROT_WRITE
,
321 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
322 if (ptr
== MAP_FAILED
)
328 /* Set the RLIMIT_AS limit to the value in *LIMIT. */
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
347 reduce_rlimit_as (void)
350 if (getrlimit (RLIMIT_AS
, &limit
) != 0)
351 FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
353 /* Use the TEST_RLIMIT_AS setting if available. */
356 const char *variable
= "TEST_RLIMIT_AS";
357 const char *target_str
= getenv (variable
);
358 if (target_str
!= NULL
)
360 target
= atoi (target_str
);
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
);
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;
380 limit
.rlim_cur
= high
;
381 xsetrlimit_as (&limit
);
385 FAIL_EXIT1 ("cannot find upper AS limit");
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
);
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
);
418 #include <support/test-driver.c>