mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-03 18:14: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/)
|
||||
|
||||
#include <os.h>
|
||||
#include <nce.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <common/trace.h>
|
||||
#include <vfs/npdm.h>
|
||||
@ -218,9 +219,7 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void ExitProcess(const DeviceState &state) {
|
||||
state.logger->Debug("Exiting process");
|
||||
if (state.thread->id)
|
||||
state.process->Kill(false);
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
throw nce::NCE::ExitException(true);
|
||||
}
|
||||
|
||||
constexpr i32 IdealCoreDontCare{-1};
|
||||
@ -279,7 +278,7 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void ExitThread(const DeviceState &state) {
|
||||
state.logger->Debug("Exiting current thread");
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
throw nce::NCE::ExitException(false);
|
||||
}
|
||||
|
||||
void SleepThread(const DeviceState &state) {
|
||||
|
@ -14,6 +14,12 @@
|
||||
#include "nce.h"
|
||||
|
||||
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) {
|
||||
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
|
||||
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) {
|
||||
if (svc)
|
||||
state.logger->ErrorNoPrefix("{} (SVC: {})\nStack Trace:{}", e.what(), svc.name, state.loader->GetStackTrace());
|
||||
|
@ -17,6 +17,21 @@ namespace skyline::nce {
|
||||
static void SvcHandler(u16 svcId, ThreadContext *ctx);
|
||||
|
||||
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);
|
||||
|
||||
NCE(const DeviceState &state);
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <nce.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/hosbinder/IHOSBinderDriver.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) {}
|
||||
|
||||
Result ISelfController::Exit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (state.thread->id)
|
||||
state.process->Kill(false);
|
||||
std::longjmp(state.thread->originalCtx, true);
|
||||
return {};
|
||||
throw nce::NCE::ExitException(true);
|
||||
}
|
||||
|
||||
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
|
Loading…
Reference in New Issue
Block a user