Fix page fault handling on linux

Change-Id: Ic7c2697d9e143c9e4d668974fce0ade3fe060a46
Signed-off-by: Jobczyk, Lukasz <lukasz.jobczyk@intel.com>
This commit is contained in:
Jobczyk, Lukasz 2019-11-26 09:25:32 +01:00 committed by sys_ocldev
parent 9727de58b0
commit 00243a455d
3 changed files with 94 additions and 7 deletions

View File

@ -21,7 +21,7 @@ std::function<void(int signal, siginfo_t *info, void *context)> PageFaultManager
PageFaultManagerLinux::PageFaultManagerLinux() { PageFaultManagerLinux::PageFaultManagerLinux() {
pageFaultHandler = [&](int signal, siginfo_t *info, void *context) { pageFaultHandler = [&](int signal, siginfo_t *info, void *context) {
if (!this->verifyPageFault(info->si_addr)) { if (!this->verifyPageFault(info->si_addr)) {
previousHandler.sa_sigaction(signal, info, context); callPreviousHandler(signal, info, context);
} }
}; };
@ -33,8 +33,10 @@ PageFaultManagerLinux::PageFaultManagerLinux() {
} }
PageFaultManagerLinux::~PageFaultManagerLinux() { PageFaultManagerLinux::~PageFaultManagerLinux() {
auto retVal = sigaction(SIGSEGV, &previousHandler, nullptr); if (!previousHandlerRestored) {
UNRECOVERABLE_IF(retVal != 0); auto retVal = sigaction(SIGSEGV, &previousHandler, nullptr);
UNRECOVERABLE_IF(retVal != 0);
}
} }
void PageFaultManagerLinux::pageFaultHandlerWrapper(int signal, siginfo_t *info, void *context) { void PageFaultManagerLinux::pageFaultHandlerWrapper(int signal, siginfo_t *info, void *context) {
@ -50,4 +52,20 @@ void PageFaultManagerLinux::protectCPUMemoryAccess(void *ptr, size_t size) {
auto retVal = mprotect(ptr, size, PROT_NONE); auto retVal = mprotect(ptr, size, PROT_NONE);
UNRECOVERABLE_IF(retVal != 0); UNRECOVERABLE_IF(retVal != 0);
} }
void PageFaultManagerLinux::callPreviousHandler(int signal, siginfo_t *info, void *context) {
if (previousHandler.sa_flags & SA_SIGINFO) {
previousHandler.sa_sigaction(signal, info, context);
} else {
if (previousHandler.sa_handler == SIG_DFL) {
auto retVal = sigaction(SIGSEGV, &previousHandler, nullptr);
UNRECOVERABLE_IF(retVal != 0);
previousHandlerRestored = true;
} else if (previousHandler.sa_handler == SIG_IGN) {
return;
} else {
previousHandler.sa_handler(signal);
}
}
}
} // namespace NEO } // namespace NEO

View File

@ -24,6 +24,9 @@ class PageFaultManagerLinux : public PageFaultManager {
void allowCPUMemoryAccess(void *ptr, size_t size) override; void allowCPUMemoryAccess(void *ptr, size_t size) override;
void protectCPUMemoryAccess(void *ptr, size_t size) override; void protectCPUMemoryAccess(void *ptr, size_t size) override;
void callPreviousHandler(int signal, siginfo_t *info, void *context);
bool previousHandlerRestored = false;
static std::function<void(int signal, siginfo_t *info, void *context)> pageFaultHandler; static std::function<void(int signal, siginfo_t *info, void *context)> pageFaultHandler;
struct sigaction previousHandler = {}; struct sigaction previousHandler = {};
}; };

View File

