forked from OSchip/llvm-project
tsan: implement internal syscall-based versions of sigaction/sigprocmask
use them in stoptheworld fixes applications that intercept sigaction/sigprocmask llvm-svn: 192686
This commit is contained in:
parent
3feb458a56
commit
59c850de6d
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 <dirent.h>, 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);
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue