summaryrefslogtreecommitdiff
path: root/lib/esan
diff options
context:
space:
mode:
authorDerek Bruening <bruening@google.com>2016-05-31 13:21:03 +0000
committerDerek Bruening <bruening@google.com>2016-05-31 13:21:03 +0000
commit0573d6c7f326a08eeff6e595fd77045a0210c32a (patch)
tree9da5bc281151889b2b8b836cbf9373fb94105a2d /lib/esan
parent124601f8dc708132bf13ccdfbe927b8118b68136 (diff)
[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
Diffstat (limited to 'lib/esan')
-rw-r--r--lib/esan/CMakeLists.txt3
-rw-r--r--lib/esan/esan.cpp12
-rw-r--r--lib/esan/esan.h3
-rw-r--r--lib/esan/esan_interceptors.cpp40
-rw-r--r--lib/esan/working_set.cpp1
-rw-r--r--lib/esan/working_set.h6
-rw-r--r--lib/esan/working_set_posix.cpp106
7 files changed, 170 insertions, 1 deletions
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 <signal.h>
+#include <sys/mman.h>
+
+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