asan: fix signal handling during stoptheworld

The problem is that without SA_RESTORER flag, kernel ignores the handler. So tracer actually did not setup any handler.
Add SA_RESTORER flag when setting up handlers.
Add a test that causes SIGSEGV in stoptheworld callback.
Move SignalContext from asan to sanitizer_common to print better diagnostics about signal in the tracer thread.

http://reviews.llvm.org/D8005

llvm-svn: 230978
This commit is contained in:
Dmitry Vyukov 2015-03-02 17:36:02 +00:00
parent d61f7d8c39
commit b79ac88155
12 changed files with 145 additions and 129 deletions

View File

@ -62,21 +62,6 @@ namespace __asan {
class AsanThread;
using __sanitizer::StackTrace;
struct SignalContext {
void *context;
uptr addr;
uptr pc;
uptr sp;
uptr bp;
SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
}
// Creates signal context in a platform-specific manner.
static SignalContext Create(void *siginfo, void *context);
};
void AsanInitFromRtl();
// asan_rtl.cc
@ -90,7 +75,6 @@ void *AsanDoesNotSupportStaticLinkage();
void AsanCheckDynamicRTPrereqs();
void AsanCheckIncompatibleRT();
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
void AsanOnSIGSEGV(int, void *siginfo, void *context);
void DisableReexec();

View File

@ -152,78 +152,6 @@ void AsanCheckIncompatibleRT() {
}
#endif // SANITIZER_ANDROID
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
#if defined(__arm__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.arm_pc;
*bp = ucontext->uc_mcontext.arm_fp;
*sp = ucontext->uc_mcontext.arm_sp;
#elif defined(__aarch64__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.pc;
*bp = ucontext->uc_mcontext.regs[29];
*sp = ucontext->uc_mcontext.sp;
#elif defined(__hppa__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.sc_iaoq[0];
/* GCC uses %r3 whenever a frame pointer is needed. */
*bp = ucontext->uc_mcontext.sc_gr[3];
*sp = ucontext->uc_mcontext.sc_gr[30];
#elif defined(__x86_64__)
# if SANITIZER_FREEBSD
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.mc_rip;
*bp = ucontext->uc_mcontext.mc_rbp;
*sp = ucontext->uc_mcontext.mc_rsp;
# else
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
*bp = ucontext->uc_mcontext.gregs[REG_RBP];
*sp = ucontext->uc_mcontext.gregs[REG_RSP];
# endif
#elif defined(__i386__)
# if SANITIZER_FREEBSD
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.mc_eip;
*bp = ucontext->uc_mcontext.mc_ebp;
*sp = ucontext->uc_mcontext.mc_esp;
# else
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
# endif
#elif defined(__powerpc__) || defined(__powerpc64__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.regs->nip;
*sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
// The powerpc{,64}-linux ABIs do not specify r31 as the frame
// pointer, but GCC always uses r31 when we need a frame pointer.
*bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
#elif defined(__sparc__)
ucontext_t *ucontext = (ucontext_t*)context;
uptr *stk_ptr;
# if defined (__arch64__)
*pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
*sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
stk_ptr = (uptr *) (*sp + 2047);
*bp = stk_ptr[15];
# else
*pc = ucontext->uc_mcontext.gregs[REG_PC];
*sp = ucontext->uc_mcontext.gregs[REG_O6];
stk_ptr = (uptr *) *sp;
*bp = stk_ptr[15];
# endif
#elif defined(__mips__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.gregs[31];
*bp = ucontext->uc_mcontext.gregs[30];
*sp = ucontext->uc_mcontext.gregs[29];
#else
# error "Unsupported arch"
#endif
}
void AsanPlatformThreadInit() {
// Nothing here for now.
}

View File

