<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Mar 5, 2015 at 6:37 AM, Dmitry Vyukov <span dir="ltr"><<a href="mailto:dvyukov@google.com" target="_blank">dvyukov@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Author: dvyukov<br>
Date: Thu Mar  5 08:37:28 2015<br>
New Revision: 231367<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=231367&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=231367&view=rev</a><br>
Log:<br>
tsan: fix signal handling during stop-the-world<br>
<br>
Long story short: stop-the-world briefly resets SIGSEGV handler to SIG_DFL.<br>
This breaks programs that handle and continue after SIGSEGV (namely JVM).<br>
See the test and comments for details.<br>
<br>
This is reincarnation of reverted r229678 (<a href="http://reviews.llvm.org/D7722" target="_blank">http://reviews.llvm.org/D7722</a>).<br>
Changed:<br>
- execute TracerThreadDieCallback only on tracer thread<br>
- reset global data in TracerThreadSignalHandler/TracerThreadDieCallback<br>
- handle EINTR from waitpid<br>
<br>
Add 3 new test:<br>
- SIGSEGV during leak checking<br>
- StopTheWorld operation during signal storm from an external process<br>
- StopTheWorld operation when the program generates and handles SIGSEGVs<br>
<br>
<a href="http://reviews.llvm.org/D8032" target="_blank">http://reviews.llvm.org/D8032</a><br>
<br>
<br>
Added:<br>
    compiler-rt/trunk/test/asan/TestCases/Linux/leak_check_segv.cc<br>
    compiler-rt/trunk/test/asan/TestCases/Linux/signal_during_stop_the_world.cc<br>
    compiler-rt/trunk/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc<br></blockquote><div><br>This test appears to be flaky. Failing every second run here: <a href="http://lab.llvm.org:8011/builders/clang-x86_64-linux-selfhost-abi-test/builds/2871">http://lab.llvm.org:8011/builders/clang-x86_64-linux-selfhost-abi-test/builds/2871</a> and failing for me locally.<br> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Modified:<br>
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld.h<br>
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc<br>
<br>
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld.h?rev=231367&r1=231366&r2=231367&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld.h?rev=231367&r1=231366&r2=231367&view=diff</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld.h (original)<br>
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld.h Thu Mar  5 08:37:28 2015<br>
@@ -59,7 +59,8 @@ typedef void (*StopTheWorldCallback)(<br>
<br>
 // Suspend all threads in the current process and run the callback on the list<br>
 // of suspended threads. This function will resume the threads before returning.<br>
-// The callback should not call any libc functions.<br>
+// The callback should not call any libc functions. The callback must not call<br>
+// exit nor _exit and instead return to the caller.<br>
 // This function should NOT be called from multiple threads simultaneously.<br>
 void StopTheWorld(StopTheWorldCallback callback, void *argument);<br>
<br>
<br>
Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc?rev=231367&r1=231366&r2=231367&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc?rev=231367&r1=231366&r2=231367&view=diff</a><br>
==============================================================================<br>
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc (original)<br>
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc Thu Mar  5 08:37:28 2015<br>
@@ -19,6 +19,7 @@<br>
 #include "sanitizer_stoptheworld.h"<br>
<br>
 #include "sanitizer_platform_limits_posix.h"<br>
+#include "sanitizer_atomic.h"<br>
<br>
 #include <errno.h><br>
 #include <sched.h> // for CLONE_* definitions<br>
@@ -70,11 +71,25 @@<br>
 COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));<br>
