From: Andrii Grynenko Date: Fri, 3 Jun 2016 22:01:28 +0000 (-0700) Subject: Add a SIGSEGV signal handler which detects stack overflow X-Git-Tag: 2016.07.26~167 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=9c0a6e4ed02ffc39ee393fbfff73dec76fcb5daa;p=folly.git Add a SIGSEGV signal handler which detects stack overflow Summary: GuardPageAllocator now keeps a track of all protected pages. If SIGSEGV occurs, signal handler can check address at fault against protected pages and if it matches - we got a fiber stack overflow. Reviewed By: yfeldblum Differential Revision: D3386960 fbshipit-source-id: 775b36ee08fecbbd87da0025f794717c222f5cce --- diff --git a/folly/fibers/GuardPageAllocator.cpp b/folly/fibers/GuardPageAllocator.cpp index 80bdbd75..87dc8f91 100644 --- a/folly/fibers/GuardPageAllocator.cpp +++ b/folly/fibers/GuardPageAllocator.cpp @@ -15,10 +15,13 @@ */ #include "GuardPageAllocator.h" +#include + #include #include #include +#include #include #include @@ -83,6 +86,9 @@ class StackCache { auto p = freeList_.back().first; if (!freeList_.back().second) { PCHECK(0 == ::mprotect(p, pagesize(), PROT_NONE)); + SYNCHRONIZED(pages, protectedPages()) { + pages.insert(reinterpret_cast(p)); + } } freeList_.pop_back(); @@ -122,9 +128,27 @@ class StackCache { ~StackCache() { assert(storage_); + SYNCHRONIZED(pages, protectedPages()) { + for (const auto& item : freeList_) { + pages.erase(reinterpret_cast(item.first)); + } + } PCHECK(0 == ::munmap(storage_, allocSize_ * kNumGuarded)); } + static bool isProtected(intptr_t addr) { + // Use a read lock for reading. + SYNCHRONIZED_CONST(pages, protectedPages()) { + for (const auto& page : pages) { + intptr_t pageEnd = page + pagesize(); + if (page <= addr && addr < pageEnd) { + return true; + } + } + } + return false; + } + private: folly::SpinLock lock_; unsigned char* storage_{nullptr}; @@ -144,8 +168,57 @@ class StackCache { static size_t allocSize(size_t size) { return pagesize() * ((size + pagesize() - 1) / pagesize() + 1); } + + static folly::Synchronized>& protectedPages() { + static auto instance = + new folly::Synchronized>(); + return *instance; + } }; +#ifndef _WIN32 + +namespace { + +struct sigaction oldSigsegvAction; + +void sigsegvSignalHandler(int signum, siginfo_t* info, void*) { + if (signum != SIGSEGV) { + std::cerr << "GuardPageAllocator signal handler called for signal: " + << signum; + return; + } + + if (info && + StackCache::isProtected(reinterpret_cast(info->si_addr))) { + std::cerr << "folly::fibers Fiber stack overflow detected." << std::endl; + } + + // Restore old signal handler and let it handle the signal. + sigaction(signum, &oldSigsegvAction, nullptr); + raise(signum); +} + +void installSignalHandler() { + static std::once_flag onceFlag; + std::call_once(onceFlag, []() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + // By default signal handlers are run on the signaled thread's stack. + // In case of stack overflow running the SIGSEGV signal handler on + // the same stack leads to another SIGSEGV and crashes the program. + // Use SA_ONSTACK, so alternate stack is used (only if configured via + // sigaltstack). + sa.sa_flags |= SA_SIGINFO | SA_ONSTACK; + sa.sa_sigaction = &sigsegvSignalHandler; + sigaction(SIGSEGV, &sa, &oldSigsegvAction); + }); +} +} + +#endif + class CacheManager { public: static CacheManager& instance() { @@ -204,7 +277,11 @@ class StackCacheEntry { }; GuardPageAllocator::GuardPageAllocator(bool useGuardPages) - : useGuardPages_(useGuardPages) {} + : useGuardPages_(useGuardPages) { +#ifndef _WIN32 + installSignalHandler(); +#endif +} GuardPageAllocator::~GuardPageAllocator() = default;