hurd: fix build of tst-system.c
[glibc.git] / malloc / tst-malloc-thread-fail.c
blob271941bb8eb1f66e15cf1fad3220ecd32a031c87
1 /* Test allocation function behavior on allocation failure.
2 Copyright (C) 2015-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 License as
7 published by the Free Software Foundation; either version 2.1 of the
8 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; see the file COPYING.LIB. If
17 not, see <https://www.gnu.org/licenses/>. */
19 /* This test case attempts to trigger various unusual conditions
20 related to allocation failures, notably switching to a different
21 arena, and falling back to mmap (via sysmalloc). */
23 #include <errno.h>
24 #include <malloc.h>
25 #include <pthread.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/resource.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
34 /* Wrapper for calloc with an optimization barrier. */
35 static void *
36 __attribute__ ((noinline, noclone))
37 allocate_zeroed (size_t a, size_t b)
39 return calloc (a, b);
42 /* System page size, as determined by sysconf (_SC_PAGE_SIZE). */
43 static unsigned long page_size;
45 /* Test parameters. */
46 static size_t allocation_size;
47 static size_t alignment;
48 static enum {
49 with_malloc,
50 with_realloc,
51 with_aligned_alloc,
52 with_memalign,
53 with_posix_memalign,
54 with_valloc,
55 with_pvalloc,
56 with_calloc,
57 last_allocation_function = with_calloc
58 } allocation_function;
60 /* True if an allocation function uses the alignment test
61 parameter. */
62 const static bool alignment_sensitive[last_allocation_function + 1] =
64 [with_aligned_alloc] = true,
65 [with_memalign] = true,
66 [with_posix_memalign] = true,
69 /* Combined pointer/expected alignment result of an allocation
70 function. */
71 struct allocate_result {
72 void *pointer;
73 size_t alignment;
76 /* Call the allocation function specified by allocation_function, with
77 allocation_size and alignment (if applicable) as arguments. No
78 alignment check. */
79 static struct allocate_result
80 allocate_1 (void)
82 switch (allocation_function)
84 case with_malloc:
85 return (struct allocate_result)
86 {malloc (allocation_size), _Alignof (max_align_t)};
87 case with_realloc:
89 void *p = realloc (NULL, 16);
90 void *q;
91 if (p == NULL)
92 q = NULL;
93 else
95 q = realloc (p, allocation_size);
96 if (q == NULL)
97 free (p);
99 return (struct allocate_result) {q, _Alignof (max_align_t)};
101 case with_aligned_alloc:
103 void *p = aligned_alloc (alignment, allocation_size);
104 return (struct allocate_result) {p, alignment};
106 case with_memalign:
108 void *p = memalign (alignment, allocation_size);
109 return (struct allocate_result) {p, alignment};
111 case with_posix_memalign:
113 void *p;
114 if (posix_memalign (&p, alignment, allocation_size))
116 if (errno == ENOMEM)
117 p = NULL;
118 else
120 printf ("error: posix_memalign (p, %zu, %zu): %m\n",
121 alignment, allocation_size);
122 abort ();
125 return (struct allocate_result) {p, alignment};
127 case with_valloc:
129 void *p = valloc (allocation_size);
130 return (struct allocate_result) {p, page_size};
132 case with_pvalloc:
134 void *p = pvalloc (allocation_size);
135 return (struct allocate_result) {p, page_size};
137 case with_calloc:
139 char *p = allocate_zeroed (1, allocation_size);
140 /* Check for non-zero bytes. */
141 if (p != NULL)
142 for (size_t i = 0; i < allocation_size; ++i)
143 if (p[i] != 0)
145 printf ("error: non-zero byte at offset %zu\n", i);
146 abort ();
148 return (struct allocate_result) {p, _Alignof (max_align_t)};
151 abort ();
154 /* Call allocate_1 and perform the alignment check on the result. */
155 static void *
156 allocate (void)
158 struct allocate_result r = allocate_1 ();
159 if ((((uintptr_t) r.pointer) & (r.alignment - 1)) != 0)
161 printf ("error: allocation function %d, size %zu not aligned to %zu\n",
162 (int) allocation_function, allocation_size, r.alignment);
163 abort ();
165 return r.pointer;
168 /* Barriers to synchronize thread creation and termination. */
169 static pthread_barrier_t start_barrier;
170 static pthread_barrier_t end_barrier;
172 /* Thread function which performs the allocation test. Called by
173 pthread_create and from the main thread. */
174 static void *
175 allocate_thread (void *closure)
177 /* Wait for the creation of all threads. */
179 int ret = pthread_barrier_wait (&start_barrier);
180 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
182 errno = ret;
183 printf ("error: pthread_barrier_wait: %m\n");
184 abort ();
188 /* Allocate until we run out of memory, creating a single-linked
189 list. */
190 struct list {
191 struct list *next;
193 struct list *head = NULL;
194 while (true)
196 struct list *e = allocate ();
197 if (e == NULL)
198 break;
200 e->next = head;
201 head = e;
204 /* Wait for the allocation of all available memory. */
206 int ret = pthread_barrier_wait (&end_barrier);
207 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
209 errno = ret;
210 printf ("error: pthread_barrier_wait: %m\n");
211 abort ();
215 /* Free the allocated memory. */
216 while (head != NULL)
218 struct list *next = head->next;
219 free (head);
220 head = next;
223 return NULL;
226 /* Number of threads (plus the main thread. */
227 enum { thread_count = 8 };
229 /* Thread attribute to request creation of threads with a non-default
230 stack size which is rather small. This avoids interfering with the
231 configured address space limit. */
232 static pthread_attr_t small_stack;
234 /* Runs one test in multiple threads, all in a subprocess so that
235 subsequent tests do not interfere with each other. */
236 static void
237 run_one (void)
239 /* Isolate the tests in a subprocess, so that we can start over
240 from scratch. */
241 pid_t pid = fork ();
242 if (pid == 0)
244 /* In the child process. Create the allocation threads. */
245 pthread_t threads[thread_count];
247 for (unsigned i = 0; i < thread_count; ++i)
249 int ret = pthread_create (threads + i, &small_stack, allocate_thread, NULL);
250 if (ret != 0)
252 errno = ret;
253 printf ("error: pthread_create: %m\n");
254 abort ();
258 /* Also run the test on the main thread. */
259 allocate_thread (NULL);
261 for (unsigned i = 0; i < thread_count; ++i)
263 int ret = pthread_join (threads[i], NULL);
264 if (ret != 0)
266 errno = ret;
267 printf ("error: pthread_join: %m\n");
268 abort ();
271 _exit (0);
273 else if (pid < 0)
275 printf ("error: fork: %m\n");
276 abort ();
279 /* In the parent process. Wait for the child process to exit. */
280 int status;
281 if (waitpid (pid, &status, 0) < 0)
283 printf ("error: waitpid: %m\n");
284 abort ();
286 if (status != 0)
288 printf ("error: exit status %d from child process\n", status);
289 exit (1);
293 /* Run all applicable allocation functions for the current test
294 parameters. */
295 static void
296 run_allocation_functions (void)
298 for (int af = 0; af <= last_allocation_function; ++af)
300 /* Run alignment-sensitive functions for non-default
301 alignments. */
302 if (alignment_sensitive[af] != (alignment != 0))
303 continue;
304 allocation_function = af;
305 run_one ();
310 do_test (void)
312 /* Limit the number of malloc arenas. We use a very low number so
313 that despute the address space limit configured below, all
314 requested arenas a can be created. */
315 if (mallopt (M_ARENA_MAX, 2) == 0)
317 printf ("error: mallopt (M_ARENA_MAX) failed\n");
318 return 1;
321 /* Determine the page size. */
323 long ret = sysconf (_SC_PAGE_SIZE);
324 if (ret < 0)
326 printf ("error: sysconf (_SC_PAGE_SIZE): %m\n");
327 return 1;
329 page_size = ret;
332 /* Limit the size of the process, so that memory allocation in
333 allocate_thread will eventually fail, without impacting the
334 entire system. */
336 struct rlimit limit;
337 if (getrlimit (RLIMIT_AS, &limit) != 0)
339 printf ("getrlimit (RLIMIT_AS) failed: %m\n");
340 return 1;
342 long target = 200 * 1024 * 1024;
343 if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
345 limit.rlim_cur = target;
346 if (setrlimit (RLIMIT_AS, &limit) != 0)
348 printf ("setrlimit (RLIMIT_AS) failed: %m\n");
349 return 1;
354 /* Initialize thread attribute with a reduced stack size. */
356 int ret = pthread_attr_init (&small_stack);
357 if (ret != 0)
359 errno = ret;
360 printf ("error: pthread_attr_init: %m\n");
361 abort ();
363 unsigned long stack_size = ((256 * 1024) / page_size) * page_size;
364 if (stack_size < 4 * page_size)
365 stack_size = 8 * page_size;
366 ret = pthread_attr_setstacksize (&small_stack, stack_size);
367 if (ret != 0)
369 errno = ret;
370 printf ("error: pthread_attr_setstacksize: %m\n");
371 abort ();
375 /* Initialize the barriers. We run thread_count threads, plus 1 for
376 the main thread. */
378 int ret = pthread_barrier_init (&start_barrier, NULL, thread_count + 1);
379 if (ret != 0)
381 errno = ret;
382 printf ("error: pthread_barrier_init: %m\n");
383 abort ();
386 ret = pthread_barrier_init (&end_barrier, NULL, thread_count + 1);
387 if (ret != 0)
389 errno = ret;
390 printf ("error: pthread_barrier_init: %m\n");
391 abort ();
395 allocation_size = 144;
396 run_allocation_functions ();
397 allocation_size = page_size;
398 run_allocation_functions ();
400 alignment = 128;
401 allocation_size = 512;
402 run_allocation_functions ();
404 allocation_size = page_size;
405 run_allocation_functions ();
407 allocation_size = 17 * page_size;
408 run_allocation_functions ();
410 /* Deallocation the barriers and the thread attribute. */
412 int ret = pthread_barrier_destroy (&end_barrier);
413 if (ret != 0)
415 errno = ret;
416 printf ("error: pthread_barrier_destroy: %m\n");
417 return 1;
419 ret = pthread_barrier_destroy (&start_barrier);
420 if (ret != 0)
422 errno = ret;
423 printf ("error: pthread_barrier_destroy: %m\n");
424 return 1;
426 ret = pthread_attr_destroy (&small_stack);
427 if (ret != 0)
429 errno = ret;
430 printf ("error: pthread_attr_destroy: %m\n");
431 return 1;
435 return 0;
438 /* The repeated allocations take some time on slow machines. */
439 #define TIMEOUT 100
441 #define TEST_FUNCTION do_test ()
442 #include "../test-skeleton.c"