<br>
 namespace __sanitizer {<br>
+<br>
+// Structure for passing arguments into the tracer thread.<br>
+struct TracerThreadArgument {<br>
+  StopTheWorldCallback callback;<br>
+  void *callback_argument;<br>
+  // The tracer thread waits on this mutex while the parent finishes its<br>
+  // preparations.<br>
+  BlockingMutex mutex;<br>
+  // Tracer thread signals its completion by setting done.<br>
+  atomic_uintptr_t done;<br>
+  uptr parent_pid;<br>
+};<br>
+<br>
 // This class handles thread suspending/unsuspending in the tracer thread.<br>
 class ThreadSuspender {<br>
  public:<br>
-  explicit ThreadSuspender(pid_t pid)<br>
-    : pid_(pid) {<br>
+  explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)<br>
+    : arg(arg)<br>
+    , pid_(pid) {<br>
       CHECK_GE(pid, 0);<br>
     }<br>
   bool SuspendAllThreads();<br>
@@ -83,6 +98,7 @@ class ThreadSuspender {<br>
   SuspendedThreadsList &suspended_threads_list() {<br>
     return suspended_threads_list_;<br>
   }<br>
+  TracerThreadArgument *arg;<br>
  private:<br>
   SuspendedThreadsList suspended_threads_list_;<br>
   pid_t pid_;<br>
@@ -184,33 +200,27 @@ bool ThreadSuspender::SuspendAllThreads(<br>
 // Pointer to the ThreadSuspender instance for use in signal handler.<br>
 static ThreadSuspender *thread_suspender_instance = NULL;<br>
<br>
-// Signals that should not be blocked (this is used in the parent thread as well<br>
-// as the tracer thread).<br>
-static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV,<br>
-                                         SIGBUS, SIGXCPU, SIGXFSZ };<br>
-<br>
-// Structure for passing arguments into the tracer thread.<br>
-struct TracerThreadArgument {<br>
-  StopTheWorldCallback callback;<br>
-  void *callback_argument;<br>
-  // The tracer thread waits on this mutex while the parent finishes its<br>
-  // preparations.<br>
-  BlockingMutex mutex;<br>
-  uptr parent_pid;<br>
-};<br>
+// Synchronous signals that should not be blocked.<br>
+static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,<br>
+                                    SIGXCPU, SIGXFSZ };<br>
<br>
 static DieCallbackType old_die_callback;<br>
<br>
 // Signal handler to wake up suspended threads when the tracer thread dies.<br>
-void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {<br>
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {<br>
   SignalContext ctx = SignalContext::Create(siginfo, uctx);<br>
   VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",<br>
       signum, ctx.addr, ctx.pc, ctx.sp);<br>
-  if (thread_suspender_instance != NULL) {<br>
+  ThreadSuspender *inst = thread_suspender_instance;<br>
+  if (inst != NULL) {<br>
     if (signum == SIGABRT)<br>
-      thread_suspender_instance->KillAllThreads();<br>
+      inst->KillAllThreads();<br>
     else<br>
-      thread_suspender_instance->ResumeAllThreads();<br>
+      inst->ResumeAllThreads();<br>
+    SetDieCallback(old_die_callback);<br>
+    old_die_callback = NULL;<br>
+    thread_suspender_instance = NULL;<br>
+    atomic_store(&inst->arg->done, 1, memory_order_relaxed);<br>
   }<br>
   internal__exit((signum == SIGABRT) ? 1 : 2);<br>
 }<br>
@@ -222,10 +232,15 @@ static void TracerThreadDieCallback() {<br>
   // point. So we correctly handle calls to Die() from within the callback, but<br>
   // not those that happen before or after the callback. Hopefully there aren't<br>
   // a lot of opportunities for that to happen...<br>
-  if (thread_suspender_instance)<br>
-    thread_suspender_instance->KillAllThreads();<br>
+  ThreadSuspender *inst = thread_suspender_instance;<br>
+  if (inst != NULL && stoptheworld_tracer_pid == internal_getpid()) {<br>
+    inst->KillAllThreads();<br>
+    thread_suspender_instance = NULL;<br>
+  }<br>
   if (old_die_callback)<br>
     old_die_callback();<br>
+  SetDieCallback(old_die_callback);<br>
+  old_die_callback = NULL;<br>
 }<br>
<br>
 // Size of alternative stack for signal handlers in the tracer thread.<br>
@@ -245,9 +260,10 @@ static int TracerThread(void* argument)<br>
   tracer_thread_argument->mutex.Lock();<br>
   tracer_thread_argument->mutex.Unlock();<br>
<br>
+  old_die_callback = GetDieCallback();<br>
   SetDieCallback(TracerThreadDieCallback);<br>
<br>
-  ThreadSuspender thread_suspender(internal_getppid());<br>
+  ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);<br>
   // Global pointer for the signal handler.<br>
   thread_suspender_instance = &thread_suspender;<br>
<br>
@@ -259,17 +275,14 @@ static int TracerThread(void* argument)<br>
   handler_stack.ss_size = kHandlerStackSize;<br>
   internal_sigaltstack(&handler_stack, NULL);<br>
<br>
-  // Install our handler for fatal signals. Other signals should be blocked by<br>
-  // the mask we inherited from the caller thread.<br>
-  for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);<br>
-       signal_index++) {<br>
-    __sanitizer_sigaction new_sigaction;<br>
-    internal_memset(&new_sigaction, 0, sizeof(new_sigaction));<br>
-    new_sigaction.sigaction = TracerThreadSignalHandler;<br>
-    new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO;<br>
-    internal_sigfillset(&new_sigaction.sa_mask);<br>
-    internal_sigaction_norestorer(kUnblockedSignals[signal_index],<br>
-                                  &new_sigaction, NULL);<br>
+  // Install our handler for synchronous signals. Other signals should be<br>
+  // blocked by the mask we inherited from the parent thread.<br>
+  for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {<br>
+    __sanitizer_sigaction act;<br>
+    internal_memset(&act, 0, sizeof(act));<br>
+    act.sigaction = TracerThreadSignalHandler;<br>
+    act.sa_flags = SA_ONSTACK | SA_SIGINFO;<br>
+    internal_sigaction_norestorer(kSyncSignals[i], &act, 0);<br>
   }<br>
<br>
   int exit_code = 0;<br>
@@ -282,9 +295,9 @@ static int TracerThread(void* argument)<br>
     thread_suspender.ResumeAllThreads();<br>
     exit_code = 0;<br>
   }<br>