@ -40,20 +40,6 @@
namespace __asan {
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
ucontext_t *ucontext = (ucontext_t*)context;
# if SANITIZER_WORDSIZE == 64
*pc = ucontext->uc_mcontext->__ss.__rip;
*bp = ucontext->uc_mcontext->__ss.__rbp;
*sp = ucontext->uc_mcontext->__ss.__rsp;
# else
*pc = ucontext->uc_mcontext->__ss.__eip;
*bp = ucontext->uc_mcontext->__ss.__ebp;
*sp = ucontext->uc_mcontext->__ss.__esp;
# endif // SANITIZER_WORDSIZE
}
bool PlatformHasDifferentMemcpyAndMemmove() {
// On OS X 10.7 memcpy() and memmove() are both resolved
// into memmove$VARIANT$sse42.

View File

@ -32,13 +32,6 @@
namespace __asan {
SignalContext SignalContext::Create(void *siginfo, void *context) {
uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
uptr pc, sp, bp;
GetPcSpBp(context, &pc, &sp, &bp);
return SignalContext(context, addr, pc, sp, bp);
}
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
ScopedDeadlySignal signal_scope(GetCurrentThread());
int code = (int)((siginfo_t*)siginfo)->si_code;

View File

@ -613,6 +613,23 @@ static inline void SanitizerBreakOptimization(void *arg) {
#endif
}
struct SignalContext {
void *context;
uptr addr;
uptr pc;
uptr sp;
uptr bp;
SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
}
// Creates signal context in a platform-specific manner.
static SignalContext Create(void *siginfo, void *context);
};
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
} // namespace __sanitizer
inline void *operator new(__sanitizer::operator_new_size_type size,

View File

@ -558,6 +558,7 @@ int internal_fork() {
}
#if SANITIZER_LINUX
#define SA_RESTORER 0x04000000
// Doesn't set sa_restorer, use with caution (see below).
int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
__sanitizer_kernel_sigaction_t k_act, k_oldact;
@ -570,7 +571,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
k_act.sigaction = u_act->sigaction;
internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
sizeof(__sanitizer_kernel_sigset_t));
k_act.sa_flags = u_act->sa_flags;
// Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
// FIXME: most often sa_restorer is unset, however the kernel requires it
// to point to a valid signal restorer that calls the rt_sigreturn syscall.
// If sa_restorer passed to the kernel is NULL, the program may crash upon
@ -928,6 +930,78 @@ void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
void internal_join_thread(void *th) {}
#endif
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
#if defined(__arm__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.arm_pc;
*bp = ucontext->uc_mcontext.arm_fp;
*sp = ucontext->uc_mcontext.arm_sp;
#elif defined(__aarch64__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.pc;
*bp = ucontext->uc_mcontext.regs[29];
*sp = ucontext->uc_mcontext.sp;
#elif defined(__hppa__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.sc_iaoq[0];
/* GCC uses %r3 whenever a frame pointer is needed. */
*bp = ucontext->uc_mcontext.sc_gr[3];
*sp = ucontext->uc_mcontext.sc_gr[30];
#elif defined(__x86_64__)
# if SANITIZER_FREEBSD
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.mc_rip;
*bp = ucontext->uc_mcontext.mc_rbp;
*sp = ucontext->uc_mcontext.mc_rsp;
# else
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
*bp = ucontext->uc_mcontext.gregs[REG_RBP];
*sp = ucontext->uc_mcontext.gregs[REG_RSP];
# endif
#elif defined(__i386__)
# if SANITIZER_FREEBSD
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.mc_eip;
*bp = ucontext->uc_mcontext.mc_ebp;
*sp = ucontext->uc_mcontext.mc_esp;
# else
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
# endif
#elif defined(__powerpc__) || defined(__powerpc64__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.regs->nip;
*sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
// The powerpc{,64}-linux ABIs do not specify r31 as the frame
// pointer, but GCC always uses r31 when we need a frame pointer.
*bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
#elif defined(__sparc__)
ucontext_t *ucontext = (ucontext_t*)context;
uptr *stk_ptr;
# if defined (__arch64__)
*pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
*sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
stk_ptr = (uptr *) (*sp + 2047);
*bp = stk_ptr[15];
# else
*pc = ucontext->uc_mcontext.gregs[REG_PC];
*sp = ucontext->uc_mcontext.gregs[REG_O6];
stk_ptr = (uptr *) *sp;
*bp = stk_ptr[15];
# endif
#elif defined(__mips__)
ucontext_t *ucontext = (ucontext_t*)context;
*pc = ucontext->uc_mcontext.gregs[31];
*bp = ucontext->uc_mcontext.gregs[30];
*sp = ucontext->uc_mcontext.gregs[29];
#else
# error "Unsupported arch"
#endif
}
} // namespace __sanitizer
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX

View File

@ -345,6 +345,19 @@ uptr GetRSS() {
void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
void internal_join_thread(void *th) { }
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
ucontext_t *ucontext = (ucontext_t*)context;
# if SANITIZER_WORDSIZE == 64
*pc = ucontext->uc_mcontext->__ss.__rip;
*bp = ucontext->uc_mcontext->__ss.__rbp;
*sp = ucontext->uc_mcontext->__ss.__rsp;
# else
*pc = ucontext->uc_mcontext->__ss.__eip;
*bp = ucontext->uc_mcontext->__ss.__ebp;
*sp = ucontext->uc_mcontext->__ss.__esp;
# endif // SANITIZER_WORDSIZE
}
} // namespace __sanitizer
#endif // SANITIZER_MAC

View File

@ -20,6 +20,7 @@
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
#include <signal.h>
#include <sys/mman.h>
#if SANITIZER_LINUX
@ -327,6 +328,13 @@ bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
return false;
}
SignalContext SignalContext::Create(void *siginfo, void *context) {
uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
uptr pc, sp, bp;
GetPcSpBp(context, &pc, &sp, &bp);
return SignalContext(context, addr, pc, sp, bp);
}
} // namespace __sanitizer
#endif // SANITIZER_POSIX

View File

