diff options
-rw-r--r-- | compiler-rt/lib/tsan/rtl/tsan_interceptors.cc | 41 | ||||
-rw-r--r-- | compiler-rt/test/tsan/signal_block.cc | 60 |
2 files changed, 92 insertions, 9 deletions
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc index fb6227651d2..90b1264bf66 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -88,8 +88,6 @@ extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *) -extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, - __sanitizer_sigset_t *oldset); DECLARE_REAL(int, fflush, __sanitizer_FILE *fp) DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size) DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) @@ -1762,6 +1760,31 @@ TSAN_INTERCEPTOR(int, epoll_pwait, int epfd, void *ev, int cnt, int timeout, #define TSAN_MAYBE_INTERCEPT_EPOLL #endif +// The following functions are intercepted merely to process pending signals. +// If program blocks signal X, we must deliver the signal before the function +// returns. Similarly, if program unblocks a signal (or returns from sigsuspend) +// it's better to deliver the signal straight away. +TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { + SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); + return REAL(sigsuspend)(mask); +} + +TSAN_INTERCEPTOR(int, sigblock, int mask) { + SCOPED_TSAN_INTERCEPTOR(sigblock, mask); + return REAL(sigblock)(mask); +} + +TSAN_INTERCEPTOR(int, sigsetmask, int mask) { + SCOPED_TSAN_INTERCEPTOR(sigsetmask, mask); + return REAL(sigsetmask)(mask); +} + +TSAN_INTERCEPTOR(int, pthread_sigmask, int how, const __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + SCOPED_TSAN_INTERCEPTOR(pthread_sigmask, how, set, oldset); + return REAL(pthread_sigmask)(how, set, oldset); +} + namespace __tsan { static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, @@ -1833,7 +1856,8 @@ void ProcessPendingSignals(ThreadState *thr) { atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed); atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed); internal_sigfillset(&sctx->emptyset); - CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sctx->emptyset, &sctx->oldset)); + int res = REAL(pthread_sigmask)(SIG_SETMASK, &sctx->emptyset, &sctx->oldset); + CHECK_EQ(res, 0); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; if (signal->armed) { @@ -1842,7 +1866,8 @@ void ProcessPendingSignals(ThreadState *thr) { &signal->siginfo, &signal->ctx); } } - CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sctx->oldset, 0)); + res = REAL(pthread_sigmask)(SIG_SETMASK, &sctx->oldset, 0); + CHECK_EQ(res, 0); atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed); } @@ -1958,11 +1983,6 @@ TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { return old.sa_handler; } -TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { - SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); - return REAL(sigsuspend)(mask); -} - TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); ThreadSignalContext *sctx = SigCtx(thr); @@ -2553,6 +2573,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(sigaction); TSAN_INTERCEPT(signal); TSAN_INTERCEPT(sigsuspend); + TSAN_INTERCEPT(sigblock); + TSAN_INTERCEPT(sigsetmask); + TSAN_INTERCEPT(pthread_sigmask); TSAN_INTERCEPT(raise); TSAN_INTERCEPT(kill); TSAN_INTERCEPT(pthread_kill); diff --git a/compiler-rt/test/tsan/signal_block.cc b/compiler-rt/test/tsan/signal_block.cc new file mode 100644 index 00000000000..dfd4259c43c --- /dev/null +++ b/compiler-rt/test/tsan/signal_block.cc @@ -0,0 +1,60 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Test that a signal is not delivered when it is blocked. + +#include "test.h" +#include <semaphore.h> +#include <signal.h> +#include <errno.h> + +int stop; +sig_atomic_t signal_blocked; + +void handler(int signum) { + if (signal_blocked) { + fprintf(stderr, "signal arrived when blocked\n"); + exit(1); + } +} + +void *thread(void *arg) { + sigset_t myset; + sigemptyset(&myset); + sigaddset(&myset, SIGUSR1); + while (!__atomic_load_n(&stop, __ATOMIC_RELAXED)) { + usleep(1); + if (pthread_sigmask(SIG_BLOCK, &myset, 0)) { + fprintf(stderr, "pthread_sigmask failed %d\n", errno); + exit(1); + } + signal_blocked = 1; + usleep(1); + signal_blocked = 0; + if (pthread_sigmask(SIG_UNBLOCK, &myset, 0)) { + fprintf(stderr, "pthread_sigmask failed %d\n", errno); + exit(1); + } + } + return 0; +} + +int main(int argc, char** argv) { + struct sigaction act = {}; + act.sa_handler = &handler; + if (sigaction(SIGUSR1, &act, 0)) { + fprintf(stderr, "sigaction failed %d\n", errno); + return 1; + } + pthread_t th; + pthread_create(&th, 0, thread, 0); + for (int i = 0; i < 100000; i++) + pthread_kill(th, SIGUSR1); + __atomic_store_n(&stop, 1, __ATOMIC_RELAXED); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: ThreadSanitizer CHECK +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: DONE |