+  SetDieCallback(old_die_callback);<br>
   thread_suspender_instance = NULL;<br>
-  handler_stack.ss_flags = SS_DISABLE;<br>
-  internal_sigaltstack(&handler_stack, NULL);<br>
+  atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);<br>
   return exit_code;<br>
 }<br>
<br>
@@ -316,53 +329,21 @@ class ScopedStackSpaceWithGuard {<br>
 // into globals.<br>
 static __sanitizer_sigset_t blocked_sigset;<br>
 static __sanitizer_sigset_t old_sigset;<br>
-static __sanitizer_sigaction old_sigactions<br>
-    [ARRAY_SIZE(kUnblockedSignals)];<br>
<br>
 class StopTheWorldScope {<br>
  public:<br>
   StopTheWorldScope() {<br>
-    // Block all signals that can be blocked safely, and install<br>
-    // default handlers for the remaining signals.<br>
-    // We cannot allow user-defined handlers to run while the ThreadSuspender<br>
-    // thread is active, because they could conceivably call some libc functions<br>
-    // which modify errno (which is shared between the two threads).<br>
-    internal_sigfillset(&blocked_sigset);<br>
-    for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);<br>
-         signal_index++) {<br>
-      // Remove the signal from the set of blocked signals.<br>
-      internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);<br>
-      // Install the default handler.<br>
-      __sanitizer_sigaction new_sigaction;<br>
-      internal_memset(&new_sigaction, 0, sizeof(new_sigaction));<br>
-      new_sigaction.handler = SIG_DFL;<br>
-      internal_sigfillset(&new_sigaction.sa_mask);<br>
-      internal_sigaction_norestorer(kUnblockedSignals[signal_index],<br>
-          &new_sigaction, &old_sigactions[signal_index]);<br>
-    }<br>
-    int sigprocmask_status =<br>
-        internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);<br>
-    CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail<br>
     // Make this process dumpable. Processes that are not dumpable cannot be<br>
     // attached to.<br>
     process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);<br>
     if (!process_was_dumpable_)<br>
       internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);<br>