@ -103,7 +103,7 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
return false;
} else {
VReport(1, "Attached to thread %d.\n", tid);
VReport(2, "Attached to thread %d.\n", tid);
// The thread is not guaranteed to stop before ptrace returns, so we must
// wait on it. Note: if the thread receives a signal concurrently,
// we can get notification about the signal before notification about stop.
@ -143,7 +143,7 @@ void ThreadSuspender::ResumeAllThreads() {
int pterrno;
if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL),
&pterrno)) {
VReport(1, "Detached from thread %d.\n", tid);
VReport(2, "Detached from thread %d.\n", tid);
} else {
// Either the thread is dead, or we are already detached.
// The latter case is possible, for instance, if this function was called
@ -202,7 +202,10 @@ struct TracerThreadArgument {
static DieCallbackType old_die_callback;
// Signal handler to wake up suspended threads when the tracer thread dies.
void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {
SignalContext ctx = SignalContext::Create(siginfo, uctx);
VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",
signum, ctx.addr, ctx.pc, ctx.sp);
if (thread_suspender_instance != NULL) {
if (signum == SIGABRT)
thread_suspender_instance->KillAllThreads();

View File

@ -189,6 +189,16 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) {
pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex);
}
static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
void *argument) {
*(volatile int*)0x1234 = 0;
}
TEST(StopTheWorld, SegvInCallback) {
// Test that tracer thread catches SIGSEGV.
StopTheWorld(&SegvCallback, NULL);
}
} // namespace __sanitizer
#endif // SANITIZER_LINUX && defined(__x86_64__)

View File

@ -167,7 +167,7 @@ struct SignalDesc {
ucontext_t ctx;
};
struct SignalContext {
struct ThreadSignalContext {
int int_signal_send;
atomic_uintptr_t in_blocking_func;
atomic_uintptr_t have_pending_signals;
@ -194,10 +194,10 @@ void InitializeLibIgnore() {
} // namespace __tsan
static SignalContext *SigCtx(ThreadState *thr) {
SignalContext *ctx = (SignalContext*)thr->signal_ctx;
static ThreadSignalContext *SigCtx(ThreadState *thr) {
ThreadSignalContext *ctx = (ThreadSignalContext*)thr->signal_ctx;
if (ctx == 0 && !thr->is_dead) {
ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
ctx = (ThreadSignalContext*)MmapOrDie(sizeof(*ctx), "ThreadSignalContext");
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
thr->signal_ctx = ctx;
}
@ -298,7 +298,7 @@ struct BlockingCall {
}
ThreadState *thr;
SignalContext *ctx;
ThreadSignalContext *ctx;
};
TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
@ -419,7 +419,7 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
buf->sp = sp;
buf->mangled_sp = mangled_sp;
buf->shadow_stack_pos = thr->shadow_stack_pos;
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
buf->in_blocking_func = sctx ?
atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
@ -442,7 +442,7 @@ static void LongJmp(ThreadState *thr, uptr *env) {
// Unwind the stack.
while (thr->shadow_stack_pos > buf->shadow_stack_pos)
FuncExit(thr);
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
if (sctx) {
sctx->int_signal_send = buf->int_signal_send;
atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
@ -878,7 +878,7 @@ static void thread_finalize(void *v) {
{
ThreadState *thr = cur_thread();
ThreadFinish(thr);
SignalContext *sctx = thr->signal_ctx;
ThreadSignalContext *sctx = thr->signal_ctx;
if (sctx) {
thr->signal_ctx = 0;
UnmapOrDie(sctx, sizeof(*sctx));
@ -1940,7 +1940,7 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
}
void ProcessPendingSignals(ThreadState *thr) {
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
if (sctx == 0 ||
atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
return;
@ -1964,7 +1964,7 @@ void ProcessPendingSignals(ThreadState *thr) {
} // namespace __tsan
static bool is_sync_signal(SignalContext *sctx, int sig) {
static bool is_sync_signal(ThreadSignalContext *sctx, int sig) {
return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
// If we are sending signal to ourselves, we must process it now.
@ -1974,7 +1974,7 @@ static bool is_sync_signal(SignalContext *sctx, int sig) {
void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
my_siginfo_t *info, void *ctx) {
ThreadState *thr = cur_thread();
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
if (sig < 0 || sig >= kSigCount) {
VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig);
return;
@ -2083,7 +2083,7 @@ TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) {
TSAN_INTERCEPTOR(int, raise, int sig) {
SCOPED_TSAN_INTERCEPTOR(raise, sig);
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
sctx->int_signal_send = sig;
@ -2095,7 +2095,7 @@ TSAN_INTERCEPTOR(int, raise, int sig) {
TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
if (pid == (int)internal_getpid()) {
@ -2111,7 +2111,7 @@ TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
SignalContext *sctx = SigCtx(thr);
ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
if (tid == pthread_self()) {

View File

@ -313,7 +313,7 @@ class Shadow : public FastState {
}
};
struct SignalContext;
struct ThreadSignalContext;
struct JmpBuf {
uptr sp;
@ -387,7 +387,7 @@ struct ThreadState {
DDLogicalThread *dd_lt;
atomic_uintptr_t in_signal_handler;
SignalContext *signal_ctx;
ThreadSignalContext *signal_ctx;
DenseSlabAllocCache block_cache;
DenseSlabAllocCache sync_cache;