Index: lib/esan/CMakeLists.txt =================================================================== --- lib/esan/CMakeLists.txt +++ lib/esan/CMakeLists.txt @@ -12,7 +12,8 @@ esan_flags.cpp esan_interface.cpp esan_interceptors.cpp - working_set.cpp) + working_set.cpp + working_set_posix.cpp) foreach (arch ${ESAN_SUPPORTED_ARCH}) add_compiler_rt_runtime(clang_rt.esan Index: lib/esan/esan.h =================================================================== --- lib/esan/esan.h +++ lib/esan/esan.h @@ -44,6 +44,11 @@ void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite); void initializeInterceptors(); +// Platform-dependent routines. +// 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); + } // namespace __esan #endif // ESAN_H Index: lib/esan/esan.cpp =================================================================== --- lib/esan/esan.cpp +++ lib/esan/esan.cpp @@ -72,6 +72,18 @@ } } +bool processSignal(int SigNum, void (*Handler)(int), void (**Result)(int)) { + if (WhichTool == ESAN_WorkingSet) + return processWorkingSetSignal(SigNum, Handler, Result); + return true; +} + +bool processSigaction(int SigNum, const void *Act, void *OldAct) { + if (WhichTool == ESAN_WorkingSet) + return processWorkingSetSigaction(SigNum, Act, OldAct); + return true; +} + #if SANITIZER_DEBUG static bool verifyShadowScheme() { // Sanity checks for our shadow mapping scheme. Index: lib/esan/esan_interceptors.cpp =================================================================== --- lib/esan/esan_interceptors.cpp +++ lib/esan/esan_interceptors.cpp @@ -386,6 +386,43 @@ #define ESAN_MAYBE_INTERCEPT_MMAP64 #endif +//===----------------------------------------------------------------------===// +// Signal-related interceptors +//===----------------------------------------------------------------------===// + +#if SANITIZER_LINUX +typedef void (*signal_handler_t)(int); +INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, signal, signum, handler); + signal_handler_t result; + if (!processSignal(signum, handler, &result)) + return result; + else + return REAL(signal)(signum, handler); +} +#define ESAN_MAYBE_INTERCEPT_SIGNAL INTERCEPT_FUNCTION(signal) +#else +#error Platform not supported +#define ESAN_MAYBE_INTERCEPT_SIGNAL +#endif + +#if SANITIZER_LINUX +INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigaction, signum, act, oldact); + if (!processSigaction(signum, act, oldact)) + return 0; + else + return REAL(sigaction)(signum, act, oldact); +} +#define ESAN_MAYBE_INTERCEPT_SIGACTION INTERCEPT_FUNCTION(sigaction) +#else +#error Platform not supported +#define ESAN_MAYBE_INTERCEPT_SIGACTION +#endif + namespace __esan { void initializeInterceptors() { @@ -407,6 +444,9 @@ INTERCEPT_FUNCTION(mmap); ESAN_MAYBE_INTERCEPT_MMAP64; + ESAN_MAYBE_INTERCEPT_SIGNAL; + ESAN_MAYBE_INTERCEPT_SIGACTION; + // TODO(bruening): we should intercept calloc() and other memory allocation // routines that zero memory and update our shadow memory appropriately. Index: lib/esan/working_set.h =================================================================== --- lib/esan/working_set.h +++ lib/esan/working_set.h @@ -25,6 +25,12 @@ void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size, bool IsWrite); +// Platform-dependent. +void registerMemoryFaultHandler(); +bool processWorkingSetSignal(int SigNum, void (*Handler)(int), + void (**Result)(int)); +bool processWorkingSetSigaction(int SigNum, const void *Act, void *OldAct); + } // namespace __esan #endif // WORKING_SET_H Index: lib/esan/working_set.cpp =================================================================== --- lib/esan/working_set.cpp +++ lib/esan/working_set.cpp @@ -78,6 +78,7 @@ void initializeWorkingSet() { // The shadow mapping assumes 64 so this cannot be changed. CHECK(getFlags()->cache_line_size == 64); + registerMemoryFaultHandler(); } int finalizeWorkingSet() { Index: lib/esan/working_set_posix.cpp =================================================================== --- /dev/null +++ lib/esan/working_set_posix.cpp @@ -0,0 +1,106 @@ +//===-- working_set_posix.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// POSIX-specific working set tool code. +//===----------------------------------------------------------------------===// + +#include "working_set.h" +#include "esan_flags.h" +#include "esan_shadow.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_linux.h" +#include +#include + +namespace __esan { + +// We only support regular POSIX threads with a single signal handler +// for the whole process == thread group. +// Thus we only need to store one app signal handler. +// FIXME: Store and use any alternate stack and signal flags set by +// the app. For now we just call the app handler from our handler. +static __sanitizer_sigaction AppSigAct; + +bool processWorkingSetSignal(int SigNum, void (*Handler)(int), + void (**Result)(int)) { + VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum); + if (SigNum == SIGSEGV) { + *Result = AppSigAct.handler; + AppSigAct.sigaction = (void (*)(int, void*, void*))Handler; + return false; // Skip real call. + } + return true; +} + +bool processWorkingSetSigaction(int SigNum, const void *ActVoid, + void *OldActVoid) { + VPrintf(2, "%s: %d\n", __FUNCTION__, SigNum); + if (SigNum == SIGSEGV) { + const struct sigaction *Act = (const struct sigaction *) ActVoid; + struct sigaction *OldAct = (struct sigaction *) OldActVoid; + if (OldAct) + internal_memcpy(OldAct, &AppSigAct, sizeof(OldAct)); + if (Act) + internal_memcpy(&AppSigAct, Act, sizeof(AppSigAct)); + return false; // Skip real call. + } + return true; +} + +static void reinstateDefaultHandler(int SigNum) { + __sanitizer_sigaction SigAct; + internal_memset(&SigAct, 0, sizeof(SigAct)); + SigAct.sigaction = (void (*)(int, void*, void*)) SIG_DFL; + int Res = internal_sigaction(SigNum, &SigAct, nullptr); + CHECK(Res == 0); + VPrintf(1, "Unregistered for %d handler\n", SigNum); +} + +// If this is a shadow fault, we handle it here; otherwise, we pass it to the +// app to handle it just as the app would do without our tool in place. +static void handleMemoryFault(int SigNum, void *Info, void *Ctx) { + if (SigNum == SIGSEGV) { + + // TODO: Add shadow memory fault detection and handling. + + if (AppSigAct.sigaction) { + // FIXME: For simplicity we ignore app options including its signal stack + // (we just use ours) and all the delivery flags. + AppSigAct.sigaction(SigNum, Info, Ctx); + } else { + // Crash instead of spinning with infinite faults. + reinstateDefaultHandler(SigNum); + } + } else + UNREACHABLE("signal not registered"); +} + +void registerMemoryFaultHandler() { + // We do not use an alternate signal stack, as doing so would require + // setting it up for each app thread. + // 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. + + __sanitizer_sigaction SigAct; + internal_memset(&SigAct, 0, sizeof(SigAct)); + SigAct.sigaction = handleMemoryFault; + // We want to handle nested signals b/c we need to handle a + // shadow fault in an app signal handler. + SigAct.sa_flags = SA_SIGINFO | SA_NODEFER; + int Res = internal_sigaction(SIGSEGV, &SigAct, &AppSigAct); + CHECK(Res == 0); + VPrintf(1, "Registered for SIGSEGV handler\n"); +} + +} // namespace __esan Index: test/esan/TestCases/workingset-signal-posix.cpp =================================================================== --- /dev/null +++ test/esan/TestCases/workingset-signal-posix.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_esan_wset -O0 %s -o %t 2>&1 +// RUN: %run %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include + +sigjmp_buf mark; + +static void SignalHandler(int Sig) { + if (Sig == SIGSEGV) { + fprintf(stderr, "Handling SIGSEGV for signal\n"); + siglongjmp(mark, 1); + } + exit(1); +} + +static void SigactionHandler(int Sig, siginfo_t *Info, void *Ctx) { + if (Sig == SIGSEGV) { + fprintf(stderr, "Handling SIGSEGV for sigaction\n"); + siglongjmp(mark, 1); + } + exit(1); +} + +int main(int argc, char **argv) { + __sighandler_t Prior = signal(SIGSEGV, SignalHandler); + assert(Prior == SIG_DFL); + if (sigsetjmp(mark, 1) == 0) + *((volatile int *)(ssize_t)argc) = 42; // Raise SIGSEGV + fprintf(stderr, "Past longjmp for signal\n"); + + Prior = signal(SIGSEGV, SIG_DFL); + assert(Prior == SignalHandler); + + struct sigaction SigAct; + SigAct.sa_sigaction = SigactionHandler; + int Res = sigfillset(&SigAct.sa_mask); + assert(Res == 0); + SigAct.sa_flags = SA_SIGINFO; + Res = sigaction(SIGSEGV, &SigAct, NULL); + assert(Res == 0); + + if (sigsetjmp(mark, 1) == 0) + *((volatile int *)(ssize_t)argc) = 42; // Raise SIGSEGV + fprintf(stderr, "Past longjmp for sigaction\n"); + + Res = sigaction(SIGSEGV, NULL, &SigAct); + assert(Res == 0); + assert(SigAct.sa_sigaction == SigactionHandler); + + 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