-    old_die_callback = GetDieCallback();<br>
   }<br>
<br>
   ~StopTheWorldScope() {<br>
-    SetDieCallback(old_die_callback);<br>
     // Restore the dumpable flag.<br>
     if (!process_was_dumpable_)<br>
       internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);<br>
-    // Restore the signal handlers.<br>
-    for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);<br>
-         signal_index++) {<br>
-      internal_sigaction_norestorer(kUnblockedSignals[signal_index],<br>
-                                    &old_sigactions[signal_index], NULL);<br>
-    }<br>
-    internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);<br>
   }<br>
<br>
  private:<br>
@@ -390,16 +371,42 @@ void StopTheWorld(StopTheWorldCallback c<br>
   tracer_thread_argument.callback = callback;<br>
   tracer_thread_argument.callback_argument = argument;<br>
   tracer_thread_argument.parent_pid = internal_getpid();<br>
+  atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);<br>
   const uptr kTracerStackSize = 2 * 1024 * 1024;<br>
   ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);<br>
   // Block the execution of TracerThread until after we have set ptrace<br>
   // permissions.<br>
   tracer_thread_argument.mutex.Lock();<br>
+  // Signal handling story.<br>
+  // We don't want async signals to be delivered to the tracer thread,<br>
+  // so we block all async signals before creating the thread. An async signal<br>
+  // handler can temporary modify errno, which is shared with this thread.<br>
+  // We ought to use pthread_sigmask here, because sigprocmask has undefined<br>
+  // behavior in multithreaded programs. However, on linux sigprocmask is<br>
+  // equivalent to pthread_sigmask with the exception that pthread_sigmask<br>
+  // does not allow to block some signals used internally in pthread<br>
+  // implementation. We are fine with blocking them here, we are really not<br>
+  // going to pthread_cancel the thread.<br>
+  // The tracer thread should not raise any synchronous signals. But in case it<br>
+  // does, we setup a special handler for sync signals that properly kills the<br>
+  // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers<br>
+  // in the tracer thread won't interfere with user program. Double note: if a<br>
+  // user does something along the lines of 'kill -11 pid', that can kill the<br>
+  // process even if user setup own handler for SEGV.<br>
+  // Thing to watch out for: this code should not change behavior of user code<br>
+  // in any observable way. In particular it should not override user signal<br>
+  // handlers.<br>
+  internal_sigfillset(&blocked_sigset);<br>
+  for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)<br>
+    internal_sigdelset(&blocked_sigset, kSyncSignals[i]);<br>
+  int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);<br>
+  CHECK_EQ(rv, 0);<br>
   uptr tracer_pid = internal_clone(<br>
       TracerThread, tracer_stack.Bottom(),<br>
       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,<br>
       &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0<br>
       /* child_tidptr */);<br>
+  internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);<br>
   int local_errno = 0;<br>
   if (internal_iserror(tracer_pid, &local_errno)) {<br>
     VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);<br>
