aboutsummaryrefslogtreecommitdiff
path: root/stdlib/exit.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2024-08-05 11:27:35 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2024-08-05 17:07:57 -0300
commitc6af8a9a3ce137a9704825d173be22a2b2d9cb49 (patch)
treedaa57dfeb5ae6e1d1c61b7b414400b3bb28d3546 /stdlib/exit.c
parent5097cd344fd243fb8deb6dec96e8073753f962f9 (diff)
downloadglibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.tar.xz
glibc-c6af8a9a3ce137a9704825d173be22a2b2d9cb49.zip
stdlib: Allow concurrent quick_exit (BZ 31997)
As for exit, also allows concurrent quick_exit to avoid race conditions when it is called concurrently. Since it uses the same internal function as exit, the __exit_lock lock is moved to __run_exit_handlers. It also solved a potential concurrent when calling exit and quick_exit concurrently. The test case 'expected' is expanded to a value larger than the minimum required by C/POSIX (32 entries) so at_quick_exit() will require libc to allocate a new block. This makes the test mre likely to trigger concurrent issues (through free() at __run_exit_handlers) if quick_exit() interacts with the at_quick_exit list concurrently. This is also the latest interpretation of the Austin Ticket [1]. Checked on x86_64-linux-gnu. [1] https://austingroupbugs.net/view.php?id=1845 Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'stdlib/exit.c')
-rw-r--r--stdlib/exit.c18
1 files changed, 10 insertions, 8 deletions
diff --git a/stdlib/exit.c b/stdlib/exit.c
index bbaf138806..8d7e2e53d0 100644
--- a/stdlib/exit.c
+++ b/stdlib/exit.c
@@ -28,6 +28,13 @@
__exit_funcs_lock is declared. */
bool __exit_funcs_done = false;
+/* The lock handles concurrent exit() and quick_exit(), even though the
+ C/POSIX standard states that calling exit() more than once is UB. The
+ recursive lock allows atexit() handlers or destructors to call exit()
+ itself. In this case, the handler list execution will resume at the
+ point of the current handler. */
+__libc_lock_define_initialized_recursive (static, __exit_lock)
+
/* Call all functions registered with `atexit' and `on_exit',
in the reverse of the order in which they were registered
perform stdio cleanup, and terminate program execution with STATUS. */
@@ -36,6 +43,9 @@ attribute_hidden
__run_exit_handlers (int status, struct exit_function_list **listp,
bool run_list_atexit, bool run_dtors)
{
+ /* The exit should never return, so there is no need to unlock it. */
+ __libc_lock_lock_recursive (__exit_lock);
+
/* First, call the TLS destructors. */
if (run_dtors)
call_function_static_weak (__call_tls_dtors);
@@ -132,17 +142,9 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
}
-/* The lock handles concurrent exit(), even though the C/POSIX standard states
- that calling exit() more than once is UB. The recursive lock allows
- atexit() handlers or destructors to call exit() itself. In this case, the
- handler list execution will resume at the point of the current handler. */
-__libc_lock_define_initialized_recursive (static, __exit_lock)
-
void
exit (int status)
{
- /* The exit should never return, so there is no need to unlock it. */
- __libc_lock_lock_recursive (__exit_lock);
__run_exit_handlers (status, &__exit_funcs, true, true);
}
libc_hidden_def (exit)