Index: compiler-rt/trunk/lib/esan/esan.h =================================================================== --- compiler-rt/trunk/lib/esan/esan.h +++ compiler-rt/trunk/lib/esan/esan.h @@ -51,6 +51,7 @@ // 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 Index: compiler-rt/trunk/lib/esan/esan.cpp =================================================================== --- compiler-rt/trunk/lib/esan/esan.cpp +++ compiler-rt/trunk/lib/esan/esan.cpp @@ -85,6 +85,12 @@ 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. Index: compiler-rt/trunk/lib/esan/esan_interceptors.cpp =================================================================== --- compiler-rt/trunk/lib/esan/esan_interceptors.cpp +++ compiler-rt/trunk/lib/esan/esan_interceptors.cpp @@ -42,6 +42,9 @@ // 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 @@ #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 @@ ESAN_MAYBE_INTERCEPT_SIGNAL; ESAN_MAYBE_INTERCEPT_SIGACTION; + ESAN_MAYBE_INTERCEPT_SIGPROCMASK; + ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK; INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(free); Index: compiler-rt/trunk/lib/esan/working_set.h =================================================================== --- compiler-rt/trunk/lib/esan/working_set.h +++ compiler-rt/trunk/lib/esan/working_set.h @@ -31,6 +31,7 @@ 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 Index: compiler-rt/trunk/lib/esan/working_set_posix.cpp =================================================================== --- compiler-rt/trunk/lib/esan/working_set_posix.cpp +++ compiler-rt/trunk/lib/esan/working_set_posix.cpp @@ -55,6 +55,21 @@ 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 @@ // 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)); Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc @@ -705,6 +705,10 @@ 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 @@ 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. Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.h =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.h +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_posix.h @@ -82,6 +82,8 @@ // 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[]); Index: compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp =================================================================== --- compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp +++ compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp @@ -1,11 +1,12 @@ // RUN: %clang_esan_wset -O0 %s -o %t 2>&1 // RUN: %run %t 2>&1 | FileCheck %s +#include +#include +#include #include #include -#include -#include -#include +#include sigjmp_buf mark; @@ -51,10 +52,24 @@ 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)