From: Sebastian Lackner Subject: ntdll: Fix race-condition when threads are killed during shutdown. Message-Id: <54F26398.7060600@fds-team.de> Date: Sun, 01 Mar 2015 01:55:52 +0100 When exit_thread is executed, nb_threads is decremented before the thread is fully shutdown. When another thread runs ExitProcess() this will cause a SIGQUIT signal to all threads, effectively decrementing nb_threads twice. The process will terminate with a wrong exitcode then because the refcount reaches zero too early. This is actually also the reason why I added the Sleep(...) at the end in one of my previous patches, I noticed that the exitcode is zero even if tests fail. Currently Wine has no locking protection of LdrShutdownProcess(), so it can only be executed safely when all other threads have terminated before. Most likely there are more Wine bugs in this area (unlikely that it works that way on Windows), but the attached patch should fix the most critical one (messed up refcounting of threads) for now. It has the side effect that under specific circumstances LdrShutdownProcess is never executed, but thats still way better than before. I'm also open for other suggestions if someone has a better idea how to fix that. --- dlls/ntdll/thread.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) From a2208cb883233d461c2d8d9b61c723273bd7f455 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Wed, 25 Feb 2015 22:45:42 +0100 Subject: ntdll: Fix race-condition when threads are killed during shutdown. --- dlls/ntdll/thread.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 3696c8e..9895f9b 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -370,6 +370,7 @@ void terminate_thread( int status ) void exit_thread( int status ) { static void *prev_teb; + sigset_t sigset; TEB *teb; if (status) /* send the exit code to the server (0 is already the default) */ @@ -383,7 +384,8 @@ void exit_thread( int status ) SERVER_END_REQ; } - if (interlocked_xchg_add( &nb_threads, -1 ) <= 1) + /* We can only shutdown cleanly if this is the last thread */ + if (interlocked_xchg_add( &nb_threads, 0 ) <= 1) { LdrShutdownProcess(); exit( status ); @@ -405,6 +407,14 @@ void exit_thread( int status ) } } + /* Additionally block SIGQUIT, to make sure that + * we're not interrupted after modifying nb_threads. */ + sigemptyset( &sigset ); + sigaddset( &sigset, SIGQUIT ); + pthread_sigmask( SIG_BLOCK, &sigset, NULL ); + + if (interlocked_xchg_add( &nb_threads, -1 ) <= 1) _exit( status ); + close( ntdll_get_thread_data()->wait_fd[0] ); close( ntdll_get_thread_data()->wait_fd[1] ); close( ntdll_get_thread_data()->reply_fd ); -- 2.3.0