1 /* Test case for async-signal-safe _Fork (with respect to malloc).
2 Copyright (C) 2021-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 is similar to tst-mallocfork2.c, but specifically stress
20 the async-signal-safeness of _Fork on multithread environment. */
22 #include <array_length.h>
27 #include <support/check.h>
28 #include <support/support.h>
29 #include <support/xsignal.h>
30 #include <support/xthread.h>
31 #include <support/xunistd.h>
34 /* How many malloc objects to keep arond. */
35 enum { malloc_objects
= 1009 };
37 /* The maximum size of an object. */
38 enum { malloc_maximum_size
= 70000 };
40 /* How many iterations the test performs before exiting. */
41 enum { iterations
= 10000 };
43 /* Barrier for synchronization with the threads sending SIGUSR1
44 signals, to make it more likely that the signals arrive during a
45 fork/free/malloc call. */
46 static pthread_barrier_t barrier
;
48 /* Set to 1 if SIGUSR1 is received. Used to detect a signal during
50 static volatile sig_atomic_t sigusr1_received
;
52 /* Periodically set to 1, to indicate that the thread is making
53 progress. Checked by liveness_signal_handler. */
54 static volatile sig_atomic_t progress_indicator
= 1;
56 /* Set to 1 if an error occurs in the signal handler. */
57 static volatile sig_atomic_t error_indicator
= 0;
60 sigusr1_handler (int signo
)
64 /* Perform a fork with a trivial subprocess. */
68 write_message ("error: fork\n");
75 int ret
= TEMP_FAILURE_RETRY (waitpid (pid
, &status
, 0));
78 write_message ("error: waitpid\n");
84 write_message ("error: unexpected exit status from subprocess\n");
91 liveness_signal_handler (int signo
)
93 if (progress_indicator
)
94 progress_indicator
= 0;
96 write_message ("warning: thread seems to be stuck\n");
99 struct signal_send_args
105 #define SIGNAL_SEND_GET_ARG(arg, field) \
106 (((struct signal_send_args *)(arg))->field)
108 /* Send SIGNO to the parent thread. If SLEEP, wait a second between
109 signals, otherwise use barriers to delay sending signals. */
111 signal_sender (void *args
)
113 int signo
= SIGNAL_SEND_GET_ARG (args
, signo
);
114 bool sleep
= SIGNAL_SEND_GET_ARG (args
, sleep
);
116 pthread_t target
= SIGNAL_SEND_GET_ARG (args
, target
);
120 xpthread_barrier_wait (&barrier
);
121 xpthread_kill (target
, signo
);
123 usleep (1 * 1000 * 1000);
125 xpthread_barrier_wait (&barrier
);
130 static pthread_t sigusr1_sender
[5];
131 static pthread_t sigusr2_sender
;
136 xsignal (SIGUSR1
, sigusr1_handler
);
137 xsignal (SIGUSR2
, liveness_signal_handler
);
139 pthread_t self
= pthread_self ();
141 struct signal_send_args sigusr2_args
= { self
, SIGUSR2
, true };
142 sigusr2_sender
= xpthread_create (NULL
, signal_sender
, &sigusr2_args
);
144 /* Send SIGUSR1 signals from several threads. Hopefully, one
145 signal will hit one of the ciritical functions. Use a barrier to
146 avoid sending signals while not running fork/free/malloc. */
147 struct signal_send_args sigusr1_args
= { self
, SIGUSR1
, false };
148 xpthread_barrier_init (&barrier
, NULL
,
149 array_length (sigusr1_sender
) + 1);
150 for (size_t i
= 0; i
< array_length (sigusr1_sender
); ++i
)
151 sigusr1_sender
[i
] = xpthread_create (NULL
, signal_sender
, &sigusr1_args
);
153 void *objects
[malloc_objects
] = {};
154 unsigned int fork_signals
= 0;
155 unsigned int free_signals
= 0;
156 unsigned int malloc_signals
= 0;
157 unsigned int seed
= 1;
158 for (int i
= 0; i
< iterations
; ++i
)
160 progress_indicator
= 1;
161 int slot
= rand_r (&seed
) % malloc_objects
;
162 size_t size
= rand_r (&seed
) % malloc_maximum_size
;
164 /* Occasionally do a fork first, to catch deadlocks there as
165 well (see bug 24161). */
166 bool do_fork
= (rand_r (&seed
) % 7) == 0;
168 xpthread_barrier_wait (&barrier
);
171 sigusr1_received
= 0;
172 pid_t pid
= _Fork ();
173 TEST_VERIFY_EXIT (pid
!= -1);
174 if (sigusr1_received
)
179 int ret
= TEMP_FAILURE_RETRY (waitpid (pid
, &status
, 0));
181 FAIL_EXIT1 ("waitpid: %m");
182 TEST_COMPARE (status
, 0);
184 sigusr1_received
= 0;
185 free (objects
[slot
]);
186 if (sigusr1_received
)
188 sigusr1_received
= 0;
189 objects
[slot
] = malloc (size
);
190 if (sigusr1_received
)
192 xpthread_barrier_wait (&barrier
);
194 if (objects
[slot
] == NULL
|| error_indicator
!= 0)
196 printf ("error: malloc: %m\n");
201 /* Clean up allocations. */
202 for (int slot
= 0; slot
< malloc_objects
; ++slot
)
203 free (objects
[slot
]);
205 printf ("info: signals received during fork: %u\n", fork_signals
);
206 printf ("info: signals received during free: %u\n", free_signals
);
207 printf ("info: signals received during malloc: %u\n", malloc_signals
);
213 #include <support/test-driver.c>