Register custom stack for signal handlers
authorAndrii Grynenko <andrii@fb.com>
Thu, 12 May 2016 23:20:40 +0000 (16:20 -0700)
committerFacebook Github Bot 1 <facebook-github-bot-1-bot@fb.com>
Thu, 12 May 2016 23:23:38 +0000 (16:23 -0700)
Summary: This updates FiberManager to automatically register custom stack for signal handlers. This ensures signal handlers will work correctly not only in case of stack overflow, but also if fiber stack size is just not enough for them.

Reviewed By: yfeldblum

Differential Revision: D3289912

fbshipit-source-id: 44959b727f70e1f1748d837b1a06c7414433e5ec

folly/experimental/fibers/FiberManager-inl.h
folly/experimental/fibers/FiberManager.cpp
folly/experimental/fibers/FiberManager.h
folly/experimental/fibers/test/StackOverflow.cpp [new file with mode: 0644]

index 52c283f58ef303d166b768975444f0c082809af6..697f14bab59c4c263aa50c30ffe3e8a83a8da4ce 100644 (file)
@@ -164,6 +164,10 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) {
 }
 
 inline bool FiberManager::loopUntilNoReady() {
+  if (UNLIKELY(!alternateSignalStackRegistered_)) {
+    registerAlternateSignalStack();
+  }
+
   // Support nested FiberManagers
   auto originalFiberManager = this;
   std::swap(currentFiberManager_, originalFiberManager);
index 7723953761fd7b91b5e7255cab10fd15e2ea86e6..2592ee1c97ff4c60c2db47821215b87cd8fb4f0b 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include "FiberManager.h"
 
+#include <signal.h>
 #include <sys/syscall.h>
 #include <unistd.h>
 
@@ -26,6 +27,8 @@
 #include <folly/experimental/fibers/Fiber.h>
 #include <folly/experimental/fibers/LoopController.h>
 
+#include <folly/SingletonThreadLocal.h>
+
 #ifdef FOLLY_SANITIZE_ADDRESS
 
 #include <dlfcn.h>
@@ -273,5 +276,62 @@ static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
 }
 
 #endif // FOLLY_SANITIZE_ADDRESS
+
+namespace {
+
+// SIGSTKSZ (8 kB on our architectures) isn't always enough for
+// folly::symbolizer, so allocate 32 kB.
+constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
+
+bool hasAlternateStack() {
+  stack_t ss;
+  sigaltstack(nullptr, &ss);
+  return !(ss.ss_flags & SS_DISABLE);
+}
+
+int setAlternateStack(char* sp, size_t size) {
+  CHECK(sp);
+  stack_t ss{};
+  ss.ss_sp = sp;
+  ss.ss_size = size;
+  return sigaltstack(&ss, nullptr);
+}
+
+int unsetAlternateStack() {
+  stack_t ss{};
+  ss.ss_flags = SS_DISABLE;
+  return sigaltstack(&ss, nullptr);
+}
+
+class ScopedAlternateSignalStack {
+ public:
+  ScopedAlternateSignalStack() {
+    if (hasAlternateStack()) {
+      return;
+    }
+
+    stack_ = folly::make_unique<AltStackBuffer>();
+
+    setAlternateStack(stack_->data(), stack_->size());
+  }
+
+  ~ScopedAlternateSignalStack() {
+    if (stack_) {
+      unsetAlternateStack();
+    }
+  }
+
+ private:
+  using AltStackBuffer = std::array<char, kAltStackSize>;
+  std::unique_ptr<AltStackBuffer> stack_;
+};
+}
+
+void FiberManager::registerAlternateSignalStack() {
+  static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
+  singleton.get();
+
+  alternateSignalStackRegistered_ = true;
+}
 }
 }
index 405b2d199118a93622451b94acc50a91cba1c60b..aa250a74a64622d59b2dd4d65a15254856d6b8f8 100644 (file)
@@ -467,6 +467,10 @@ class FiberManager : public ::folly::Executor {
   void unpoisonFiberStack(const Fiber* fiber);
 
 #endif // FOLLY_SANITIZE_ADDRESS
+
+  bool alternateSignalStackRegistered_{false};
+
+  void registerAlternateSignalStack();
 };
 
 /**
diff --git a/folly/experimental/fibers/test/StackOverflow.cpp b/folly/experimental/fibers/test/StackOverflow.cpp
new file mode 100644 (file)
index 0000000..d8462e1
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <folly/experimental/fibers/FiberManagerMap.h>
+#include <folly/experimental/symbolizer/SignalHandler.h>
+#include <folly/init/Init.h>
+
+void f(int* p) {
+  // Make sure recursion is not optimized out
+  int a[100];
+  for (size_t i = 0; i < 100; ++i) {
+    a[i] = i;
+    ++(a[i]);
+    if (p) {
+      a[i] += p[i];
+    }
+  }
+  f(a);
+}
+
+int main(int argc, char* argv[]) {
+  folly::init(&argc, &argv);
+
+  folly::EventBase evb;
+  folly::fibers::getFiberManager(evb).addTask([&]() { f(nullptr); });
+  evb.loop();
+}