@ -42,7 +42,9 @@ TEST(PageFaultManagerLinuxTest, givenProtectedMemoryWhenTryingToAccessThenPageFa
class MockFailPageFaultManager : public PageFaultManagerLinux { class MockFailPageFaultManager : public PageFaultManagerLinux {
public: public:
using PageFaultManagerLinux::callPreviousHandler;
using PageFaultManagerLinux::PageFaultManagerLinux; using PageFaultManagerLinux::PageFaultManagerLinux;
using PageFaultManagerLinux::previousHandlerRestored;
bool verifyPageFault(void *ptr) override { bool verifyPageFault(void *ptr) override {
verifyCalled = true; verifyCalled = true;
@ -53,15 +55,24 @@ class MockFailPageFaultManager : public PageFaultManagerLinux {
mockCalled = true; mockCalled = true;
} }
static void mockPageFaultSimpleHandler(int signal) {
simpleMockCalled = true;
}
~MockFailPageFaultManager() override { ~MockFailPageFaultManager() override {
mockCalled = false; mockCalled = false;
simpleMockCalled = false;
} }
static bool mockCalled; static bool mockCalled;
static bool simpleMockCalled;
bool verifyCalled = false; bool verifyCalled = false;
}; };
bool MockFailPageFaultManager::mockCalled = false;
TEST(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleThenDefaultHandlerIsCalled) { bool MockFailPageFaultManager::mockCalled = false;
bool MockFailPageFaultManager::simpleMockCalled = false;
TEST(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleAndSigInfoFlagSetThenSaSigactionIsCalled) {
struct sigaction previousHandler = {}; struct sigaction previousHandler = {};
struct sigaction mockHandler = {}; struct sigaction mockHandler = {};
mockHandler.sa_flags = SA_SIGINFO; mockHandler.sa_flags = SA_SIGINFO;
@ -69,12 +80,67 @@ TEST(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleThenDefaultH
auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler); auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler);
EXPECT_EQ(retVal, 0); EXPECT_EQ(retVal, 0);
MockFailPageFaultManager mockPageFaultManager; auto mockPageFaultManager = std::make_unique<MockFailPageFaultManager>();
EXPECT_FALSE(MockFailPageFaultManager::mockCalled); EXPECT_FALSE(MockFailPageFaultManager::mockCalled);
EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled);
std::raise(SIGSEGV); std::raise(SIGSEGV);
EXPECT_TRUE(mockPageFaultManager.verifyCalled); EXPECT_TRUE(mockPageFaultManager->verifyCalled);
EXPECT_TRUE(MockFailPageFaultManager::mockCalled); EXPECT_TRUE(MockFailPageFaultManager::mockCalled);
EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled);
mockPageFaultManager.reset();
sigaction(SIGSEGV, &previousHandler, nullptr); sigaction(SIGSEGV, &previousHandler, nullptr);
} }
TEST(PageFaultManagerLinuxTest, givenPageFaultThatNEOShouldNotHandleThenSaHandlerIsCalled) {
struct sigaction previousHandler = {};
struct sigaction mockHandler = {};
mockHandler.sa_handler = MockFailPageFaultManager::mockPageFaultSimpleHandler;
auto retVal = sigaction(SIGSEGV, &mockHandler, &previousHandler);
EXPECT_EQ(retVal, 0);
auto mockPageFaultManager = std::make_unique<MockFailPageFaultManager>();
EXPECT_FALSE(MockFailPageFaultManager::mockCalled);
EXPECT_FALSE(MockFailPageFaultManager::simpleMockCalled);
std::raise(SIGSEGV);
EXPECT_TRUE(mockPageFaultManager->verifyCalled);
EXPECT_FALSE(MockFailPageFaultManager::mockCalled);
EXPECT_TRUE(MockFailPageFaultManager::simpleMockCalled);
mockPageFaultManager.reset();
sigaction(SIGSEGV, &previousHandler, nullptr);
}
TEST(PageFaultManagerLinuxTest, givenDefaultSaHandlerWhenInvokeCallPreviousSaHandlerThenPreviousHandlerIsRestored) {
struct sigaction originalHandler = {};
struct sigaction mockDefaultHandler = {};
mockDefaultHandler.sa_handler = SIG_DFL;
auto retVal = sigaction(SIGSEGV, &mockDefaultHandler, &originalHandler);
EXPECT_EQ(retVal, 0);
auto mockPageFaultManager = std::make_unique<MockFailPageFaultManager>();
mockPageFaultManager->callPreviousHandler(0, nullptr, nullptr);
EXPECT_TRUE(mockPageFaultManager->previousHandlerRestored);
mockPageFaultManager.reset();
sigaction(SIGSEGV, &originalHandler, nullptr);
}
TEST(PageFaultManagerLinuxTest, givenIgnoringSaHandlerWhenInvokeCallPreviousSaHandlerThenNothingHappend) {
struct sigaction originalHandler = {};
struct sigaction mockDefaultHandler = {};
mockDefaultHandler.sa_handler = SIG_IGN;
auto retVal = sigaction(SIGSEGV, &mockDefaultHandler, &originalHandler);
EXPECT_EQ(retVal, 0);
auto mockPageFaultManager = std::make_unique<MockFailPageFaultManager>();
mockPageFaultManager->callPreviousHandler(0, nullptr, nullptr);
EXPECT_FALSE(mockPageFaultManager->previousHandlerRestored);
mockPageFaultManager.reset();
sigaction(SIGSEGV, &originalHandler, nullptr);
}