Tell ASAN about fiber stacks
authorAndrew Cox <andrewcox@fb.com>
Thu, 17 Mar 2016 14:09:07 +0000 (07:09 -0700)
committerFacebook Github Bot 1 <facebook-github-bot-1-bot@fb.com>
Thu, 17 Mar 2016 14:20:20 +0000 (07:20 -0700)
Summary:ASAN needs to know about the stack extents. Currently it has no knowledge of
fibers and so it can give false positives, particularly in cases of no-return
(think exceptions).

See: https://github.com/google/sanitizers/issues/189

This change along with a related ASAN diff fixes that, and I've verified it
fixes false positive test failures I'm seeing when throws happen from fibers.

Also rips out some hacks that attempted to work around the limitations of
ASAN these changes should fix.

This change depends on:
D3017630
D2952854
D3017619

And will also depend on rollout of libasan.so to /usr/local/fbcode platform dirs on all machines.

Reviewed By: andriigrynenko

Differential Revision: D2952899

fb-gh-sync-id: 19da779227c6c0f30c5755806325aa4cba364cfe
shipit-source-id: 19da779227c6c0f30c5755806325aa4cba364cfe

folly/experimental/fibers/Fiber.cpp
folly/experimental/fibers/FiberManager-inl.h
folly/experimental/fibers/FiberManager.cpp
folly/experimental/fibers/FiberManager.h

index 47426fda49cd52c5d8bc7da6a5168d01bdfdca81..83e15cc20048f7e49419fff597fd4e852865cd81 100644 (file)
@@ -132,19 +132,8 @@ void Fiber::fiberFuncHelper(intptr_t fiber) {
   reinterpret_cast<Fiber*>(fiber)->fiberFunc();
 }
 
