From 2ea64dd24946cbc9f3f4ac3f6c6b98a486c56e73 Mon Sep 17 00:00:00 2001 From: Andrew Cox Date: Thu, 17 Mar 2016 07:09:07 -0700 Subject: [PATCH] Tell ASAN about fiber stacks 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 | 19 +--- folly/experimental/fibers/FiberManager-inl.h | 25 +++++- folly/experimental/fibers/FiberManager.cpp | 92 +++++++++++++++++++- folly/experimental/fibers/FiberManager.h | 13 +++ 4 files changed, 129 insertions(+), 20 deletions(-) diff --git a/folly/experimental/fibers/Fiber.cpp b/folly/experimental/fibers/Fiber.cpp index 47426fda..83e15cc2 100644 --- a/folly/experimental/fibers/Fiber.cpp +++ b/folly/experimental/fibers/Fiber.cpp @@ -132,19 +132,8 @@ void Fiber::fiberFuncHelper(intptr_t fiber) { reinterpret_cast(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(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); diff --git a/folly/experimental/fibers/FiberManager-inl.h b/folly/experimental/fibers/FiberManager-inl.h index 61d4fec0..1e6ed035 100644 --- a/folly/experimental/fibers/FiberManager-inl.h +++ b/folly/experimental/fibers/FiberManager-inl.h @@ -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_(); diff --git a/folly/experimental/fibers/FiberManager.cpp b/folly/experimental/fibers/FiberManager.cpp index c6094864..d4dc6944 100644 --- a/folly/experimental/fibers/FiberManager.cpp +++ b/folly/experimental/fibers/FiberManager.cpp @@ -26,6 +26,29 @@ #include #include +#ifdef FOLLY_SANITIZE_ADDRESS + +#include + +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(top) - static_cast(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 }} diff --git a/folly/experimental/fibers/FiberManager.h b/folly/experimental/fibers/FiberManager.h index f90b9d67..919b0965 100644 --- a/folly/experimental/fibers/FiberManager.h +++ b/folly/experimental/fibers/FiberManager.h @@ -337,6 +337,9 @@ class FiberManager : public ::folly::Executor { AtomicLinkedListHook nextRemoteTask; }; + intptr_t activateFiber(Fiber* fiber); + intptr_t deactivateFiber(Fiber* fiber); + typedef folly::IntrusiveList 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 }; /** -- 2.34.1