summaryrefslogtreecommitdiff
path: root/lib/esan
diff options
context:
space:
mode:
authorDerek Bruening <bruening@google.com>2016-07-06 21:04:48 +0000
committerDerek Bruening <bruening@google.com>2016-07-06 21:04:48 +0000
commit3df39427e495cb2ae84b9d7fc8541d4601f7b597 (patch)
tree7f4a0a5a7f6cb3aa17e1250fb82cf2c4891f1f4e /lib/esan
parentccf627eb417a383adffda4d894a3776792bbbf4c (diff)
[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 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@274672 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/esan')
-rw-r--r--lib/esan/esan.cpp6
-rw-r--r--lib/esan/esan.h1
-rw-r--r--lib/esan/esan_interceptors.cpp39
-rw-r--r--lib/esan/working_set.h1
-rw-r--r--lib/esan/working_set_posix.cpp25
5 files changed, 70 insertions, 2 deletions
diff --git a/lib/esan/esan.cpp b/lib/esan/esan.cpp
index e6d6aff31..970f91a83 100644
--- a/lib/esan/esan.cpp
+++ b/lib/esan/esan.cpp
@@ -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.
diff --git a/lib/esan/esan.h b/lib/esan/esan.h
index a5db5dfb3..4171522db 100644
--- a/lib/esan/esan.h
+++ b/lib/esan/esan.h
@@ -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
diff --git a/lib/esan/esan_interceptors.cpp b/lib/esan/esan_interceptors.cpp
index 188336bed..647f01085 100644
--- a/lib/esan/esan_interceptors.cpp
+++ b/lib/esan/esan_interceptors.cpp
@@ -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);
diff --git a/lib/esan/working_set.h b/lib/esan/working_set.h
index 034dfe6d9..660b5d066 100644
--- a/lib/esan/working_set.h
+++ b/lib/esan/working_set.h
@@ -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
diff --git a/lib/esan/working_set_posix.cpp b/lib/esan/working_set_posix.cpp
index 0f36c86fe..fcfa87128 100644
--- a/lib/esan/working_set_posix.cpp
+++ b/lib/esan/working_set_posix.cpp
@@ -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));