[PATCH] [sanitizer] Handle Die() in StopTheWorld.

Sergey Matveev earthdok at google.com
Mon Aug 26 06:17:24 PDT 2013


  move StopTheWorld setup/teardown into a scoped object, as per offline discussion

Hi kcc,

http://llvm-reviews.chandlerc.com/D1515

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1515?vs=3750&id=3751#toc

Files:
  lib/sanitizer_common/sanitizer_common.cc
  lib/sanitizer_common/sanitizer_common.h
  lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc

Index: lib/sanitizer_common/sanitizer_common.cc
===================================================================
--- lib/sanitizer_common/sanitizer_common.cc
+++ lib/sanitizer_common/sanitizer_common.cc
@@ -37,11 +37,15 @@
 // child thread will be different from |report_fd_pid|.
 static uptr report_fd_pid = 0;
 
-static void (*DieCallback)(void);
-void SetDieCallback(void (*callback)(void)) {
+static DieCallbackType DieCallback;
+void SetDieCallback(DieCallbackType callback) {
   DieCallback = callback;
 }
 
+DieCallbackType GetDieCallback() {
+  return DieCallback;
+}
+
 void NORETURN Die() {
   if (DieCallback) {
     DieCallback();
Index: lib/sanitizer_common/sanitizer_common.h
===================================================================
--- lib/sanitizer_common/sanitizer_common.h
+++ lib/sanitizer_common/sanitizer_common.h
@@ -165,7 +165,9 @@
 
 // Specific tools may override behavior of "Die" and "CheckFailed" functions
 // to do tool-specific job.
-void SetDieCallback(void (*callback)(void));
+typedef void (*DieCallbackType)(void);
+void SetDieCallback(DieCallbackType);
+DieCallbackType GetDieCallback();
 typedef void (*CheckFailedCallbackType)(const char *, int, const char *,
                                        u64, u64);
 void SetCheckFailedCallback(CheckFailedCallbackType callback);
Index: lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
===================================================================
--- lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+++ lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -191,6 +191,8 @@
   BlockingMutex mutex;
 };
 
+static DieCallbackType old_die_callback;
+
 // Signal handler to wake up suspended threads when the tracer thread dies.
 void TracerThreadSignalHandler(int signum, siginfo_t *siginfo, void *) {
   if (thread_suspender_instance != NULL) {
@@ -202,6 +204,19 @@
   internal__exit((signum == SIGABRT) ? 1 : 2);
 }
 
+static void TracerThreadDieCallback() {
+  // Generally a call to Die() in the tracer thread should be fatal to the
+  // parent process as well, because they share the address space.
+  // This really only works correctly if all the threads are suspended at this
+  // point. So we correctly handle calls to Die() from within the callback, but
+  // not those that happen before or after the callback. Hopefully there aren't
+  // a lot of opportunities for that to happen...
+  if (thread_suspender_instance)
+    thread_suspender_instance->KillAllThreads();
+  if (old_die_callback)
+    old_die_callback();
+}
+
 // Size of alternative stack for signal handlers in the tracer thread.
 static const int kHandlerStackSize = 4096;
 
@@ -214,6 +229,8 @@
   tracer_thread_argument->mutex.Lock();
   tracer_thread_argument->mutex.Unlock();
 
+  SetDieCallback(TracerThreadDieCallback);
+
   ThreadSuspender thread_suspender(internal_getppid());
   // Global pointer for the signal handler.
   thread_suspender_instance = &thread_suspender;
@@ -283,40 +300,67 @@
   internal_memset(arr, 0, sizeof(arr));
 }
 
+// We have a limitation on the stack frame size, so some stuff had to be moved
+// into globals.
 static sigset_t blocked_sigset;
 static sigset_t old_sigset;
 static struct sigaction old_sigactions[ARRAY_SIZE(kUnblockedSignals)];
 
-void StopTheWorld(StopTheWorldCallback callback, void *argument) {
-  // Glibc's sigaction() has a side-effect where it copies garbage stack values
-  // into oldact, which can cause false negatives in LSan. As a quick workaround
-  // we zero some stack space here.
-  WipeStack();
-  // Block all signals that can be blocked safely, and install default handlers
-  // for the remaining signals.
-  // We cannot allow user-defined handlers to run while the ThreadSuspender
-  // thread is active, because they could conceivably call some libc functions
-  // which modify errno (which is shared between the two threads).
-  sigfillset(&blocked_sigset);
-  for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
-       signal_index++) {
-    // Remove the signal from the set of blocked signals.
-    sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);
-    // Install the default handler.
-    struct sigaction new_sigaction;
-    internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
-    new_sigaction.sa_handler = SIG_DFL;
-    sigfillset(&new_sigaction.sa_mask);
-    sigaction(kUnblockedSignals[signal_index], &new_sigaction,
-                    &old_sigactions[signal_index]);
+class StopTheWorldScope {
+ public:
+  StopTheWorldScope() {
+    // Glibc's sigaction() has a side-effect where it copies garbage stack values
+    // into oldact, which can cause false negatives in LSan. As a quick workaround
+    // we zero some stack space here.
+    WipeStack();
+    // Block all signals that can be blocked safely, and install default handlers
+    // for the remaining signals.
+    // We cannot allow user-defined handlers to run while the ThreadSuspender
+    // thread is active, because they could conceivably call some libc functions
+    // which modify errno (which is shared between the two threads).
+    sigfillset(&blocked_sigset);
+    for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
+         signal_index++) {
+      // Remove the signal from the set of blocked signals.
+      sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);
+      // Install the default handler.
+      struct sigaction new_sigaction;
+      internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
+      new_sigaction.sa_handler = SIG_DFL;
+      sigfillset(&new_sigaction.sa_mask);
+      sigaction(kUnblockedSignals[signal_index], &new_sigaction,
+                      &old_sigactions[signal_index]);
+    }
+    int sigprocmask_status =
+        sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+    CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail
+    // Make this process dumpable. Processes that are not dumpable cannot be
+    // attached to.
+    process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+    if (!process_was_dumpable_)
+      internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+    old_die_callback = GetDieCallback();
+  }
+
+  ~StopTheWorldScope() {
+    SetDieCallback(old_die_callback);
+    // Restore the dumpable flag.
+    if (!process_was_dumpable_)
+      internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+    // Restore the signal handlers.
+    for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
+         signal_index++) {
+      sigaction(kUnblockedSignals[signal_index],
+                &old_sigactions[signal_index], NULL);
+    }
+    sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);
   }
-  int sigprocmask_status = sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
-  CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail
-  // Make this process dumpable. Processes that are not dumpable cannot be
-  // attached to.
-  int process_was_dumpable = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
-  if (!process_was_dumpable)
-    internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ private:
+  int process_was_dumpable_;
+};
+
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {
+  StopTheWorldScope in_stoptheworld;
   // Prepare the arguments for TracerThread.
   struct TracerThreadArgument tracer_thread_argument;
   tracer_thread_argument.callback = callback;
@@ -349,16 +393,6 @@
     if (internal_iserror(waitpid_status, &wperrno))
       Report("Waiting on the tracer thread failed (errno %d).\n", wperrno);
   }
-  // Restore the dumpable flag.
-  if (!process_was_dumpable)
-    internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
-  // Restore the signal handlers.
-  for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
-       signal_index++) {
-    sigaction(kUnblockedSignals[signal_index],
-              &old_sigactions[signal_index], NULL);
-  }
-  sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);
 }
 
 // Platform-specific methods from SuspendedThreadsList.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D1515.3.patch
Type: text/x-patch
Size: 8086 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20130826/10c14d12/attachment.bin>


More information about the llvm-commits mailing list