Commit 9c0a6e4e authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot 2

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
parent 8aac0e33
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
*/ */
#include "GuardPageAllocator.h" #include "GuardPageAllocator.h"
#include <signal.h>
#include <mutex> #include <mutex>
#include <folly/Singleton.h> #include <folly/Singleton.h>
#include <folly/SpinLock.h> #include <folly/SpinLock.h>
#include <folly/Synchronized.h>
#include <folly/portability/SysMman.h> #include <folly/portability/SysMman.h>
#include <folly/portability/Unistd.h> #include <folly/portability/Unistd.h>
...@@ -83,6 +86,9 @@ class StackCache { ...@@ -83,6 +86,9 @@ class StackCache {
auto p = freeList_.back().first; auto p = freeList_.back().first;
if (!freeList_.back().second) { if (!freeList_.back().second) {
PCHECK(0 == ::mprotect(p, pagesize(), PROT_NONE)); PCHECK(0 == ::mprotect(p, pagesize(), PROT_NONE));
SYNCHRONIZED(pages, protectedPages()) {
pages.insert(reinterpret_cast<intptr_t>(p));
}
} }
freeList_.pop_back(); freeList_.pop_back();
...@@ -122,9 +128,27 @@ class StackCache { ...@@ -122,9 +128,27 @@ class StackCache {
~StackCache() { ~StackCache() {
assert(storage_); assert(storage_);
SYNCHRONIZED(pages, protectedPages()) {
for (const auto& item : freeList_) {
pages.erase(reinterpret_cast<intptr_t>(item.first));
}
}
PCHECK(0 == ::munmap(storage_, allocSize_ * kNumGuarded)); 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: private:
folly::SpinLock lock_; folly::SpinLock lock_;
unsigned char* storage_{nullptr}; unsigned char* storage_{nullptr};
...@@ -144,8 +168,57 @@ class StackCache { ...@@ -144,8 +168,57 @@ class StackCache {
static size_t allocSize(size_t size) { static size_t allocSize(size_t size) {
return pagesize() * ((size + pagesize() - 1) / pagesize() + 1); return pagesize() * ((size + pagesize() - 1) / pagesize() + 1);
} }
static folly::Synchronized<std::unordered_set<intptr_t>>& protectedPages() {
static auto instance =
new folly::Synchronized<std::unordered_set<intptr_t>>();
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<intptr_t>(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 { class CacheManager {
public: public:
static CacheManager& instance() { static CacheManager& instance() {
...@@ -204,7 +277,11 @@ class StackCacheEntry { ...@@ -204,7 +277,11 @@ class StackCacheEntry {
}; };
GuardPageAllocator::GuardPageAllocator(bool useGuardPages) GuardPageAllocator::GuardPageAllocator(bool useGuardPages)
: useGuardPages_(useGuardPages) {} : useGuardPages_(useGuardPages) {
#ifndef _WIN32
installSignalHandler();
#endif
}
GuardPageAllocator::~GuardPageAllocator() = default; GuardPageAllocator::~GuardPageAllocator() = default;
......
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