From 59c850de6da85014f358529f9636e49ce86e5bd2 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 15 Oct 2013 11:31:51 +0000 Subject: [PATCH] tsan: implement internal syscall-based versions of sigaction/sigprocmask use them in stoptheworld fixes applications that intercept sigaction/sigprocmask llvm-svn: 192686 --- .../sanitizer_common_syscalls.inc | 5 +-- .../lib/sanitizer_common/sanitizer_linux.cc | 25 +++++++++++++++ .../lib/sanitizer_common/sanitizer_linux.h | 22 +++++++++++++ .../sanitizer_stoptheworld_linux_libcdep.cc | 32 +++++++++---------- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc index 942ffc4c9c24..eef79864f21d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -37,6 +37,7 @@ #if SANITIZER_LINUX #include "sanitizer_libc.h" +#include "sanitizer_linux.h" #define PRE_SYSCALL(name) \ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name @@ -103,10 +104,6 @@ struct sanitizer_kernel_sockaddr { char sa_data[14]; }; -// Real sigset size is always passed as a syscall argument. -// Declare it "void" to catch sizeof(kernel_sigset_t). -typedef void kernel_sigset_t; - static void kernel_write_iovec(const __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index c1390ff1ae58..9b5620d66059 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -602,6 +602,31 @@ uptr internal_sigaltstack(const struct sigaltstack *ss, return internal_syscall(__NR_sigaltstack, ss, oss); } +uptr internal_sigaction(int signum, const kernel_sigaction_t *act, + struct kernel_sigaction_t *oldact) { + return internal_syscall(__NR_rt_sigaction, signum, act, oldact, + sizeof(kernel_sigset_t)); +} + +uptr internal_sigprocmask(int how, kernel_sigset_t *set, + kernel_sigset_t *oldset) { + return internal_syscall(__NR_rt_sigprocmask, (uptr)how, &set->sig[0], + &oldset->sig[0], sizeof(kernel_sigset_t)); +} + +void internal_sigfillset(kernel_sigset_t *set) { + internal_memset(set, 0xff, sizeof(*set)); +} + +void internal_sigdelset(kernel_sigset_t *set, int signum) { + signum -= 1; + CHECK_GE(signum, 0); + CHECK_LT(signum, sizeof(*set) * 8); + const uptr idx = signum / (sizeof(set->sig[0]) * 8); + const uptr bit = signum % (sizeof(set->sig[0]) * 8); + set->sig[idx] &= ~(1 << bit); +} + // ThreadLister implementation. ThreadLister::ThreadLister(int pid) : pid_(pid), diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h index 08b06e578e6f..5a642c88f53f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -20,17 +20,39 @@ struct link_map; // Opaque type returned by dlopen(). struct sigaltstack; +typedef struct siginfo siginfo_t; namespace __sanitizer { // Dirent structure for getdents(). Note that this structure is different from // the one in , which is used by readdir(). struct linux_dirent; +struct kernel_sigset_t { + u8 sig[FIRST_32_SECOND_64(16, 8)]; +}; + +struct kernel_sigaction_t { + union { + void (*sigaction)(int signo, siginfo_t *info, void *ctx); + void (*handler)(int signo); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + kernel_sigset_t sa_mask; +}; + // Syscall wrappers. uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); uptr internal_sigaltstack(const struct sigaltstack* ss, struct sigaltstack* oss); +uptr internal_sigaction(int signum, const kernel_sigaction_t *act, + kernel_sigaction_t *oldact); +uptr internal_sigprocmask(int how, kernel_sigset_t *set, + kernel_sigset_t *oldset); +void internal_sigfillset(kernel_sigset_t *set); +void internal_sigdelset(kernel_sigset_t *set, int signum); + #ifdef __x86_64__ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index 163bd45a1beb..492da16c8095 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -252,12 +252,12 @@ static int TracerThread(void* argument) { // the mask we inherited from the caller thread. for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { - struct sigaction new_sigaction; + kernel_sigaction_t new_sigaction; internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sa_sigaction = TracerThreadSignalHandler; + new_sigaction.sigaction = TracerThreadSignalHandler; new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; - sigfillset(&new_sigaction.sa_mask); - sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); } int exit_code = 0; @@ -307,9 +307,9 @@ NOINLINE static void WipeStack() { // We have a limitation on the stack frame size, so some stuff had to be moved // into globals. -static sigset_t blocked_sigset; -static sigset_t old_sigset; -static struct sigaction old_sigactions[ARRAY_SIZE(kUnblockedSignals)]; +static kernel_sigset_t blocked_sigset; +static kernel_sigset_t old_sigset; +static kernel_sigaction_t old_sigactions[ARRAY_SIZE(kUnblockedSignals)]; class StopTheWorldScope { public: @@ -323,21 +323,21 @@ class StopTheWorldScope { // We cannot allow user-defined handlers to run while the ThreadSuspender // thread is active, because they could conceivably call some libc functions // which modify errno (which is shared between the two threads). - sigfillset(&blocked_sigset); + internal_sigfillset(&blocked_sigset); for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { // Remove the signal from the set of blocked signals. - sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); + internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); // Install the default handler. - struct sigaction new_sigaction; + kernel_sigaction_t new_sigaction; internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sa_handler = SIG_DFL; - sigfillset(&new_sigaction.sa_mask); - sigaction(kUnblockedSignals[signal_index], &new_sigaction, + new_sigaction.handler = SIG_DFL; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, &old_sigactions[signal_index]); } int sigprocmask_status = - sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); + internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail // Make this process dumpable. Processes that are not dumpable cannot be // attached to. @@ -355,10 +355,10 @@ class StopTheWorldScope { // Restore the signal handlers. for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { - sigaction(kUnblockedSignals[signal_index], + internal_sigaction(kUnblockedSignals[signal_index], &old_sigactions[signal_index], NULL); } - sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); + internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); } private: