diff options
author | Rich Felker <dalias@aerifal.cx> | 2011-04-03 13:03:18 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2011-04-03 13:03:18 -0400 |
commit | 1ad049b7b60b2c1704b8bb5b94ee4f95d7540b3a (patch) | |
tree | ba71b21231616ff573fe20d293cf338a1ca8212a /src/thread | |
parent | c9b2d8016fca3b0545433e9d58a04c038b6fc921 (diff) | |
download | musl-1ad049b7b60b2c1704b8bb5b94ee4f95d7540b3a.tar.gz musl-1ad049b7b60b2c1704b8bb5b94ee4f95d7540b3a.tar.bz2 musl-1ad049b7b60b2c1704b8bb5b94ee4f95d7540b3a.tar.xz musl-1ad049b7b60b2c1704b8bb5b94ee4f95d7540b3a.zip |
fix race condition in rsyscall handler
the problem: there is a (single-instruction) race condition window
between a thread flagging itself dead and decrementing itself from the
thread count. if it receives the rsyscall signal at this exact moment,
the rsyscall caller will never succeed in signalling enough flags to
succeed, and will deadlock forever. in previous versions of musl, the
about-to-terminate thread masked all signals prior to decrementing
the thread count, but this cost a whole syscall just to account for
extremely rare races.
the solution is a huge hack: rather than blocking in the signal
handler if the thread is dead, modify the signal mask of the saved
context and return in order to prevent further signal handling by the
dead thread. this allows the dead thread to continue decrementing the
thread count (if it had not yet done so) and exiting, even while the
live part of the program blocks for rsyscall.
Diffstat (limited to 'src/thread')
-rw-r--r-- | src/thread/pthread_create.c | 2 |
1 files changed, 1 insertions, 1 deletions
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c index 9df4f712..344b39f8 100644 --- a/src/thread/pthread_create.c +++ b/src/thread/pthread_create.c @@ -85,7 +85,7 @@ static void rsyscall_handler(int sig, siginfo_t *si, void *ctx) /* Threads which have already decremented themselves from the * thread count must not increment rs.cnt or otherwise act. */ if (self->dead) { - __wait(&rs.hold, 0, 1, 1); + sigaddset(&((ucontext_t *)ctx)->uc_sigmask, SIGSYSCALL); return; } |