@@ -413,14 +420,27 @@ void StopTheWorld(StopTheWorldCallback c<br>
 #endif<br>
     // Allow the tracer thread to start.<br>
     tracer_thread_argument.mutex.Unlock();<br>
-    // Since errno is shared between this thread and the tracer thread, we<br>
-    // must avoid using errno while the tracer thread is running.<br>
-    // At this point, any signal will either be blocked or kill us, so waitpid<br>
-    // should never return (and set errno) while the tracer thread is alive.<br>
-    uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);<br>
-    if (internal_iserror(waitpid_status, &local_errno))<br>
+    // NOTE: errno is shared between this thread and the tracer thread.<br>
+    // internal_waitpid may call syscall() which can access/spoil errno,<br>
+    // so we can't call it now. Instead we for the tracer thread to finish using<br>
+    // the spin loop below. Man page for sched_yield says "In the Linux<br>
+    // implementation, sched_yield() always succeeds", so let's hope it does not<br>
+    // spoil errno. Note that this spin loop runs only for brief periods before<br>
+    // the tracer thread has suspended us and when it starts unblocking threads.<br>
+    while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)<br>
+      sched_yield();<br>
+    // Now the tracer thread is about to exit and does not touch errno,<br>
+    // wait for it.<br>
+    for (;;) {<br>
+      uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);<br>
+      if (!internal_iserror(waitpid_status, &local_errno))<br>
+        break;<br>
+      if (local_errno == EINTR)<br>
+        continue;<br>
       VReport(1, "Waiting on the tracer thread failed (errno %d).\n",<br>
               local_errno);<br>
+      break;<br>
+    }<br>
   }<br>
 }<br>
