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:
PixelyIon 2021-10-16 12:34:24 +01:00
parent eff5711c49
commit 3b4bbd2b38
4 changed files with 33 additions and 9 deletions

View File

@ -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) {

View File

@ -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());

View File

@ -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);

View File

@ -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) {