Open
Description
I have a multithreaded Linux application that heavily relies on signals. Unfortunately, ThreadSanitizer doesn't seem to support nested signals, which causes my application to hang.
I am using TSan provided with gcc and I built gcc (commit 3e0768d2ffde3f20c5baa92d33869f0c196245c4) myself in order to debug ThreadSanitizer. Unfortunately, I don't know where to find the TSan version used by gcc with this commit. This was the first time I built gcc, so I hope it's reasonably okay.
gcc -v
Using built-in specs.
COLLECT_GCC=./gcc
COLLECT_LTO_WRAPPER=/usr/local/gcc/libexec/gcc/x86_64-pc-linux-gnu/15.0.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../configure --prefix=/usr/local/gcc --enable-languages=c,c++ --disable-bootstrap --with-dwarf2 --enable-checking=no CFLAGS='-g -O0' CXXFLAGS='-g -O0'
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 15.0.1 20250327 (experimental) (GCC)
Here's what happens:
- A thread receives a signal.
- TSan catches the signal with its
sighandler()
and incrementsTheadState::pending_signals
. - TSan intercepts a call made by the thread and handles the pending signal in
ProcessPendingSignalsImpl()
. ProcessPendingSignalsImpl()
decrementsTheadState::pending_signals
, stores the old signal mask/set inThreadSignalContext::oldset
of the thread and executes the application's signal handler for the pending signal.- The application's signal calls
sigsuspend()
, a signal-safe function, to block the thread until it receives another signal. - When the other signal
sigsuspend()
is waiting for was caught, TSan'ssighandler()
is executed again incrementingTheadState::pending_signals
. - Before leaving the
sigsuspend()
call made by the application's signal handler, TSan callsProcessPendingSignalsImpl()
to process the new pending signal. ProcessPendingSignalsImpl()
decrementsTheadState::pending_signals
and stores again the old signal mask/set inThreadSignalContext::oldset
of the thread. Unfortunately, this overwrites the previous stored old set. The previous old set, which had most signals unblocked, is now overwritten with a set that is used by the first call toProcessPendingSignalsImpl()
. Now, when TSan returns from the nested call toProcessPendingSignalsImpl()
, it restores the old set with all signals blocked and when TSan returns from the firstProcessPendingSignalsImpl()
, it again restores the same old set with all signals blocked.- My application continues execution with all signals blocked, although they should be unblocked, resulting in the application hanging.
Is there a workaround for this issue or is this something that needs to be fixed in TSan in order to work?
Or am I doing something wrong?