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,7 +107,17 @@ 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
HostSignalHandler(signal, info, ctx);
}
}
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 (signal == SIGSEGV) {
if (staticNce && staticNce->TrapHandler(reinterpret_cast<u8 *>(info->si_addr), true))
return;
bool runningUnderDebugger{[]() { bool runningUnderDebugger{[]() {
static std::ifstream status("/proc/self/status"); static std::ifstream status("/proc/self/status");
status.seekg(0); status.seekg(0);
@ -134,8 +144,7 @@ namespace skyline::nce {
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 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 signal::ExceptionalSignalHandler(signal, info, ctx); // Delegate throwing a host exception to the exceptional signal handler
}
} }
void *NceTlsRestorer() { void *NceTlsRestorer() {
@ -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());