Harden failure signal handler in the face of memory corruptions
[folly.git] / folly / experimental / symbolizer / SignalHandler.cpp
index f66d013373aa47cda6a9d42c442ff29c39609d73..02e5ef05219c58fa43d6c7fb3f8c81f40c63fa47 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,30 +44,28 @@ namespace {
  */
 class FatalSignalCallbackRegistry {
  public:
-  typedef std::function<void()> Func;
-
   FatalSignalCallbackRegistry();
 
-  void add(Func func);
+  void add(SignalCallback func);
   void markInstalled();
   void run();
 
  private:
   std::atomic<bool> installed_;
   std::mutex mutex_;
-  std::vector<Func> handlers_;
+  std::vector<SignalCallback> handlers_;
 };
 
 FatalSignalCallbackRegistry::FatalSignalCallbackRegistry()
   : installed_(false) {
 }
 
-void FatalSignalCallbackRegistry::add(Func func) {
+void FatalSignalCallbackRegistry::add(SignalCallback func) {
   std::lock_guard<std::mutex> lock(mutex_);
   CHECK(!installed_)
     << "FatalSignalCallbackRegistry::add may not be used "
        "after installing the signal handlers.";
-  handlers_.push_back(std::move(func));
+  handlers_.push_back(func);
 }
 
 void FatalSignalCallbackRegistry::markInstalled() {
@@ -79,7 +77,7 @@ void FatalSignalCallbackRegistry::markInstalled() {
 
 void FatalSignalCallbackRegistry::run() {
   if (!installed_) {
-    return;  // Shouldn't happen
+    return;
   }
 
   for (auto& fn : handlers_) {
@@ -112,6 +110,7 @@ void callPreviousSignalHandler(int signum) {
   for (auto p = kFatalSignals; p->name; ++p) {
     if (p->number == signum) {
       sigaction(signum, &p->oldAction, nullptr);
+      raise(signum);
       return;
     }
   }
@@ -189,36 +188,71 @@ void dumpSignalInfo(int signum, siginfo_t* siginfo) {
   print("), stack trace: ***\n");
 }
 
-void dumpStackTrace() {
+namespace {
+constexpr size_t kDefaultCapacity = 500;
+
+// Note: not thread-safe, but that's okay, as we only let one thread
+// in our signal handler at a time.
+SignalSafeElfCache signalSafeElfCache(kDefaultCapacity);
+}  // namespace
+
+void dumpStackTrace(bool symbolize) __attribute__((noinline));
+
+void dumpStackTrace(bool symbolize) {
   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
   // Get and symbolize stack trace
   constexpr size_t kMaxStackTraceDepth = 100;
-  AddressInfo addresses[kMaxStackTraceDepth];
+  FrameArray<kMaxStackTraceDepth> addresses;
 
   // Skip the getStackTrace frame
-  ssize_t stackTraceDepth = getStackTrace(addresses, kMaxStackTraceDepth, 1);
-  if (stackTraceDepth < 0) {
+  if (!getStackTraceSafe(addresses)) {
     print("(error retrieving stack trace)\n");
+  } else if (symbolize) {
+    Symbolizer symbolizer(&signalSafeElfCache);
+    symbolizer.symbolize(addresses);
+
+    FDSymbolizePrinter printer(STDERR_FILENO, SymbolizePrinter::COLOR_IF_TTY);
+
+    // Skip the top 2 frames:
+    // getStackTraceSafe
+    // dumpStackTrace (here)
+    //
+    // Leaving signalHandler on the stack for clarity, I think.
+    printer.println(addresses, 2);
   } else {
-    Symbolizer symbolizer;
-    symbolizer.symbolize(addresses, stackTraceDepth);
-
-    FDSymbolizePrinter printer(STDERR_FILENO);
-    printer.print(addresses, stackTraceDepth);
+    print("(safe mode, symbolizer not available)\n");
+    AddressFormatter formatter;
+    for (ssize_t i = 0; i < addresses.frameCount; ++i) {
+      print(formatter.format(addresses.addresses[i]));
+      print("\n");
+    }
   }
 }
 
-std::atomic<pthread_t*> gSignalThread;
+// On Linux, pthread_t is a pointer, so 0 is an invalid value, which we
+// take to indicate "no thread in the signal handler".
+//
+// POSIX defines PTHREAD_NULL for this purpose, but that's not available.
+constexpr pthread_t kInvalidThreadId = 0;
+
+std::atomic<pthread_t> gSignalThread(kInvalidThreadId);
+std::atomic<bool> gInRecursiveSignalHandler(false);
 
 // Here be dragons.
 void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
   // First, let's only let one thread in here at a time.
   pthread_t myId = pthread_self();
 
-  pthread_t* prevSignalThread = nullptr;
-  while (!gSignalThread.compare_exchange_strong(prevSignalThread, &myId)) {
-    if (pthread_equal(*prevSignalThread, myId)) {
-      print("Entered fatal signal handler recursively. We're in trouble.\n");
+  pthread_t prevSignalThread = kInvalidThreadId;
+  while (!gSignalThread.compare_exchange_strong(prevSignalThread, myId)) {
+    if (pthread_equal(prevSignalThread, myId)) {
+      // First time here. Try to dump the stack trace without symbolization.
+      // If we still fail, well, we're mightily screwed, so we do nothing the
+      // next time around.
+      if (!gInRecursiveSignalHandler.exchange(true)) {
+        print("Entered fatal signal handler recursively. We're in trouble.\n");
+        dumpStackTrace(false);  // no symbolization
+      }
       return;
     }
 
@@ -228,12 +262,12 @@ void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
     ts.tv_nsec = 100L * 1000 * 1000;  // 100ms
     nanosleep(&ts, nullptr);
 
-    prevSignalThread = nullptr;
+    prevSignalThread = kInvalidThreadId;
   }
 
   dumpTimeInfo();
   dumpSignalInfo(signum, info);
-  dumpStackTrace();
+  dumpStackTrace(true);  // with symbolization
 
   // Run user callbacks
   gFatalSignalCallbackRegistry->run();
@@ -241,22 +275,21 @@ void innerSignalHandler(int signum, siginfo_t* info, void* uctx) {
 
 void signalHandler(int signum, siginfo_t* info, void* uctx) {
   SCOPE_EXIT { fsyncNoInt(STDERR_FILENO); };
-  try {
-    innerSignalHandler(signum, info, uctx);
-  } catch (...) {
-    // Ignore any exceptions. What? Exceptions?
-    print("Exception in innerSignalHandler!\n");
-  }
+  innerSignalHandler(signum, info, uctx);
 
-  gSignalThread = nullptr;
+  gSignalThread = kInvalidThreadId;
   // Kill ourselves with the previous handler.
   callPreviousSignalHandler(signum);
 }
 
 }  // namespace
 
-void addFatalSignalCallback(std::function<void()> handler) {
-  gFatalSignalCallbackRegistry->add(std::move(handler));
+void addFatalSignalCallback(SignalCallback cb) {
+  gFatalSignalCallbackRegistry->add(cb);
+}
+
+void installFatalSignalCallbacks() {
+  gFatalSignalCallbackRegistry->markInstalled();
 }
 
 namespace {
@@ -271,8 +304,6 @@ void installFatalSignalHandler() {
     return;
   }
 
-  gFatalSignalCallbackRegistry->markInstalled();
-
   struct sigaction sa;
   memset(&sa, 0, sizeof(sa));
   sigemptyset(&sa.sa_mask);
@@ -285,4 +316,3 @@ void installFatalSignalHandler() {
 }
 
 }}  // namespaces
-