Introduce the use_sigaltstack flag (off by default), which enables using alternate
per-thread stacks for signal handling. This allows to print more verbose error reports
for stack overflows.



git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@154092 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 6f03d67..39d3adf 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -181,6 +181,8 @@
 int AsanClose(int fd);
 
 bool AsanInterceptsSignal(int signum);
+void SetAlternateSignalStack();
+void UnsetAlternateSignalStack();
 void InstallSignalHandlers();
 int GetPid();
 uintptr_t GetThreadSelf();
@@ -249,6 +251,7 @@
 extern bool   FLAG_allow_user_poisoning;
 extern int    FLAG_sleep_before_dying;
 extern bool   FLAG_handle_segv;
+extern bool   FLAG_use_sigaltstack;
 
 extern int asan_inited;
 // Used to avoid infinite recursion in __asan_init().
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index 15bdd93..0ab09e6 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -34,6 +34,8 @@
 // since most of the stuff here is inlinable.
 #include <algorithm>
 
+static const size_t kAltStackSize = SIGSTKSZ * 4;  // SIGSTKSZ is not enough.
+
 namespace __asan {
 
 static void MaybeInstallSigaction(int signum,
@@ -44,6 +46,7 @@
   REAL(memset)(&sigact, 0, sizeof(sigact));
   sigact.sa_sigaction = handler;
   sigact.sa_flags = SA_SIGINFO;
+  if (FLAG_use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
   CHECK(0 == REAL(sigaction)(signum, &sigact, 0));
 }
 
@@ -63,7 +66,40 @@
   ShowStatsAndAbort();
 }
 
+void SetAlternateSignalStack() {
+  stack_t altstack, oldstack;
+  CHECK(0 == sigaltstack(NULL, &oldstack));
+  // If the alternate stack is already in place, do nothing.
+  if ((oldstack.ss_flags & SS_DISABLE) == 0) return;
+  // TODO(glider): the mapped stack should have the MAP_STACK flag in the
+  // future. It is not required by man 2 sigaltstack now (they're using
+  // malloc()).
+  void* base = AsanMmapSomewhereOrDie(kAltStackSize, __FUNCTION__);
+  altstack.ss_sp = base;
+  altstack.ss_flags = 0;
+  altstack.ss_size = kAltStackSize;
+  CHECK(0 == sigaltstack(&altstack, NULL));
+  if (FLAG_v > 0) {
+    Report("Alternative stack for T%d set: [%p,%p)\n",
+           asanThreadRegistry().GetCurrentTidOrMinusOne(),
+           altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size);
+  }
+}
+
+void UnsetAlternateSignalStack() {
+  stack_t altstack, oldstack;
+  altstack.ss_sp = NULL;
+  altstack.ss_flags = SS_DISABLE;
+  altstack.ss_size = 0;
+  CHECK(0 == sigaltstack(&altstack, &oldstack));
+  AsanUnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
+}
+
 void InstallSignalHandlers() {
+  // Set the alternate signal stack for the main thread.
+  // This will cause SetAlternateSignalStack to be called twice, but the stack
+  // will be actually set only once.
+  if (FLAG_use_sigaltstack) SetAlternateSignalStack();
   MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV);
   MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV);
 }
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 3587d45..8f69cca 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -39,6 +39,7 @@
 size_t FLAG_malloc_context_size = kMallocContextSize;
 uintptr_t FLAG_large_malloc;
 bool   FLAG_handle_segv;
+bool   FLAG_use_sigaltstack;
 bool   FLAG_replace_str;
 bool   FLAG_replace_intrin;
 bool   FLAG_replace_cfallocator;  // Used on Mac only.
@@ -442,6 +443,7 @@
   FLAG_poison_shadow = IntFlagValue(options, "poison_shadow=", 1);
   FLAG_report_globals = IntFlagValue(options, "report_globals=", 1);
   FLAG_handle_segv = IntFlagValue(options, "handle_segv=", ASAN_NEEDS_SEGV);
+  FLAG_use_sigaltstack = IntFlagValue(options, "use_sigaltstack=", 0);
   FLAG_symbolize = IntFlagValue(options, "symbolize=", 1);
   FLAG_demangle = IntFlagValue(options, "demangle=", 1);
   FLAG_debug = IntFlagValue(options, "debug=", 0);
@@ -470,7 +472,6 @@
   InitializeAsanInterceptors();
 
   ReplaceSystemMalloc();
-  InstallSignalHandlers();
 
   if (FLAG_v) {
     Printf("|| `[%p, %p]` || HighMem    ||\n", kHighMemBeg, kHighMemEnd);
@@ -517,6 +518,8 @@
     AsanDie();
   }
 
+  InstallSignalHandlers();
+
   // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
   // should be set to 1 prior to initializing the threads.
   asan_inited = 1;
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index b6d0965..315db7e 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -82,6 +82,7 @@
 
 thread_return_t AsanThread::ThreadStart() {
   Init();
+  if (FLAG_use_sigaltstack) SetAlternateSignalStack();
 
   if (!start_routine_) {
     // start_routine_ == NULL if we're on the main thread or on one of the
@@ -93,6 +94,7 @@
 
   thread_return_t res = start_routine_(arg_);
   malloc_storage().CommitBack();
+  if (FLAG_use_sigaltstack) UnsetAlternateSignalStack();
 
   this->Destroy();
 
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index b3a770b..94f4d04 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -262,6 +262,14 @@
   return GetCurrentThreadId();
 }
 
+void SetAlternateSignalStack() {
+  // FIXME: Decide what to do on Windows.
+}
+
+void UnsetAlternateSignalStack() {
+  // FIXME: Decide what to do on Windows.
+}
+
 void InstallSignalHandlers() {
   // FIXME: Decide what to do on Windows.
 }