mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-23 11:19:16 +01:00
Switch to using exceptions for guest exiting
Guest-driven exiting could cause objects left on the heap due to a `std::longjmp` from high up in the host call stack, this has been fixed by introducing `ExitException` which implicitly unrolls the stack with the exception handling mechanism.
This commit is contained in:
parent
eff5711c49
commit
3b4bbd2b38
@ -2,6 +2,7 @@
|
|||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
|
#include <nce.h>
|
||||||
#include <kernel/types/KProcess.h>
|
#include <kernel/types/KProcess.h>
|
||||||
#include <common/trace.h>
|
#include <common/trace.h>
|
||||||
#include <vfs/npdm.h>
|
#include <vfs/npdm.h>
|
||||||
@ -218,9 +219,7 @@ namespace skyline::kernel::svc {
|
|||||||
|
|
||||||
void ExitProcess(const DeviceState &state) {
|
void ExitProcess(const DeviceState &state) {
|
||||||
state.logger->Debug("Exiting process");
|
state.logger->Debug("Exiting process");
|
||||||
if (state.thread->id)
|
throw nce::NCE::ExitException(true);
|
||||||
state.process->Kill(false);
|
|
||||||
std::longjmp(state.thread->originalCtx, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr i32 IdealCoreDontCare{-1};
|
constexpr i32 IdealCoreDontCare{-1};
|
||||||
@ -279,7 +278,7 @@ namespace skyline::kernel::svc {
|
|||||||
|
|
||||||
void ExitThread(const DeviceState &state) {
|
void ExitThread(const DeviceState &state) {
|
||||||
state.logger->Debug("Exiting current thread");
|
state.logger->Debug("Exiting current thread");
|
||||||
std::longjmp(state.thread->originalCtx, true);
|
throw nce::NCE::ExitException(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SleepThread(const DeviceState &state) {
|
void SleepThread(const DeviceState &state) {
|
||||||
|
@ -14,6 +14,12 @@
|
|||||||
#include "nce.h"
|
#include "nce.h"
|
||||||
|
|
||||||
namespace skyline::nce {
|
namespace skyline::nce {
|
||||||
|
NCE::ExitException::ExitException(bool killAllThreads) : killAllThreads(killAllThreads) {}
|
||||||
|
|
||||||
|
const char *NCE::ExitException::what() const noexcept {
|
||||||
|
return killAllThreads ? "ExitProcess" : "ExitThread";
|
||||||
|
}
|
||||||
|
|
||||||
void NCE::SvcHandler(u16 svcId, ThreadContext *ctx) {
|
void NCE::SvcHandler(u16 svcId, ThreadContext *ctx) {
|
||||||
TRACE_EVENT_END("guest");
|
TRACE_EVENT_END("guest");
|
||||||
|
|
||||||
@ -42,6 +48,12 @@ namespace skyline::nce {
|
|||||||
}
|
}
|
||||||
abi::__cxa_end_catch(); // We call this prior to the longjmp to cause the exception object to be destroyed
|
abi::__cxa_end_catch(); // We call this prior to the longjmp to cause the exception object to be destroyed
|
||||||
std::longjmp(state.thread->originalCtx, true);
|
std::longjmp(state.thread->originalCtx, true);
|
||||||
|
} catch (const ExitException &e) {
|
||||||
|
if (e.killAllThreads && state.thread->id) {
|
||||||
|
signal::BlockSignal({SIGINT});
|
||||||
|
state.process->Kill(false);
|
||||||
|
}
|
||||||
|
std::longjmp(state.thread->originalCtx, true);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
if (svc)
|
if (svc)
|
||||||
state.logger->ErrorNoPrefix("{} (SVC: {})\nStack Trace:{}", e.what(), svc.name, state.loader->GetStackTrace());
|
state.logger->ErrorNoPrefix("{} (SVC: {})\nStack Trace:{}", e.what(), svc.name, state.loader->GetStackTrace());
|
||||||
@ -94,7 +106,7 @@ namespace skyline::nce {
|
|||||||
std::ifstream status("/proc/self/status");
|
std::ifstream status("/proc/self/status");
|
||||||
constexpr std::string_view TracerPidTag = "TracerPid:";
|
constexpr std::string_view TracerPidTag = "TracerPid:";
|
||||||
|
|
||||||
for (std::string line; std::getline(status, line); ) {
|
for (std::string line; std::getline(status, line);) {
|
||||||
if (line.starts_with(TracerPidTag)) {
|
if (line.starts_with(TracerPidTag)) {
|
||||||
line = line.substr(TracerPidTag.size());
|
line = line.substr(TracerPidTag.size());
|
||||||
|
|
||||||
|
@ -17,6 +17,21 @@ namespace skyline::nce {
|
|||||||
static void SvcHandler(u16 svcId, ThreadContext *ctx);
|
static void SvcHandler(u16 svcId, ThreadContext *ctx);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief An exception which causes the throwing thread to exit alongside all threads optionally
|
||||||
|
* @note Exiting must not be performed directly as it could leak temporary objects on the stack by not calling their destructors
|
||||||
|
*/
|
||||||
|
struct ExitException : std::exception {
|
||||||
|
bool killAllThreads; //!< If to kill all threads or just the throwing thread
|
||||||
|
|
||||||
|
ExitException(bool killAllThreads = true);
|
||||||
|
|
||||||
|
virtual const char* what() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles any signals in the NCE threads
|
||||||
|
*/
|
||||||
static void SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls);
|
static void SignalHandler(int signal, siginfo *info, ucontext *ctx, void **tls);
|
||||||
|
|
||||||
NCE(const DeviceState &state);
|
NCE(const DeviceState &state);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <nce.h>
|
||||||
#include <kernel/types/KProcess.h>
|
#include <kernel/types/KProcess.h>
|
||||||
#include <services/hosbinder/IHOSBinderDriver.h>
|
#include <services/hosbinder/IHOSBinderDriver.h>
|
||||||
#include "ISelfController.h"
|
#include "ISelfController.h"
|
||||||
@ -9,10 +10,7 @@ namespace skyline::service::am {
|
|||||||
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), hosbinder(manager.CreateOrGetService<hosbinder::IHOSBinderDriver>("dispdrv")), BaseService(state, manager) {}
|
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), hosbinder(manager.CreateOrGetService<hosbinder::IHOSBinderDriver>("dispdrv")), BaseService(state, manager) {}
|
||||||
|
|
||||||
Result ISelfController::Exit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result ISelfController::Exit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
if (state.thread->id)
|
throw nce::NCE::ExitException(true);
|
||||||
state.process->Kill(false);
|
|
||||||
std::longjmp(state.thread->originalCtx, true);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||||
|
Loading…
Reference in New Issue
Block a user