From 0573d6c7f326a08eeff6e595fd77045a0210c32a Mon Sep 17 00:00:00 2001 From: Derek Bruening Date: Tue, 31 May 2016 13:21:03 +0000 Subject: [esan] Intercept and chain signal handlers Summary: In preparation for fault-based shadow memory iteration, we add support for our own signal handler by adding app signal handler interception as well as chaining for SIGSEGV. This is done in a simple manner: we do not honor the app's alternate stack nor any sigaction flags for SIGSEGV. Adds a new test of transparency in app signal handling. Reviewers: aizatsky Subscribers: filcab, kubabrecka, vitalybuka, zhaoqin, kcc, eugenis, llvm-commits Differential Revision: http://reviews.llvm.org/D20577 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@271272 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/esan/CMakeLists.txt | 3 +- lib/esan/esan.cpp | 12 +++++ lib/esan/esan.h | 3 ++ lib/esan/esan_interceptors.cpp | 40 ++++++++++++++++ lib/esan/working_set.cpp | 1 + lib/esan/working_set.h | 6 +++ lib/esan/working_set_posix.cpp | 106 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 lib/esan/working_set_posix.cpp (limited to 'lib/esan') diff --git a/lib/esan/CMakeLists.txt b/lib/esan/CMakeLists.txt index 24bed26a2..ac994d949 100644 --- a/lib/esan/CMakeLists.txt +++ b/lib/esan/CMakeLists.txt @@ -14,7 +14,8 @@ set(ESAN_SOURCES esan_interceptors.cpp esan_linux.cpp cache_frag.cpp - working_set.cpp) + working_set.cpp + working_set_posix.cpp) foreach (arch ${ESAN_SUPPORTED_ARCH}) add_compiler_rt_runtime(clang_rt.esan diff --git a/lib/esan/esan.cpp b/lib/esan/esan.cpp index 2711e4b97..c9629d65c 100644 --- a/lib/esan/esan.cpp +++ b/lib/esan/esan.cpp @@ -73,6 +73,18 @@ void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite) { } } +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. diff --git a/lib/esan/esan.h b/lib/esan/esan.h index 5be2240b3..eb2e8b474 100644 --- a/lib/esan/esan.h +++ b/lib/esan/esan.h @@ -49,6 +49,9 @@ void initializeInterceptors(); void verifyAddressSpace(); bool fixMmapAddr(void **Addr, SIZE_T Size, int Flags); 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); } // namespace __esan diff --git a/lib/esan/esan_interceptors.cpp b/lib/esan/esan_interceptors.cpp index a9489d29e..8ed870984 100644 --- a/lib/esan/esan_interceptors.cpp +++ b/lib/esan/esan_interceptors.cpp @@ -351,6 +351,43 @@ INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags, #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() { @@ -372,6 +409,9 @@ void initializeInterceptors() { 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. diff --git a/lib/esan/working_set.cpp b/lib/esan/working_set.cpp index 3cee6155b..68ea9e74a 100644 --- a/lib/esan/working_set.cpp +++ b/lib/esan/working_set.cpp @@ -78,6 +78,7 @@ void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size, void initializeWorkingSet() { // The shadow mapping assumes 64 so this cannot be changed. CHECK(getFlags()->cache_line_size == 64); + registerMemoryFaultHandler(); } int finalizeWorkingSet() { diff --git a/lib/esan/working_set.h b/lib/esan/working_set.h index 4b1bc1596..3750a480a 100644 --- a/lib/esan/working_set.h +++ b/lib/esan/working_set.h @@ -25,6 +25,12 @@ int finalizeWorkingSet(); 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 diff --git a/lib/esan/working_set_posix.cpp b/lib/esan/working_set_posix.cpp new file mode 100644 index 000000000..f0191766b --- /dev/null +++ b/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 -- cgit v1.2.3