Commit 20047d63 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

more asan wrappers for fibers

Summary: Wrap more of poisoning/unpoisoning and fiber stack switching.

Reviewed By: luciang

Differential Revision: D31635307

fbshipit-source-id: 9a6f9adbefe6fd995cfa864e0498102e237e6351
parent d9ae16d1
...@@ -28,47 +28,12 @@ ...@@ -28,47 +28,12 @@
#include <folly/ConstexprMath.h> #include <folly/ConstexprMath.h>
#include <folly/SingletonThreadLocal.h> #include <folly/SingletonThreadLocal.h>
#include <folly/memory/SanitizeAddress.h>
#include <folly/portability/Config.h> #include <folly/portability/Config.h>
#include <folly/portability/SysSyscall.h> #include <folly/portability/SysSyscall.h>
#include <folly/portability/Unistd.h> #include <folly/portability/Unistd.h>
#include <folly/synchronization/SanitizeThread.h> #include <folly/synchronization/SanitizeThread.h>
#ifdef FOLLY_SANITIZE_ADDRESS
#ifndef _WIN32
#include <dlfcn.h>
#endif
static void __sanitizer_start_switch_fiber_weak(
void** fake_stack_save,
void const* fiber_stack_base,
size_t fiber_stack_extent)
__attribute__((__weakref__("__sanitizer_start_switch_fiber")));
static void __sanitizer_finish_switch_fiber_weak(
void* fake_stack_save,
void const** old_stack_base,
size_t* old_stack_extent)
__attribute__((__weakref__("__sanitizer_finish_switch_fiber")));
static void __asan_unpoison_memory_region_weak(
void const /* nolint */ volatile* addr, size_t size)
__attribute__((__weakref__("__asan_unpoison_memory_region")));
typedef void (*AsanStartSwitchStackFuncPtr)(void**, void const*, size_t);
typedef void (*AsanFinishSwitchStackFuncPtr)(void*, void const**, size_t*);
typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
void const /* nolint */ volatile*, size_t);
namespace folly {
namespace fibers {
static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc();
static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc();
static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
} // namespace fibers
} // namespace folly
#endif
namespace std { namespace std {
template <> template <>
struct hash<folly::fibers::FiberManager::Options> { struct hash<folly::fibers::FiberManager::Options> {
...@@ -219,123 +184,31 @@ void FiberManager::FibersPoolResizer::run() { ...@@ -219,123 +184,31 @@ void FiberManager::FibersPoolResizer::run() {
} }
} }
#ifdef FOLLY_SANITIZE_ADDRESS
void FiberManager::registerStartSwitchStackWithAsan( void FiberManager::registerStartSwitchStackWithAsan(
void** saveFakeStack, const void* stackBottom, size_t stackSize) { void** saveFakeStack, const void* stackBottom, size_t stackSize) {
// Check if we can find a fiber enter function and call it if we find one sanitizer_start_switch_fiber(saveFakeStack, stackBottom, stackSize);
static AsanStartSwitchStackFuncPtr fn = getStartSwitchStackFunc();
if (fn == nullptr) {
LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
} else {
fn(saveFakeStack, stackBottom, stackSize);
}
} }
void FiberManager::registerFinishSwitchStackWithAsan( void FiberManager::registerFinishSwitchStackWithAsan(
void* saveFakeStack, const void** saveStackBottom, size_t* saveStackSize) { void* saveFakeStack, const void** saveStackBottom, size_t* saveStackSize) {
// Check if we can find a fiber exit function and call it if we find one sanitizer_finish_switch_fiber(saveFakeStack, saveStackBottom, saveStackSize);
static AsanFinishSwitchStackFuncPtr fn = getFinishSwitchStackFunc();
if (fn == nullptr) {
LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
} else {
fn(saveFakeStack, saveStackBottom, saveStackSize);
}
} }
void FiberManager::freeFakeStack(void* fakeStack) { void FiberManager::freeFakeStack(void* fakeStack) {
static AsanStartSwitchStackFuncPtr fnStart = getStartSwitchStackFunc();
static AsanFinishSwitchStackFuncPtr fnFinish = getFinishSwitchStackFunc();
if (fnStart == nullptr || fnFinish == nullptr) {
LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
}
void* saveFakeStack; void* saveFakeStack;
const void* stackBottom; const void* stackBottom;
size_t stackSize; size_t stackSize;
fnStart(&saveFakeStack, nullptr, 0); sanitizer_start_switch_fiber(&saveFakeStack, nullptr, 0);
fnFinish(fakeStack, &stackBottom, &stackSize); sanitizer_finish_switch_fiber(fakeStack, &stackBottom, &stackSize);
fnStart(nullptr, stackBottom, stackSize); sanitizer_start_switch_fiber(nullptr, stackBottom, stackSize);
fnFinish(saveFakeStack, nullptr, nullptr); sanitizer_finish_switch_fiber(saveFakeStack, nullptr, nullptr);
} }
void FiberManager::unpoisonFiberStack(const Fiber* fiber) { void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
auto stack = fiber->getStack(); auto stack = fiber->getStack();
asan_unpoison_memory_region(stack.first, stack.second);
// Check if we can find a fiber enter function and call it if we find one
static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
if (fn == nullptr) {
LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
} else {
fn(stack.first, stack.second);
}
} }
static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc() {
AsanStartSwitchStackFuncPtr fn{nullptr};
// Check whether weak reference points to statically linked enter function
if (nullptr != (fn = &::__sanitizer_start_switch_fiber_weak)) {
return fn;
}
// Check whether we can find a dynamically linked enter function
#ifndef _WIN32
if (nullptr !=
(fn = (AsanStartSwitchStackFuncPtr)dlsym(
RTLD_DEFAULT, "__sanitizer_start_switch_fiber"))) {
return fn;
}
#endif
// Couldn't find the function at all
return nullptr;
}
static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc() {
AsanFinishSwitchStackFuncPtr fn{nullptr};
// Check whether weak reference points to statically linked exit function
if (nullptr != (fn = &::__sanitizer_finish_switch_fiber_weak)) {
return fn;
}
// Check whether we can find a dynamically linked exit function
#ifndef _WIN32
if (nullptr !=
(fn = (AsanFinishSwitchStackFuncPtr)dlsym(
RTLD_DEFAULT, "__sanitizer_finish_switch_fiber"))) {
return fn;
}
#endif
// Couldn't find the function at all
return nullptr;
}
static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
// Check whether weak reference points to statically linked unpoison function
if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
return fn;
}
// Check whether we can find a dynamically linked unpoison function
#ifndef _WIN32
if (nullptr !=
(fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
return fn;
}
#endif
// Couldn't find the function at all
return nullptr;
}
#endif // FOLLY_SANITIZE_ADDRESS
// TVOS and WatchOS platforms have SIGSTKSZ but not sigaltstack // TVOS and WatchOS platforms have SIGSTKSZ but not sigaltstack
#if defined(SIGSTKSZ) && !FOLLY_APPLE_TVOS && !FOLLY_APPLE_WATCHOS #if defined(SIGSTKSZ) && !FOLLY_APPLE_TVOS && !FOLLY_APPLE_WATCHOS
......
...@@ -602,11 +602,8 @@ class FiberManager : public ::folly::Executor { ...@@ -602,11 +602,8 @@ 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 // 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. // find the right stack extents when it needs to poison/unpoison the stack.
void registerStartSwitchStackWithAsan( void registerStartSwitchStackWithAsan(
void** saveFakeStack, const void* stackBase, size_t stackSize); void** saveFakeStack, const void* stackBase, size_t stackSize);
void registerFinishSwitchStackWithAsan( void registerFinishSwitchStackWithAsan(
...@@ -614,8 +611,6 @@ class FiberManager : public ::folly::Executor { ...@@ -614,8 +611,6 @@ class FiberManager : public ::folly::Executor {
void freeFakeStack(void* fakeStack); void freeFakeStack(void* fakeStack);
void unpoisonFiberStack(const Fiber* fiber); void unpoisonFiberStack(const Fiber* fiber);
#endif // FOLLY_SANITIZE_ADDRESS
bool alternateSignalStackRegistered_{false}; bool alternateSignalStackRegistered_{false};
void maybeRegisterAlternateSignalStack(); void maybeRegisterAlternateSignalStack();
......
...@@ -19,23 +19,57 @@ ...@@ -19,23 +19,57 @@
#include <folly/lang/Extern.h> #include <folly/lang/Extern.h>
// Address Sanitizer interface may be found at: // Address Sanitizer interface may be found at:
// https://github.com/llvm-mirror/compiler-rt/blob/master/include/sanitizer/asan_interface.h // https://github.com/llvm/llvm-project/blob/main/compiler-rt/include/sanitizer/asan_interface.h
// https://github.com/llvm/llvm-project/blob/main/compiler-rt/include/sanitizer/common_interface_defs.h
extern "C" void __asan_poison_memory_region(void const volatile*, std::size_t);
extern "C" void __asan_unpoison_memory_region(
void const volatile*, std::size_t);
extern "C" void* __asan_region_is_poisoned(void*, std::size_t); extern "C" void* __asan_region_is_poisoned(void*, std::size_t);
extern "C" int __asan_address_is_poisoned(void const volatile*);
extern "C" void __sanitizer_start_switch_fiber(
void**, void const*, std::size_t);
extern "C" void __sanitizer_finish_switch_fiber(
void*, void const**, std::size_t*);
namespace { namespace {
FOLLY_CREATE_EXTERN_ACCESSOR(
asan_poison_memory_region_access_v, __asan_poison_memory_region);
FOLLY_CREATE_EXTERN_ACCESSOR(
asan_unpoison_memory_region_access_v, __asan_unpoison_memory_region);
FOLLY_CREATE_EXTERN_ACCESSOR( FOLLY_CREATE_EXTERN_ACCESSOR(
asan_region_is_poisoned_access_v, __asan_region_is_poisoned); asan_region_is_poisoned_access_v, __asan_region_is_poisoned);
FOLLY_CREATE_EXTERN_ACCESSOR(
asan_address_is_poisoned_access_v, __asan_address_is_poisoned);
FOLLY_CREATE_EXTERN_ACCESSOR(
sanitizer_start_switch_fiber_access_v, __sanitizer_start_switch_fiber);
FOLLY_CREATE_EXTERN_ACCESSOR(
sanitizer_finish_switch_fiber_access_v, __sanitizer_finish_switch_fiber);
} // namespace constexpr bool E = folly::kIsLibrarySanitizeAddress;
static constexpr auto const E = folly::kIsLibrarySanitizeAddress; } // namespace
namespace folly { namespace folly {
namespace detail { namespace detail {
FOLLY_STORAGE_CONSTEXPR asan_mark_memory_region_t* const
asan_poison_memory_region_v = asan_poison_memory_region_access_v<E>;
FOLLY_STORAGE_CONSTEXPR asan_mark_memory_region_t* const
asan_unpoison_memory_region_v = asan_unpoison_memory_region_access_v<E>;
FOLLY_STORAGE_CONSTEXPR asan_region_is_poisoned_t* const FOLLY_STORAGE_CONSTEXPR asan_region_is_poisoned_t* const
asan_region_is_poisoned_v = asan_region_is_poisoned_access_v<E>; asan_region_is_poisoned_v = asan_region_is_poisoned_access_v<E>;
FOLLY_STORAGE_CONSTEXPR asan_address_is_poisoned_t* const
asan_address_is_poisoned_v = asan_address_is_poisoned_access_v<E>;
FOLLY_STORAGE_CONSTEXPR sanitizer_start_switch_fiber_t* const
sanitizer_start_switch_fiber_v = sanitizer_start_switch_fiber_access_v<E>;
FOLLY_STORAGE_CONSTEXPR sanitizer_finish_switch_fiber_t* const
sanitizer_finish_switch_fiber_v = sanitizer_finish_switch_fiber_access_v<E>;
} // namespace detail } // namespace detail
} // namespace folly } // namespace folly
...@@ -22,22 +22,96 @@ namespace folly { ...@@ -22,22 +22,96 @@ namespace folly {
namespace detail { namespace detail {
using asan_mark_memory_region_t = void(void const volatile*, std::size_t);
using asan_region_is_poisoned_t = void*(void* ptr, std::size_t len); using asan_region_is_poisoned_t = void*(void* ptr, std::size_t len);
using asan_address_is_poisoned_t = int(void const volatile*);
using sanitizer_start_switch_fiber_t = void(void**, void const*, std::size_t);
using sanitizer_finish_switch_fiber_t = void(void*, void const**, std::size_t*);
extern asan_mark_memory_region_t* const asan_poison_memory_region_v;
extern asan_mark_memory_region_t* const asan_unpoison_memory_region_v;
extern asan_region_is_poisoned_t* const asan_region_is_poisoned_v; extern asan_region_is_poisoned_t* const asan_region_is_poisoned_v;
extern asan_address_is_poisoned_t* const asan_address_is_poisoned_v;
extern sanitizer_start_switch_fiber_t* const sanitizer_start_switch_fiber_v;
extern sanitizer_finish_switch_fiber_t* const sanitizer_finish_switch_fiber_v;
} // namespace detail } // namespace detail
// asan_region_is_poisoned // asan_poison_memory_region
// //
// mimic: __asan_region_is_poisoned, llvm compiler-rt // Marks the memory region as poisoned.
//
// When Address Sanitizer is in force and a memory region is marked poisoned,
// accesses to any part of the memory region will trap.
//
// mimic: __asan_poison_memory_region, llvm compiler-rt
FOLLY_ALWAYS_INLINE static void asan_poison_memory_region(
void const volatile* const addr, std::size_t const size) {
auto fun = detail::asan_poison_memory_region_v;
return kIsSanitizeAddress && fun ? fun(addr, size) : void();
}
// asan_unpoison_memory_region
//
// Marks the memory region as not poisoned anymore.
//
// When Address Sanitizer is in force and a memory region is marked poisoned,
// accesses to any part of the memory region will trap.
//
// mimic: __asan_poison_memory_region, llvm compiler-rt
FOLLY_ALWAYS_INLINE static void asan_unpoison_memory_region(
void const volatile* const addr, std::size_t const size) {
auto fun = detail::asan_unpoison_memory_region_v;
return kIsSanitizeAddress && fun ? fun(addr, size) : void();
}
// asan_region_is_poisoned
// //
// Returns the address of the first byte in the region known to be poisoned, // Returns the address of the first byte in the region known to be poisoned,
// or nullptr if there is no such byte. If Address Sanitizer is unavailable, // or nullptr if there is no such byte. If Address Sanitizer is unavailable,
// always returns nullptr. // always returns nullptr.
//
// mimic: __asan_region_is_poisoned, llvm compiler-rt
FOLLY_ALWAYS_INLINE static void* asan_region_is_poisoned( FOLLY_ALWAYS_INLINE static void* asan_region_is_poisoned(
void* const ptr, std::size_t const len) { void* const addr, std::size_t const size) {
auto fun = detail::asan_region_is_poisoned_v; auto fun = detail::asan_region_is_poisoned_v;
return kIsSanitizeAddress ? fun(ptr, len) : nullptr; return kIsSanitizeAddress && fun ? fun(addr, size) : nullptr;
}
// asan_address_is_poisoned
//
// Returns whether the address is known to be poisoned. If Address Sanitizer is
// unavailable, always returns 0.
//
// mimic: __asan_address_is_poisoned, llvm compiler-rt
FOLLY_ALWAYS_INLINE static int asan_address_is_poisoned(
void const volatile* const addr) {
auto fun = detail::asan_address_is_poisoned_v;
return kIsSanitizeAddress && fun ? fun(addr) : 0;
}
// sanitizer_start_switch_fiber
//
// Notifies Address Sanitizer, if available, that a switch to a different stack
// is imminent.
//
// mimic: __sanitizer_start_switch_fiber, llvm compiler-rt
FOLLY_ALWAYS_INLINE static void sanitizer_start_switch_fiber(
void** const save, void const* const bottom, std::size_t const size) {
auto fun = detail::sanitizer_start_switch_fiber_v;
return kIsSanitizeAddress && fun ? fun(save, bottom, size) : void();
}
// sanitizer_finish_switch_fiber
//
// Notifies Address Sanitizer, if available, that a switch to a different stack
// is complete.
//
// mimic: __sanitizer_finish_switch_fiber, llvm compiler-rt
FOLLY_ALWAYS_INLINE static void sanitizer_finish_switch_fiber(
void* const save, void const** const bottom, std::size_t* const size) {
auto fun = detail::sanitizer_finish_switch_fiber_v;
return kIsSanitizeAddress && fun ? fun(save, bottom, size) : void();
} }
} // namespace folly } // namespace folly
...@@ -16,15 +16,53 @@ ...@@ -16,15 +16,53 @@
#include <folly/memory/SanitizeAddress.h> #include <folly/memory/SanitizeAddress.h>
#include <new>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
class SanitizeAddressTest : public testing::Test {}; class SanitizeAddressTest : public testing::Test {};
TEST_F(SanitizeAddressTest, asan_region_is_poisoned) { TEST_F(SanitizeAddressTest, asan_poison) {
auto data = malloc(4096); constexpr auto page = size_t(1 << 12);
EXPECT_EQ(nullptr, folly::asan_region_is_poisoned(data, 4096)); constexpr auto size = page * 4;
free(data); constexpr auto offs = 27;
enum class opaque : unsigned char {};
// malloc
auto const addr =
static_cast<opaque*>(operator new (size, std::align_val_t{page}));
EXPECT_EQ(nullptr, folly::asan_region_is_poisoned(addr, size));
EXPECT_EQ(0, folly::asan_address_is_poisoned(addr + offs));
// poison
folly::asan_poison_memory_region(addr + page, page);
EXPECT_EQ(
folly::kIsSanitizeAddress ? addr + page : nullptr,
folly::asan_region_is_poisoned(addr, size));
EXPECT_EQ(
nullptr,
folly::asan_region_is_poisoned(addr + 2 * page, size - 2 * page));
EXPECT_EQ(
folly::kIsSanitizeAddress ? 1 : 0,
folly::asan_address_is_poisoned(addr + page + offs));
EXPECT_EQ(0, folly::asan_address_is_poisoned(addr + 2 * page + offs));
// unpoison
folly::asan_unpoison_memory_region(addr + page, page);
EXPECT_EQ(nullptr, folly::asan_region_is_poisoned(addr, size));
EXPECT_EQ(0, folly::asan_address_is_poisoned(addr + page + offs));
// free
operator delete (addr, size, std::align_val_t{page});
EXPECT_EQ(
folly::kIsSanitizeAddress ? addr : nullptr,
folly::asan_region_is_poisoned(addr, size));
EXPECT_EQ( EXPECT_EQ(
folly::kIsSanitizeAddress ? data : nullptr, folly::kIsSanitizeAddress ? 1 : 0,
folly::asan_region_is_poisoned(data, 4096)); folly::asan_address_is_poisoned(addr + offs));
} }
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