summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-04-26 15:47:44 -0400
committerRich Felker <dalias@aerifal.cx>2013-04-26 15:47:44 -0400
commit23f21c304fd6a7592b70927e247129c5a2bc2390 (patch)
tree0fab3bbf2287cde6d13c38c594a5901bc85cd330
parenta0473a0c826016aec1181819fcd4fff5c074f042 (diff)
downloadmusl-23f21c304fd6a7592b70927e247129c5a2bc2390.tar.gz
always block signals in pthread_exit before decrementing thread count
the thread count (1+libc.threads_minus_1) must always be greater than or equal to the number of threads which could have application code running, even in an async-signal-safe sense. there is at least one dangerous race condition if this invariant fails to hold: dlopen could allocate too little TLS for existing threads, and a signal handler running in the exiting thread could claim the allocated TLS for itself (via __tls_get_addr), leaving too little for the other threads it was allocated for and thereby causing out-of-bounds access. there may be other situations where it's dangerous for the thread count to be too low, particularly in the case where only one thread should be left, in which case locking may be omitted. however, all such code paths seem to arise from undefined behavior, since async-signal-unsafe functions are not permitted to be called from a signal handler that interrupts pthread_exit (which is itself async-signal-unsafe). this change may also simplify logic in __synccall and improve the chances of making __synccall async-signal-safe.
-rw-r--r--src/thread/pthread_create.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index e6760abb..0cd2d6c2 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -32,6 +32,8 @@ _Noreturn void pthread_exit(void *result)
self->dead = 1;
__unlock(self->killlock);
+ __syscall(SYS_rt_sigprocmask, SIG_BLOCK, SIGALL_SET, 0, _NSIG/8);
+
do n = libc.threads_minus_1;
while (n && a_cas(&libc.threads_minus_1, n, n-1)!=n);
if (!n) exit(0);
@@ -39,8 +41,6 @@ _Noreturn void pthread_exit(void *result)
if (self->detached && self->map_base) {
if (self->detached == 2)
__syscall(SYS_set_tid_address, 0);
- __syscall(SYS_rt_sigprocmask, SIG_BLOCK,
- SIGALL_SET, 0, _NSIG/8);
__unmapself(self->map_base, self->map_size);
}