diff options
author | Derek Bruening <bruening@google.com> | 2016-07-06 21:04:48 +0000 |
---|---|---|
committer | Derek Bruening <bruening@google.com> | 2016-07-06 21:04:48 +0000 |
commit | 3df39427e495cb2ae84b9d7fc8541d4601f7b597 (patch) | |
tree | 7f4a0a5a7f6cb3aa17e1250fb82cf2c4891f1f4e /lib/esan | |
parent | ccf627eb417a383adffda4d894a3776792bbbf4c (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.cpp | 6 | ||||
-rw-r--r-- | lib/esan/esan.h | 1 | ||||
-rw-r--r-- | lib/esan/esan_interceptors.cpp | 39 | ||||
-rw-r--r-- | lib/esan/working_set.h | 1 | ||||
-rw-r--r-- | lib/esan/working_set_posix.cpp | 25 |
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)); |