tsan: fix signal handling

We left ignore_interceptors>0 when calling signal handlers
from blocking interceptors, this leads to missing synchronization in such signal handler.

llvm-svn: 200003
This commit is contained in:
Dmitry Vyukov 2014-01-24 14:16:00 +00:00
parent 041510b290
commit 1b3862ab06
1 changed files with 67 additions and 51 deletions

View File

@ -1628,6 +1628,63 @@ TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
return res;
}
namespace __tsan {
static void CallUserSignalHandler(bool sigact, int sig, my_siginfo_t *info,
void *uctx) {
// Ensure that the handler does not spoil errno.
const int saved_errno = errno;
errno = 0;
if (sigact)
sigactions[sig].sa_sigaction(sig, info, uctx);
else
sigactions[sig].sa_handler(sig);
if (flags()->report_bugs && errno != 0) {
Context *ctx = CTX();
__tsan::StackTrace stack;
uptr pc = sigact ?
(uptr)sigactions[sig].sa_sigaction :
(uptr)sigactions[sig].sa_handler;
pc += 1; // return address is expected, OutputReport() will undo this
stack.Init(&pc, 1);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeErrnoInSignal);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack);
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
}
}
errno = saved_errno;
}
void ProcessPendingSignals(ThreadState *thr) {
SignalContext *sctx = SigCtx(thr);
if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
return;
thr->in_signal_handler = true;
sctx->pending_signal_count = 0;
// These are too big for stack.
static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
REAL(sigfillset)(&emptyset);
pthread_sigmask(SIG_SETMASK, &emptyset, &oldset);
for (int sig = 0; sig < kSigCount; sig++) {
SignalDesc *signal = &sctx->pending_signals[sig];
if (signal->armed) {
signal->armed = false;
if (sigactions[sig].sa_handler != SIG_DFL
&& sigactions[sig].sa_handler != SIG_IGN) {
CallUserSignalHandler(signal->sigaction, sig, &signal->siginfo,
&signal->ctx);
}
}
}
pthread_sigmask(SIG_SETMASK, &oldset, 0);
CHECK_EQ(thr->in_signal_handler, true);
thr->in_signal_handler = false;
}
} // namespace __tsan
void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
my_siginfo_t *info, void *ctx) {
ThreadState *thr = cur_thread();
@ -1643,10 +1700,16 @@ void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
(sctx && sctx->in_blocking_func == 1)) {
CHECK_EQ(thr->in_signal_handler, false);
thr->in_signal_handler = true;
if (sigact)
sigactions[sig].sa_sigaction(sig, info, ctx);
else
sigactions[sig].sa_handler(sig);
if (sctx && sctx->in_blocking_func == 1) {
// We ignore interceptors in blocking functions,
// temporary enbled them again while we are calling user function.
int const i = thr->ignore_interceptors;
thr->ignore_interceptors = 0;
CallUserSignalHandler(sigact, sig, info, ctx);
thr->ignore_interceptors = i;
} else {
CallUserSignalHandler(sigact, sig, info, ctx);
}
CHECK_EQ(thr->in_signal_handler, true);
thr->in_signal_handler = false;
return;
@ -2033,53 +2096,6 @@ static void finalize(void *arg) {
REAL(_exit)(status);
}
void ProcessPendingSignals(ThreadState *thr) {
SignalContext *sctx = SigCtx(thr);
if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
return;
Context *ctx = CTX();
thr->in_signal_handler = true;
sctx->pending_signal_count = 0;
// These are too big for stack.
static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
REAL(sigfillset)(&emptyset);
pthread_sigmask(SIG_SETMASK, &emptyset, &oldset);
for (int sig = 0; sig < kSigCount; sig++) {
SignalDesc *signal = &sctx->pending_signals[sig];
if (signal->armed) {
signal->armed = false;
if (sigactions[sig].sa_handler != SIG_DFL
&& sigactions[sig].sa_handler != SIG_IGN) {
// Insure that the handler does not spoil errno.
const int saved_errno = errno;
errno = 0;
if (signal->sigaction)
sigactions[sig].sa_sigaction(sig, &signal->siginfo, &signal->ctx);
else
sigactions[sig].sa_handler(sig);
if (flags()->report_bugs && errno != 0) {
__tsan::StackTrace stack;
uptr pc = signal->sigaction ?
(uptr)sigactions[sig].sa_sigaction :
(uptr)sigactions[sig].sa_handler;
pc += 1; // return address is expected, OutputReport() will undo this
stack.Init(&pc, 1);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeErrnoInSignal);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack);
OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
}
}
errno = saved_errno;
}
}
}
pthread_sigmask(SIG_SETMASK, &oldset, 0);
CHECK_EQ(thr->in_signal_handler, true);
thr->in_signal_handler = false;
}
static void unreachable() {
Printf("FATAL: ThreadSanitizer: unreachable called\n");
Die();