mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-04 23:55:08 +01:00
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:
parent
b04a0c386a
commit
cb2614f80e
@ -107,37 +107,46 @@ namespace skyline::nce {
|
||||
|
||||
*tls = nullptr;
|
||||
} else { // If TLS wasn't restored then this occurred in host code
|
||||
if (signal == SIGSEGV) {
|
||||
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
|
||||
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 (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() {
|
||||
ThreadContext *threadCtx;
|
||||
asm volatile("MRS %x0, TPIDR_EL0":"=r"(threadCtx));
|
||||
@ -149,6 +158,7 @@ namespace skyline::nce {
|
||||
|
||||
NCE::NCE(const DeviceState &state) : state(state) {
|
||||
signal::SetTlsRestorer(&NceTlsRestorer);
|
||||
staticNce = this;
|
||||
}
|
||||
|
||||
constexpr u8 MainSvcTrampolineSize{17}; // Size of the main SVC trampoline function in u32 units
|
||||
|
@ -64,6 +64,15 @@ namespace skyline::nce {
|
||||
*/
|
||||
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);
|
||||
|
||||
struct PatchData {
|
||||
|
@ -330,7 +330,8 @@ namespace skyline::soc::gm20b {
|
||||
void ChannelGpfifo::Run() {
|
||||
pthread_setname_np(pthread_self(), "GPFIFO");
|
||||
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) {
|
||||
Logger::Debug("Processing pushbuffer: 0x{:X}, Size: 0x{:X}", gpEntry.Address(), +gpEntry.size);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <common/signal.h>
|
||||
#include <nce.h>
|
||||
#include <loader/loader.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <soc.h>
|
||||
@ -115,7 +116,8 @@ namespace skyline::soc::host1x {
|
||||
void ChannelCommandFifo::Run() {
|
||||
pthread_setname_np(pthread_self(), "ChannelCommandFifo");
|
||||
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) {
|
||||
Logger::Debug("Processing pushbuffer: 0x{:X}, size: 0x{:X}", gather.data(), gather.size());
|
||||
@ -144,4 +146,4 @@ namespace skyline::soc::host1x {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user