Fix page fault handling on linux
Change-Id: Ic7c2697d9e143c9e4d668974fce0ade3fe060a46 Signed-off-by: Jobczyk, Lukasz <lukasz.jobczyk@intel.com>
This commit is contained in:
parent
9727de58b0
commit
00243a455d
|
@ -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() {
|
||||
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
|
||||
|
|
|
@ -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 = {};
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue