From 3f3ec4b99f79d32a0bf15495559ca9883bd751f2 Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Wed, 24 Jul 2024 23:41:32 +0300 Subject: [PATCH] exit(3): make it thread-safe It was explained by Rich Felker on libc-coord. See https://austingroupbugs.net/view.php?id=1845. Reviewed by: imp, markj Tested by: antoine (exp-run) Sponsored by: The FreeBSD Foundation MFC after: 1 month Differential revision: https://reviews.freebsd.org/D46108 --- lib/libc/stdlib/exit.3 | 19 ++++++++++++++++++- lib/libc/stdlib/exit.c | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/libc/stdlib/exit.3 b/lib/libc/stdlib/exit.3 index 1ff590bb3ae..b117e77c9e3 100644 --- a/lib/libc/stdlib/exit.3 +++ b/lib/libc/stdlib/exit.3 @@ -29,7 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd August 5, 2021 +.Dd July 24, 2024 .Dt EXIT 3 .Os .Sh NAME @@ -102,6 +102,23 @@ values described in .Xr sysexits 3 may be used to provide more information to the parent process. .Pp +Calls to the +.Fn exit +function are serialized. +All functions registered by +.Xr atexit 3 +are executed in the first thread that called +.Nm exit . +If any other thread of the process calls +.Nm exit +before all registered functions have completed or before the process +terminates, the thread is blocked until the process terminates. +The exit status of the process is the +.Fa status +argument of the first +.Nm exit +call which thread proceeds the atexit handlers. +.Pp Note that .Fn exit does nothing to prevent bottomless recursion should a function registered diff --git a/lib/libc/stdlib/exit.c b/lib/libc/stdlib/exit.c index bca978428ce..16631fad5b9 100644 --- a/lib/libc/stdlib/exit.c +++ b/lib/libc/stdlib/exit.c @@ -31,6 +31,7 @@ #include "namespace.h" #include +#include #include #include "un-namespace.h" @@ -48,6 +49,20 @@ void (*__cleanup)(void); */ int __isthreaded = 0; +static pthread_mutex_t exit_mutex; +static pthread_once_t exit_mutex_once = PTHREAD_ONCE_INIT; + +static void +exit_mutex_init_once(void) +{ + pthread_mutexattr_t ma; + + _pthread_mutexattr_init(&ma); + _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); + _pthread_mutex_init(&exit_mutex, &ma); + _pthread_mutexattr_destroy(&ma); +} + /* * Exit, flushing stdio buffers if necessary. */ @@ -59,6 +74,12 @@ exit(int status) _thread_autoinit_dummy_decl = 1; + /* Make exit(3) thread-safe */ + if (__isthreaded) { + _once(&exit_mutex_once, exit_mutex_init_once); + _pthread_mutex_lock(&exit_mutex); + } + /* * We're dealing with cleaning up thread_local destructors in the case of * the process termination through main() exit.