<br>
<br>
Added: compiler-rt/trunk/test/asan/TestCases/Linux/leak_check_segv.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/leak_check_segv.cc?rev=231367&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/leak_check_segv.cc?rev=231367&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/test/asan/TestCases/Linux/leak_check_segv.cc (added)<br>
+++ compiler-rt/trunk/test/asan/TestCases/Linux/leak_check_segv.cc Thu Mar  5 08:37:28 2015<br>
@@ -0,0 +1,23 @@<br>
+// Test that SIGSEGV during leak checking does not crash the process.<br>
+// RUN: %clangxx_asan -O1 %s -o %t && LSAN_OPTIONS="verbosity=1" not %run %t 2>&1<br>
+// REQUIRES: asan-64-bits<br>
+#include <stdlib.h><br>
+#include <stdio.h><br>
+#include <sys/mman.h><br>
+#include <sanitizer/lsan_interface.h><br>
+<br>
+char data[10 * 1024 * 1024];<br>
+<br>
+int main() {<br>
+  void *p = malloc(10 * 1024 * 1024);<br>
+  // surprise-surprise!<br>
+  mprotect((void*)(((unsigned long)p + 4095) & ~4095), 16 * 1024, PROT_NONE);<br>
+  mprotect((void*)(((unsigned long)data + 4095) & ~4095), 16 * 1024, PROT_NONE);<br>
+  __lsan_do_leak_check();<br>
+  fprintf(stderr, "DONE\n");<br>
+}<br>
+<br>
+// CHECK: Tracer caught signal 11<br>
+// CHECK: LeakSanitizer has encountered a fatal error<br>
+// CHECK-NOT: DONE<br>
+<br>
<br>
Added: compiler-rt/trunk/test/asan/TestCases/Linux/signal_during_stop_the_world.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/signal_during_stop_the_world.cc?rev=231367&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/asan/TestCases/Linux/signal_during_stop_the_world.cc?rev=231367&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/test/asan/TestCases/Linux/signal_during_stop_the_world.cc (added)<br>
+++ compiler-rt/trunk/test/asan/TestCases/Linux/signal_during_stop_the_world.cc Thu Mar  5 08:37:28 2015<br>
@@ -0,0 +1,60 @@<br>
+// Test StopTheWorld behavior during signal storm.<br>
+// Historically StopTheWorld crashed because did not handle EINTR properly.<br>
+// The test is somewhat convoluted, but that's what caused crashes previously.<br>
+<br>
+// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s<br>
+<br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <signal.h><br>
+#include <unistd.h><br>
+#include <sys/types.h><br>
+#include <sys/prctl.h><br>
+#include <sys/wait.h><br>
+#include <time.h><br>
+#include <pthread.h><br>
+#include <sanitizer/lsan_interface.h><br>
+<br>
+static void handler(int signo);<br>
+static void *thr(void *arg);<br>
+<br>
+int main() {<br>
+  struct sigaction act = {};<br>
+  act.sa_handler = handler;<br>
+  sigaction(SIGPROF, &act, 0);<br>
+<br>
+  pid_t pid = fork();<br>
+  if (pid < 0) {<br>
+    fprintf(stderr, "failed to fork\n");<br>
+    exit(1);<br>
+  }<br>
+  if (pid == 0) {<br>
+    // Child constantly sends signals to parent to cause spurious return from<br>
+    // waitpid in StopTheWorld.<br>
+    prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);<br>
+    pid_t parent = getppid();<br>
+    for (;;) {<br>
+      // There is no strong reason for these two particular signals,<br>
+      // but at least one of them ought to unblock waitpid.<br>
+      kill(parent, SIGCHLD);<br>
+      kill(parent, SIGPROF);<br>
+    }<br>
+  }<br>
+  usleep(10000);  // Let the child start.<br>
+  __lsan_do_leak_check();<br>
+  // Kill and join the child.<br>
+  kill(pid, SIGTERM);<br>
+  waitpid(pid, 0, 0);<br>
+  sleep(1);  // If the tracer thread still runs, give it time to crash.<br>
+  fprintf(stderr, "DONE\n");<br>
+// CHECK: DONE<br>
+}<br>
+<br>
+static void handler(int signo) {<br>
+}<br>
+<br>
+static void *thr(void *arg) {<br>
+  for (;;)<br>
+    sleep(1);<br>
+  return 0;<br>
+}<br>
<br>
Added: compiler-rt/trunk/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc?rev=231367&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc?rev=231367&view=auto</a><br>
==============================================================================<br>
--- compiler-rt/trunk/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc (added)<br>
+++ compiler-rt/trunk/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc Thu Mar  5 08:37:28 2015<br>
@@ -0,0 +1,41 @@<br>
+// RUN: %clangxx -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" ASAN_OPTIONS="handle_segv=0 allow_user_segv_handler=1" %run %t 2>&1 | FileCheck %s<br>
+<br>
+// JVM uses SEGV to preempt threads. All threads do a load from a known address<br>
+// periodically. When runtime needs to preempt threads, it unmaps the page.<br>
+// Threads start triggering SEGV one by one. The signal handler blocks<br>
+// threads while runtime does its thing. Then runtime maps the page again<br>
+// and resumes the threads.<br>
+// Previously this pattern conflicted with stop-the-world machinery,<br>
+// because it briefly reset SEGV handler to SIG_DFL.<br>
+// As the consequence JVM just silently died.<br>
+<br>
+// This test sets memory flushing rate to maximum, then does series of<br>
+// "benign" SEGVs that are handled by signal handler, and ensures that<br>
+// the process survive.<br>
+<br>
+#include <stdio.h><br>
+#include <stdlib.h><br>
+#include <signal.h><br>
+#include <sys/mman.h><br>
+<br>
+void *guard;<br>
+<br>
+void handler(int signo, siginfo_t *info, void *uctx) {<br>
+  mprotect(guard, 4096, PROT_READ | PROT_WRITE);<br>
+}<br>
+<br>
+int main() {<br>
+  struct sigaction a, old;<br>
+  a.sa_sigaction = handler;<br>
+  a.sa_flags = SA_SIGINFO;<br>
+  sigaction(SIGSEGV, &a, &old);<br>
+  guard = mmap(0, 4096, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);<br>
+  for (int i = 0; i < 1000000; i++) {<br>
+    mprotect(guard, 4096, PROT_NONE);<br>
+    *(int*)guard = 1;<br>
+  }<br>
+  sigaction(SIGSEGV, &old, 0);<br>
+  fprintf(stderr, "DONE\n");<br>
+}<br>
+<br>
+// CHECK: DONE<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu" target="_blank">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div></div>