summaryrefslogtreecommitdiff
path: root/src/thread/pthread_create.c
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2018-05-04 14:26:31 -0400
committerRich Felker <dalias@aerifal.cx>2018-05-05 11:09:51 -0400
commit526e64f54d729947b35fd39129bc86cbc0b5f098 (patch)
treed13eff2e89069dadfff5ea537c56f422847f8def /src/thread/pthread_create.c
parent4df42163511182bfd6fc55e85250b93786dcce7e (diff)
downloadmusl-526e64f54d729947b35fd39129bc86cbc0b5f098.tar.gz
improve pthread_exit synchronization with functions targeting tid
if the last thread exited via pthread_exit, the logic that marked it dead did not account for the possibility of it targeting itself via atexit handlers. for example, an atexit handler calling pthread_kill(pthread_self(), SIGKILL) would return success (previously, ESRCH) rather than causing termination via the signal. move the release of killlock after the determination is made whether the exiting thread is the last thread. in the case where it's not, move the release all the way to the end of the function. this way we can clear the tid rather than spending storage on a dedicated dead-flag. clearing the tid is also preferable in that it hardens against inadvertent use of the value after the thread has terminated but before it is joined.
Diffstat (limited to 'src/thread/pthread_create.c')
-rw-r--r--src/thread/pthread_create.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index ac06d7a7..a86b5e1b 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -39,9 +39,11 @@ _Noreturn void __pthread_exit(void *result)
LOCK(self->exitlock);
- /* Mark this thread dead before decrementing count */
+ /* Access to target the exiting thread with syscalls that use
+ * its kernel tid is controlled by killlock. For detached threads,
+ * any use past this point would have undefined behavior, but for
+ * joinable threads it's a valid usage that must be handled. */
LOCK(self->killlock);
- self->dead = 1;
/* Block all signals before decrementing the live thread count.
* This is important to ensure that dynamically allocated TLS
@@ -49,20 +51,14 @@ _Noreturn void __pthread_exit(void *result)
* reasons as well. */
__block_all_sigs(&set);
- /* Wait to unlock the kill lock, which governs functions like
- * pthread_kill which target a thread id, until signals have
- * been blocked. This precludes observation of the thread id
- * as a live thread (with application code running in it) after
- * the thread was reported dead by ESRCH being returned. */
- UNLOCK(self->killlock);
-
/* It's impossible to determine whether this is "the last thread"
* until performing the atomic decrement, since multiple threads
* could exit at the same time. For the last thread, revert the
- * decrement and unblock signals to give the atexit handlers and
- * stdio cleanup code a consistent state. */
+ * decrement, restore the tid, and unblock signals to give the
+ * atexit handlers and stdio cleanup code a consistent state. */
if (a_fetch_add(&libc.threads_minus_1, -1)==0) {
libc.threads_minus_1 = 0;
+ UNLOCK(self->killlock);
__restore_sigs(&set);
exit(0);
}
@@ -113,6 +109,12 @@ _Noreturn void __pthread_exit(void *result)
__unmapself(self->map_base, self->map_size);
}
+ /* After the kernel thread exits, its tid may be reused. Clear it
+ * to prevent inadvertent use and inform functions that would use
+ * it that it's no longer available. */
+ self->tid = 0;
+ UNLOCK(self->killlock);
+
for (;;) __syscall(SYS_exit, 0);
}