Handle host accesses for NCE Memory Trapping API

We cannot ignore accesses from the host to a region protected by the NCE Memory Trapping API, there's often access to regions which have overlap with a protected region unintentionally and those accesses need to be handled correctly rather than leading to a crash. This is done by implementing an additional signal handler `NCE::HostSignalHandler` to lookup any potential traps on a `SIGSEGV` and handle them correctly or when there isn't a corresponding trap raise a `SIGTRAP` when debugger is connected or delegate to `signal::ExceptionalSignalHandler` when it isn't.
This commit is contained in:
PixelyIon 2022-04-03 17:37:35 +05:30
parent b04a0c386a
commit cb2614f80e
4 changed files with 53 additions and 31 deletions

View File

@ -107,37 +107,46 @@ namespace skyline::nce {
*tls = nullptr; *tls = nullptr;
} else { // If TLS wasn't restored then this occurred in host code } else { // If TLS wasn't restored then this occurred in host code
if (signal == SIGSEGV) { HostSignalHandler(signal, info, ctx);
bool runningUnderDebugger{[]() {
static std::ifstream status("/proc/self/status");
status.seekg(0);
constexpr std::string_view TracerPidTag{"TracerPid:"};
for (std::string line; std::getline(status, line);) {
if (line.starts_with(TracerPidTag)) {
line = line.substr(TracerPidTag.size());
for (char character : line)
if (std::isspace(character))
continue;
else
return character != '0';
return false;
}
}
return false;
}()};
if (runningUnderDebugger)
raise(SIGTRAP); // Notify the debugger if we've got a SIGSEGV as the debugger doesn't catch them by default as they might be hooked
}
signal::ExceptionalSignalHandler(signal, info, ctx); //!< Delegate throwing a host exception to the exceptional signal handler
} }
} }
static NCE* staticNce{nullptr}; //!< A static instance of NCE for use in the signal handler
void NCE::HostSignalHandler(int signal, siginfo *info, ucontext *ctx) {
if (signal == SIGSEGV) {
if (staticNce && staticNce->TrapHandler(reinterpret_cast<u8 *>(info->si_addr), true))
return;
bool runningUnderDebugger{[]() {
static std::ifstream status("/proc/self/status");
status.seekg(0);
constexpr std::string_view TracerPidTag{"TracerPid:"};
for (std::string line; std::getline(status, line);) {
if (line.starts_with(TracerPidTag)) {
line = line.substr(TracerPidTag.size());
for (char character : line)
if (std::isspace(character))
continue;
else
return character != '0';
return false;
}
}
return false;
}()};
if (runningUnderDebugger)
raise(SIGTRAP); // Notify the debugger if we've got a SIGSEGV as the debugger doesn't catch them by default as they might be hooked
}
signal::ExceptionalSignalHandler(signal, info, ctx); // Delegate throwing a host exception to the exceptional signal handler
}
void *NceTlsRestorer() { void *NceTlsRestorer() {
ThreadContext *threadCtx; ThreadContext *threadCtx;
asm volatile("MRS %x0, TPIDR_EL0":"=r"(threadCtx)); asm volatile("MRS %x0, TPIDR_EL0":"=r"(threadCtx));
@ -149,6 +158,7 @@ namespace skyline::nce {
NCE::NCE(const DeviceState &state) : state(state) { NCE::NCE(const DeviceState &state) : state(state) {
signal::SetTlsRestorer(&NceTlsRestorer); signal::SetTlsRestorer(&NceTlsRestorer);
staticNce = this;
} }
constexpr u8 MainSvcTrampolineSize{17}; // Size of the main SVC trampoline function in u32 units constexpr u8 MainSvcTrampolineSize{17}; // Size of the main SVC trampoline function in u32 units

View File

@ -64,6 +64,15 @@ namespace skyline::nce {
*/ */
static void SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls); static void SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls);
/**
* @brief Handles signals for any host threads which may access NCE trapped memory
* @note Any untrapped SIGSEGVs will emit SIGTRAP when a debugger is attached rather than throwing an exception
*/
static void HostSignalHandler(int signal, siginfo *info, ucontext *ctx);
/**
* @note There should only be one instance of NCE concurrently
*/
NCE(const DeviceState &state); NCE(const DeviceState &state);
struct PatchData { struct PatchData {

View File

@ -330,7 +330,8 @@ namespace skyline::soc::gm20b {
void ChannelGpfifo::Run() { void ChannelGpfifo::Run() {
pthread_setname_np(pthread_self(), "GPFIFO"); pthread_setname_np(pthread_self(), "GPFIFO");
try { try {
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler); signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE}, signal::ExceptionalSignalHandler);
signal::SetSignalHandler({SIGSEGV}, nce::NCE::HostSignalHandler); // We may access NCE trapped memory
gpEntries.Process([this](GpEntry gpEntry) { gpEntries.Process([this](GpEntry gpEntry) {
Logger::Debug("Processing pushbuffer: 0x{:X}, Size: 0x{:X}", gpEntry.Address(), +gpEntry.size); Logger::Debug("Processing pushbuffer: 0x{:X}, Size: 0x{:X}", gpEntry.Address(), +gpEntry.size);

View File

@ -2,6 +2,7 @@
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/signal.h> #include <common/signal.h>
#include <nce.h>
#include <loader/loader.h> #include <loader/loader.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <soc.h> #include <soc.h>
@ -115,7 +116,8 @@ namespace skyline::soc::host1x {
void ChannelCommandFifo::Run() { void ChannelCommandFifo::Run() {
pthread_setname_np(pthread_self(), "ChannelCommandFifo"); pthread_setname_np(pthread_self(), "ChannelCommandFifo");
try { try {
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler); signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE}, signal::ExceptionalSignalHandler);
signal::SetSignalHandler({SIGSEGV}, nce::NCE::HostSignalHandler); // We may access NCE trapped memory
gatherQueue.Process([this](span<u32> gather) { gatherQueue.Process([this](span<u32> gather) {
Logger::Debug("Processing pushbuffer: 0x{:X}, size: 0x{:X}", gather.data(), gather.size()); Logger::Debug("Processing pushbuffer: 0x{:X}, size: 0x{:X}", gather.data(), gather.size());