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() {
pageFaultHandler = [&](int signal, siginfo_t *info, void *context) {
if (!this->verifyPageFault(info->si_addr)) {
previousHandler.sa_sigaction(signal, info, context);
callPreviousHandler(signal, info, context);
}
};
@ -33,8 +33,10 @@ PageFaultManagerLinux::PageFaultManagerLinux() {
}
PageFaultManagerLinux::~PageFaultManagerLinux() {
auto retVal = sigaction(SIGSEGV, &previousHandler, nullptr);
UNRECOVERABLE_IF(retVal != 0);
if (!previousHandlerRestored) {
auto retVal = sigaction(SIGSEGV, &previousHandler, nullptr);
UNRECOVERABLE_IF(retVal != 0);
}
}
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);
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

View File

@ -24,6 +24,9 @@ class PageFaultManagerLinux : public PageFaultManager {
void allowCPUMemoryAccess(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;
struct sigaction previousHandler = {};
};

View File

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