summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2016-07-27 14:34:21 +0000
committerDmitry Vyukov <dvyukov@google.com>2016-07-27 14:34:21 +0000
commit246e0283d40e4045e3d4adc42e2f0303be19c7f9 (patch)
tree30e612d75ac61e44404c6fcd8833845692feb68f
parent6756a2c95335fba8bece4402e62f5057a20f3b4c (diff)
downloadbcm5719-llvm-246e0283d40e4045e3d4adc42e2f0303be19c7f9.tar.gz
bcm5719-llvm-246e0283d40e4045e3d4adc42e2f0303be19c7f9.zip
tsan: don't deliver signals when they are blocked
When we delay signals we can deliver them when the signal is blocked. This can be surprising to the program. Intercept signal blocking functions merely to process pending signals. As the result, at worst we will delay a signal till return from the signal blocking function. llvm-svn: 276876
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interceptors.cc41
-rw-r--r--compiler-rt/test/tsan/signal_block.cc60
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
OpenPOWER on IntegriCloud