From: Rich Felker Date: Fri, 26 Apr 2013 19:47:44 +0000 (-0400) Subject: always block signals in pthread_exit before decrementing thread count X-Git-Url: http://nsz.repo.hu/git/?p=musl;a=commitdiff_plain;h=23f21c304fd6a7592b70927e247129c5a2bc2390 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. --- 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); }