-/*
- * Some weird bug in ASAN causes fiberFunc to allocate boundless amounts of
- * memory inside __asan_handle_no_return.  Work around this in ASAN builds by
- * tricking the compiler into thinking it may, someday, return.
- */
-#ifdef FOLLY_SANITIZE_ADDRESS
-volatile bool loopForever = true;
-#else
-static constexpr bool loopForever = true;
-#endif
-
 void Fiber::fiberFunc() {
-  while (loopForever) {
+  while (true) {
     DCHECK_EQ(state_, NOT_STARTED);
 
     threadId_ = localThreadId();
@@ -176,9 +165,8 @@ void Fiber::fiberFunc() {
 
     state_ = INVALID;
 
-    fiberManager_.activeFiber_ = nullptr;
+    auto context = fiberManager_.deactivateFiber(this);
 
-    auto context = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
     DCHECK_EQ(reinterpret_cast<Fiber*>(context), this);
   }
 }
@@ -191,12 +179,11 @@ intptr_t Fiber::preempt(State state) {
     DCHECK_EQ(state_, RUNNING);
     DCHECK_NE(state, RUNNING);
 
-    fiberManager_.activeFiber_ = nullptr;
     state_ = state;
 
     recordStackPosition();
 
-    ret = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
+    ret = fiberManager_.deactivateFiber(this);
 
     DCHECK_EQ(fiberManager_.activeFiber_, this);
     DCHECK_EQ(state_, READY_TO_RUN);
index 61d4fec0badf8e124ecbb613c628f5ed8a2eb0f7..1e6ed03583734125e8482f66a50066e962ab6dcf 100644 (file)
@@ -58,6 +58,28 @@ inline void FiberManager::ensureLoopScheduled() {
   loopController_->schedule();
 }
 
+inline intptr_t FiberManager::activateFiber(Fiber* fiber) {
+  DCHECK_EQ(activeFiber_, (Fiber*)nullptr);
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+  registerFiberActivationWithAsan(fiber);
+#endif
+
+  activeFiber_ = fiber;
+  return jumpContext(&mainContext_, &fiber->fcontext_, fiber->data_);
+}
+
+inline intptr_t FiberManager::deactivateFiber(Fiber* fiber) {
+  DCHECK_EQ(activeFiber_, fiber);
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+  registerFiberDeactivationWithAsan(fiber);
+#endif
+
+  activeFiber_ = nullptr;
+  return jumpContext(&fiber->fcontext_, &mainContext_, 0);
+}
+
 inline void FiberManager::runReadyFiber(Fiber* fiber) {
   SCOPE_EXIT {
     assert(currentFiber_ == nullptr);
@@ -74,8 +96,7 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) {
 
   while (fiber->state_ == Fiber::NOT_STARTED ||
          fiber->state_ == Fiber::READY_TO_RUN) {
-    activeFiber_ = fiber;
-    jumpContext(&mainContext_, &fiber->fcontext_, fiber->data_);
+    activateFiber(fiber);
     if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {
       try {
         immediateFunc_();
index c60948649d89db5365bc1947668d7283a0a1bdb1..d4dc69447f4e3b21f87301a32fc9fdd69078b42d 100644 (file)
 #include <folly/experimental/fibers/Fiber.h>
 #include <folly/experimental/fibers/LoopController.h>
 
+#ifdef FOLLY_SANITIZE_ADDRESS
+
+#include <dlfcn.h>
+
+static void __asan_enter_fiber_weak(
+    void const* fiber_stack_base,
+    size_t fiber_stack_extent)
+    __attribute__((__weakref__("__asan_enter_fiber")));
+static void __asan_exit_fiber_weak()
+    __attribute__((__weakref__("__asan_exit_fiber")));
+
+typedef void (*AsanEnterFiberFuncPtr)(void const*, size_t);
+typedef void (*AsanExitFiberFuncPtr)();
+
+namespace folly { namespace fibers {
+
+static AsanEnterFiberFuncPtr getEnterFiberFunc();
+static AsanExitFiberFuncPtr getExitFiberFunc();
+
+}}
+
+#endif
+
 namespace folly { namespace fibers {
 
 FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
@@ -88,7 +111,6 @@ Fiber* FiberManager::getFiber() {
   ++fiberId_;
   bool recordStack = (options_.recordStackEvery != 0) &&
                      (fiberId_ % options_.recordStackEvery == 0);
-  fiber->init(recordStack);
   return fiber;
 }
 
@@ -139,7 +161,7 @@ void FiberManager::doFibersPoolResizing() {
   maxFibersActiveLastPeriod_ = fibersActive_;
 }
 
-void FiberManager::FiberManager::FibersPoolResizer::operator()() {
+void FiberManager::FibersPoolResizer::operator()() {
   fiberManager_.doFibersPoolResizing();
   fiberManager_.timeoutManager_->registerTimeout(
       *this,
@@ -147,4 +169,70 @@ void FiberManager::FiberManager::FibersPoolResizer::operator()() {
         fiberManager_.options_.fibersPoolResizePeriodMs));
 }
 
+#ifdef FOLLY_SANITIZE_ADDRESS
+
+void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
+  auto context = &fiber->fcontext_;
+  void* top = context->stackBase();
+  void* bottom = context->stackLimit();
+  size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
+
+  // Check if we can find a fiber enter function and call it if we find one
+  static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
+  if (fn == nullptr) {
+    LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
+  } else {
+    fn(bottom, extent);
+  }
+}
+
+void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
+  (void)fiber; // currently unused
+
+  // Check if we can find a fiber exit function and call it if we find one
+  static AsanExitFiberFuncPtr fn = getExitFiberFunc();
+  if (fn == nullptr) {
+    LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
+  } else {
+    fn();
+  }
+}
+
+static AsanEnterFiberFuncPtr getEnterFiberFunc() {
+  AsanEnterFiberFuncPtr fn{nullptr};
+
+  // Check whether weak reference points to statically linked enter function
+  if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
+    return fn;
+  }
+
+  // Check whether we can find a dynamically linked enter function
+  if (nullptr !=
+      (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
+    return fn;
+  }
+
+  // Couldn't find the function at all
+  return nullptr;
+}
+
+static AsanExitFiberFuncPtr getExitFiberFunc() {
+  AsanExitFiberFuncPtr fn{nullptr};
+
+  // Check whether weak reference points to statically linked exit function
+  if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
+    return fn;
+  }
+
+  // Check whether we can find a dynamically linked enter function
+  if (nullptr !=
+      (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
+    return fn;
+  }
+
+  // Couldn't find the function at all
+  return nullptr;
+}
+
+#endif // FOLLY_SANITIZE_ADDRESS
 }}
index f90b9d67444a8591c11dc8f1dcd8b3ea57a52d38..919b09651dcda9599caa3351efb42d2cb12140a0 100644 (file)
@@ -337,6 +337,9 @@ class FiberManager : public ::folly::Executor {
     AtomicLinkedListHook<RemoteTask> nextRemoteTask;
   };
 
+  intptr_t activateFiber(Fiber* fiber);
+  intptr_t deactivateFiber(Fiber* fiber);
+
   typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
 
   Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
@@ -450,6 +453,16 @@ class FiberManager : public ::folly::Executor {
 
   void runReadyFiber(Fiber* fiber);
   void remoteReadyInsert(Fiber* fiber);
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+
+  // These methods notify ASAN when a fiber is entered/exited so that ASAN can
+  // find the right stack extents when it needs to poison/unpoison the stack.
+
+  void registerFiberActivationWithAsan(Fiber* fiber);
+  void registerFiberDeactivationWithAsan(Fiber* fiber);
+
+#endif // FOLLY_SANITIZE_ADDRESS
 };
 
 /**