From c6af8a9a3ce137a9704825d173be22a2b2d9cb49 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Mon, 5 Aug 2024 11:27:35 -0300 Subject: 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 --- stdlib/exit.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'stdlib/exit.c') 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) -- cgit v1.2.3