Commit 2ea64dd2 authored by Andrew Cox's avatar Andrew Cox Committed by Facebook Github Bot 1

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
parent d9e0b59d
...@@ -132,19 +132,8 @@ void Fiber::fiberFuncHelper(intptr_t fiber) { ...@@ -132,19 +132,8 @@ void Fiber::fiberFuncHelper(intptr_t fiber) {
reinterpret_cast<Fiber*>(fiber)->fiberFunc(); 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() { void Fiber::fiberFunc() {
while (loopForever) { while (true) {
DCHECK_EQ(state_, NOT_STARTED); DCHECK_EQ(state_, NOT_STARTED);
threadId_ = localThreadId(); threadId_ = localThreadId();
...@@ -176,9 +165,8 @@ void Fiber::fiberFunc() { ...@@ -176,9 +165,8 @@ void Fiber::fiberFunc() {
state_ = INVALID; state_ = INVALID;
fiberManager_.activeFiber_ = nullptr; auto context = fiberManager_.deactivateFiber(this);
auto context = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0);
DCHECK_EQ(reinterpret_cast<Fiber*>(context), this); DCHECK_EQ(reinterpret_cast<Fiber*>(context), this);
} }
} }
...@@ -191,12 +179,11 @@ intptr_t Fiber::preempt(State state) { ...@@ -191,12 +179,11 @@ intptr_t Fiber::preempt(State state) {
DCHECK_EQ(state_, RUNNING); DCHECK_EQ(state_, RUNNING);
DCHECK_NE(state, RUNNING); DCHECK_NE(state, RUNNING);
fiberManager_.activeFiber_ = nullptr;
state_ = state; state_ = state;
recordStackPosition(); recordStackPosition();
ret = jumpContext(&fcontext_, &fiberManager_.mainContext_, 0); ret = fiberManager_.deactivateFiber(this);
DCHECK_EQ(fiberManager_.activeFiber_, this); DCHECK_EQ(fiberManager_.activeFiber_, this);
DCHECK_EQ(state_, READY_TO_RUN); DCHECK_EQ(state_, READY_TO_RUN);
......
...@@ -58,6 +58,28 @@ inline void FiberManager::ensureLoopScheduled() { ...@@ -58,6 +58,28 @@ inline void FiberManager::ensureLoopScheduled() {
loopController_->schedule(); 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) { inline void FiberManager::runReadyFiber(Fiber* fiber) {
SCOPE_EXIT { SCOPE_EXIT {
assert(currentFiber_ == nullptr); assert(currentFiber_ == nullptr);
...@@ -74,8 +96,7 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) { ...@@ -74,8 +96,7 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) {
while (fiber->state_ == Fiber::NOT_STARTED || while (fiber->state_ == Fiber::NOT_STARTED ||
fiber->state_ == Fiber::READY_TO_RUN) { fiber->state_ == Fiber::READY_TO_RUN) {
activeFiber_ = fiber; activateFiber(fiber);
jumpContext(&mainContext_, &fiber->fcontext_, fiber->data_);
if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) { if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {
try { try {
immediateFunc_(); immediateFunc_();
......
...@@ -26,6 +26,29 @@ ...@@ -26,6 +26,29 @@
#include <folly/experimental/fibers/Fiber.h> #include <folly/experimental/fibers/Fiber.h>
#include <folly/experimental/fibers/LoopController.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 { namespace folly { namespace fibers {
FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr; FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
...@@ -88,7 +111,6 @@ Fiber* FiberManager::getFiber() { ...@@ -88,7 +111,6 @@ Fiber* FiberManager::getFiber() {
++fiberId_; ++fiberId_;
bool recordStack = (options_.recordStackEvery != 0) && bool recordStack = (options_.recordStackEvery != 0) &&
(fiberId_ % options_.recordStackEvery == 0); (fiberId_ % options_.recordStackEvery == 0);
fiber->init(recordStack);
return fiber; return fiber;
} }
...@@ -139,7 +161,7 @@ void FiberManager::doFibersPoolResizing() { ...@@ -139,7 +161,7 @@ void FiberManager::doFibersPoolResizing() {
maxFibersActiveLastPeriod_ = fibersActive_; maxFibersActiveLastPeriod_ = fibersActive_;
} }
void FiberManager::FiberManager::FibersPoolResizer::operator()() { void FiberManager::FibersPoolResizer::operator()() {
fiberManager_.doFibersPoolResizing(); fiberManager_.doFibersPoolResizing();
fiberManager_.timeoutManager_->registerTimeout( fiberManager_.timeoutManager_->registerTimeout(
*this, *this,
...@@ -147,4 +169,70 @@ void FiberManager::FiberManager::FibersPoolResizer::operator()() { ...@@ -147,4 +169,70 @@ void FiberManager::FiberManager::FibersPoolResizer::operator()() {
fiberManager_.options_.fibersPoolResizePeriodMs)); 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
}} }}
...@@ -337,6 +337,9 @@ class FiberManager : public ::folly::Executor { ...@@ -337,6 +337,9 @@ class FiberManager : public ::folly::Executor {
AtomicLinkedListHook<RemoteTask> nextRemoteTask; AtomicLinkedListHook<RemoteTask> nextRemoteTask;
}; };
intptr_t activateFiber(Fiber* fiber);
intptr_t deactivateFiber(Fiber* fiber);
typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue; typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */ Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
...@@ -450,6 +453,16 @@ class FiberManager : public ::folly::Executor { ...@@ -450,6 +453,16 @@ class FiberManager : public ::folly::Executor {
void runReadyFiber(Fiber* fiber); void runReadyFiber(Fiber* fiber);
void remoteReadyInsert(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
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment