[compiler-rt] r271272 - [esan] Intercept and chain signal handlers

Derek Bruening via llvm-commits llvm-commits at lists.llvm.org
Tue May 31 06:21:07 PDT 2016


Author: bruening
Date: Tue May 31 08:21:03 2016
New Revision: 271272

URL: http://llvm.org/viewvc/llvm-project?rev=271272&view=rev
Log:
[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

Added:
    compiler-rt/trunk/lib/esan/working_set_posix.cpp   (with props)
    compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp   (with props)
Modified:
    compiler-rt/trunk/lib/esan/CMakeLists.txt
    compiler-rt/trunk/lib/esan/esan.cpp
    compiler-rt/trunk/lib/esan/esan.h
    compiler-rt/trunk/lib/esan/esan_interceptors.cpp
    compiler-rt/trunk/lib/esan/working_set.cpp
    compiler-rt/trunk/lib/esan/working_set.h

Modified: compiler-rt/trunk/lib/esan/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/CMakeLists.txt?rev=271272&r1=271271&r2=271272&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/esan/CMakeLists.txt Tue May 31 08:21:03 2016
@@ -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

Modified: compiler-rt/trunk/lib/esan/esan.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/esan.cpp?rev=271272&r1=271271&r2=271272&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/esan.cpp (original)
+++ compiler-rt/trunk/lib/esan/esan.cpp Tue May 31 08:21:03 2016
@@ -73,6 +73,18 @@ void processRangeAccess(uptr PC, uptr Ad
   }
 }
 
+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.

Modified: compiler-rt/trunk/lib/esan/esan.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/esan.h?rev=271272&r1=271271&r2=271272&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/esan.h (original)
+++ compiler-rt/trunk/lib/esan/esan.h Tue May 31 08:21:03 2016
@@ -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
 

Modified: compiler-rt/trunk/lib/esan/esan_interceptors.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/esan_interceptors.cpp?rev=271272&r1=271271&r2=271272&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/esan_interceptors.cpp (original)
+++ compiler-rt/trunk/lib/esan/esan_interceptors.cpp Tue May 31 08:21:03 2016
@@ -351,6 +351,43 @@ INTERCEPTOR(void *, mmap64, void *addr,
 #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.
 

Modified: compiler-rt/trunk/lib/esan/working_set.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/working_set.cpp?rev=271272&r1=271271&r2=271272&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/working_set.cpp (original)
+++ compiler-rt/trunk/lib/esan/working_set.cpp Tue May 31 08:21:03 2016
@@ -78,6 +78,7 @@ void processRangeAccessWorkingSet(uptr P
 void initializeWorkingSet() {
   // The shadow mapping assumes 64 so this cannot be changed.
   CHECK(getFlags()->cache_line_size == 64);
+  registerMemoryFaultHandler();
 }
 
 int finalizeWorkingSet() {

Modified: compiler-rt/trunk/lib/esan/working_set.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/working_set.h?rev=271272&r1=271271&r2=271272&view=diff
==============================================================================
--- compiler-rt/trunk/lib/esan/working_set.h (original)
+++ compiler-rt/trunk/lib/esan/working_set.h Tue May 31 08:21:03 2016
@@ -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

Added: compiler-rt/trunk/lib/esan/working_set_posix.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/esan/working_set_posix.cpp?rev=271272&view=auto
==============================================================================
--- compiler-rt/trunk/lib/esan/working_set_posix.cpp (added)
+++ compiler-rt/trunk/lib/esan/working_set_posix.cpp Tue May 31 08:21:03 2016
@@ -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

Propchange: compiler-rt/trunk/lib/esan/working_set_posix.cpp
------------------------------------------------------------------------------
    svn:eol-style = LF

Added: compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp?rev=271272&view=auto
==============================================================================
--- compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp (added)
+++ compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp Tue May 31 08:21:03 2016
@@ -0,0 +1,59 @@
+// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <assert.h>
+
+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

Propchange: compiler-rt/trunk/test/esan/TestCases/workingset-signal-posix.cpp
------------------------------------------------------------------------------
    svn:eol-style = LF




More information about the llvm-commits mailing list