forked from OSchip/llvm-project
[esan|wset] Ensure SIGSEGV is not blocked
Summary: Adds interception of sigprocmask and pthread_sigmask to esan so that the working set tool can prevent SIGSEGV from being blocked. A blocked SIGSEGV results in crashes due to our lazy shadow page allocation scheme. Adds new sanitizer helper functions internal_sigemptyset and internal_sigismember. Adds a test to workingset-signal-posix.cpp. Reviewers: aizatsky Subscribers: vitalybuka, zhaoqin, kcc, eugenis, llvm-commits, kubabrecka Differential Revision: http://reviews.llvm.org/D22063 llvm-svn: 274672
This commit is contained in:
parent
01e8082418
commit
34913f3beb
|
@ -85,6 +85,12 @@ bool processSigaction(int SigNum, const void *Act, void *OldAct) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool processSigprocmask(int How, void *Set, void *OldSet) {
|
||||
if (__esan_which_tool == ESAN_WorkingSet)
|
||||
return processWorkingSetSigprocmask(How, Set, OldSet);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if SANITIZER_DEBUG
|
||||
static bool verifyShadowScheme() {
|
||||
// Sanity checks for our shadow mapping scheme.
|
||||
|
|
|
@ -51,6 +51,7 @@ uptr checkMmapResult(uptr Addr, SIZE_T Size);
|
|||
// The return value indicates whether to call the real version or not.
|
||||
bool processSignal(int SigNum, void (*Handler)(int), void (**Result)(int));
|
||||
bool processSigaction(int SigNum, const void *Act, void *OldAct);
|
||||
bool processSigprocmask(int How, void *Set, void *OldSet);
|
||||
|
||||
} // namespace __esan
|
||||
|
||||
|
|
|
@ -42,6 +42,9 @@ using namespace __esan; // NOLINT
|
|||
// intercept malloc soon ourselves and can then remove this undef.
|
||||
#undef SANITIZER_INTERCEPT_REALPATH
|
||||
|
||||
// We provide our own version:
|
||||
#undef SANITIZER_INTERCEPT_SIGPROCMASK
|
||||
|
||||
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!EsanIsInitialized)
|
||||
|
||||
#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
|
||||
|
@ -420,6 +423,40 @@ int real_sigaction(int signum, const void *act, void *oldact) {
|
|||
#define ESAN_MAYBE_INTERCEPT_SIGACTION
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
|
||||
__sanitizer_sigset_t *oldset) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
|
||||
int res = 0;
|
||||
if (processSigprocmask(how, set, oldset))
|
||||
res = REAL(sigprocmask)(how, set, oldset);
|
||||
if (!res && oldset)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
|
||||
return res;
|
||||
}
|
||||
#define ESAN_MAYBE_INTERCEPT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask)
|
||||
#else
|
||||
#define ESAN_MAYBE_INTERCEPT_SIGPROCMASK
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_WINDOWS
|
||||
INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
|
||||
__sanitizer_sigset_t *oldset) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset);
|
||||
int res = 0;
|
||||
if (processSigprocmask(how, set, oldset))
|
||||
res = REAL(sigprocmask)(how, set, oldset);
|
||||
if (!res && oldset)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
|
||||
return res;
|
||||
}
|
||||
#define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK INTERCEPT_FUNCTION(pthread_sigmask)
|
||||
#else
|
||||
#define ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK
|
||||
#endif
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Malloc interceptors
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -493,6 +530,8 @@ void initializeInterceptors() {
|
|||
|
||||
ESAN_MAYBE_INTERCEPT_SIGNAL;
|
||||
ESAN_MAYBE_INTERCEPT_SIGACTION;
|
||||
ESAN_MAYBE_INTERCEPT_SIGPROCMASK;
|
||||
ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
|
||||
|
||||
INTERCEPT_FUNCTION(calloc);
|
||||
INTERCEPT_FUNCTION(free);
|
||||
|
|
|
@ -31,6 +31,7 @@ void registerMemoryFaultHandler();
|
|||
bool processWorkingSetSignal(int SigNum, void (*Handler)(int),
|
||||
void (**Result)(int));
|
||||
bool processWorkingSetSigaction(int SigNum, const void *Act, void *OldAct);
|
||||
bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet);
|
||||
|
||||
} // namespace __esan
|
||||
|
||||
|
|
|
@ -55,6 +55,21 @@ bool processWorkingSetSigaction(int SigNum, const void *ActVoid,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool processWorkingSetSigprocmask(int How, void *Set, void *OldSet) {
|
||||
VPrintf(2, "%s\n", __FUNCTION__);
|
||||
// All we need to do is ensure that SIGSEGV is not blocked.
|
||||
// FIXME: we are not fully transparent as we do not pretend that
|
||||
// SIGSEGV is still blocked on app queries: that would require
|
||||
// per-thread mask tracking.
|
||||
if (Set && (How == SIG_BLOCK || How == SIG_SETMASK)) {
|
||||
if (internal_sigismember((__sanitizer_sigset_t *)Set, SIGSEGV)) {
|
||||
VPrintf(1, "%s: removing SIGSEGV from the blocked set\n", __FUNCTION__);
|
||||
internal_sigdelset((__sanitizer_sigset_t *)Set, SIGSEGV);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void reinstateDefaultHandler(int SigNum) {
|
||||
__sanitizer_sigaction SigAct;
|
||||
internal_memset(&SigAct, 0, sizeof(SigAct));
|
||||
|
@ -95,8 +110,14 @@ void registerMemoryFaultHandler() {
|
|||
// FIXME: This could result in problems with emulating the app's signal
|
||||
// handling if the app relies on an alternate stack for SIGSEGV.
|
||||
|
||||
// We assume SIGSEGV is not blocked and won't be blocked by the app, so
|
||||
// we leave the mask alone.
|
||||
// We require that SIGSEGV is not blocked. We use a sigprocmask
|
||||
// interceptor to ensure that in the future. Here we ensure it for
|
||||
// the current thread. We assume there are no other threads at this
|
||||
// point during initialization, or that at least they do not block
|
||||
// SIGSEGV.
|
||||
__sanitizer_sigset_t SigSet;
|
||||
internal_sigemptyset(&SigSet);
|
||||
internal_sigprocmask(SIG_BLOCK, &SigSet, nullptr);
|
||||
|
||||
__sanitizer_sigaction SigAct;
|
||||
internal_memset(&SigAct, 0, sizeof(SigAct));
|
||||
|
|
|
@ -705,6 +705,10 @@ void internal_sigfillset(__sanitizer_sigset_t *set) {
|
|||
internal_memset(set, 0xff, sizeof(*set));
|
||||
}
|
||||
|
||||
void internal_sigemptyset(__sanitizer_sigset_t *set) {
|
||||
internal_memset(set, 0, sizeof(*set));
|
||||
}
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
|
||||
signum -= 1;
|
||||
|
@ -715,6 +719,16 @@ void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
|
|||
const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
|
||||
k_set->sig[idx] &= ~(1 << bit);
|
||||
}
|
||||
|
||||
bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
|
||||
signum -= 1;
|
||||
CHECK_GE(signum, 0);
|
||||
CHECK_LT(signum, sizeof(*set) * 8);
|
||||
__sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
|
||||
const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
|
||||
const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
|
||||
return k_set->sig[idx] & (1 << bit);
|
||||
}
|
||||
#endif // SANITIZER_LINUX
|
||||
|
||||
// ThreadLister implementation.
|
||||
|
|
|
@ -82,6 +82,8 @@ int my_pthread_attr_getstack(void *attr, void **addr, uptr *size);
|
|||
// order for internal_sigaction() to bypass interceptors.
|
||||
int internal_sigaction(int signum, const void *act, void *oldact);
|
||||
void internal_sigfillset(__sanitizer_sigset_t *set);
|
||||
void internal_sigemptyset(__sanitizer_sigset_t *set);
|
||||
bool internal_sigismember(__sanitizer_sigset_t *set, int signum);
|
||||
|
||||
uptr internal_execve(const char *filename, char *const argv[],
|
||||
char *const envp[]);
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
|
||||
// RUN: %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
sigjmp_buf mark;
|
||||
|
||||
|
@ -51,10 +52,24 @@ int main(int argc, char **argv) {
|
|||
assert(Res == 0);
|
||||
assert(SigAct.sa_sigaction == SigactionHandler);
|
||||
|
||||
// Test blocking SIGSEGV and raising a shadow fault.
|
||||
sigset_t Set;
|
||||
sigemptyset(&Set);
|
||||
sigaddset(&Set, SIGSEGV);
|
||||
Res = sigprocmask(SIG_BLOCK, &Set, NULL);
|
||||
// Make a large enough mapping that its start point will be before any
|
||||
// prior library-region shadow access.
|
||||
char *buf = (char *)mmap(0, 640*1024, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
buf[0] = 4;
|
||||
munmap(buf, 640*1024);
|
||||
fprintf(stderr, "Past blocked-SIGSEGV shadow fault\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
// CHECK: Handling SIGSEGV for signal
|
||||
// CHECK-NEXT: Past longjmp for signal
|
||||
// CHECK-NEXT: Handling SIGSEGV for sigaction
|
||||
// CHECK-NEXT: Past longjmp for sigaction
|
||||
// CHECK: {{.*}} EfficiencySanitizer: the total working set size: {{[0-9][0-9][0-9]}} Bytes ({{[0-9][0-9]}} cache lines)
|
||||
// CHECK-NEXT: Past blocked-SIGSEGV shadow fault
|
||||
// CHECK: {{.*}} EfficiencySanitizer: the total working set size: {{[0-9]+}} Bytes ({{[0-9][0-9]}} cache lines)
|
||||
|
|
Loading…
Reference in New Issue