From aac64b1ded252e09debf4ed06dffe75b957cd57f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 14 Mar 2019 09:15:10 -0700 Subject: [PATCH] dmnt-cheat: workaround for 6.0.0+ kernel bug. --- .../dmnt/source/dmnt_cheat_manager.cpp | 80 +++++++++++++------ stratosphere/dmnt/source/dmnt_cheat_vm.cpp | 2 +- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/stratosphere/dmnt/source/dmnt_cheat_manager.cpp b/stratosphere/dmnt/source/dmnt_cheat_manager.cpp index f534b0acf..e84a72afd 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_manager.cpp +++ b/stratosphere/dmnt/source/dmnt_cheat_manager.cpp @@ -34,7 +34,7 @@ static Handle g_cheat_process_debug_hnd = 0; static bool g_enable_cheats_by_default = true; /* For debug event thread management. */ -static HosMutex g_debug_event_thread_lock; +static HosMutex g_debug_event_thread_lock, g_attach_lock; static bool g_has_debug_events_thread = false; /* To save some copying. */ @@ -46,6 +46,10 @@ static CheatEntry g_cheat_entries[DmntCheatManager::MaxCheatCount]; /* Global frozen address storage. */ static std::map g_frozen_addresses_map; +/* WORKAROUND: These prevent a kernel deadlock from occurring on 6.0.0+ */ +static HosThread g_workaround_threads[4]; +static void KernelWorkaroundThreadFunc(void *arg) { svcSleepThread(INT64_MAX); } + void DmntCheatManager::StartDebugEventsThread() { std::scoped_lock lk(g_debug_event_thread_lock); @@ -119,19 +123,17 @@ bool DmntCheatManager::HasActiveCheatProcess() { } void DmntCheatManager::ContinueCheatProcess() { - if (HasActiveCheatProcess()) { - /* Loop getting debug events. */ - u64 debug_event_buf[0x50]; - while (R_SUCCEEDED(svcGetDebugEvent((u8 *)debug_event_buf, g_cheat_process_debug_hnd))) { - /* ... */ - } + /* Loop getting debug events. */ + u8 debug_event_buf[0x50]; + while (R_SUCCEEDED(svcGetDebugEvent((u8 *)debug_event_buf, g_cheat_process_debug_hnd))) { + /* ... */ + } - /* Continue the process. */ - if (kernelAbove300()) { - svcContinueDebugEvent(g_cheat_process_debug_hnd, 5, nullptr, 0); - } else { - svcLegacyContinueDebugEvent(g_cheat_process_debug_hnd, 5, 0); - } + /* Continue the process, if needed. */ + if (kernelAbove300()) { + svcContinueDebugEvent(g_cheat_process_debug_hnd, 5, nullptr, 0); + } else { + svcLegacyContinueDebugEvent(g_cheat_process_debug_hnd, 5, 0); } } @@ -690,18 +692,28 @@ static void StartDebugProcess(u64 pid) { } Result DmntCheatManager::ForceOpenCheatProcess() { - std::scoped_lock lk(g_cheat_lock); + std::scoped_lock attach_lk(g_attach_lock); Result rc; - if (HasActiveCheatProcess()) { - return 0; + /* Acquire the cheat lock for long enough to close the process if needed. */ + { + std::scoped_lock lk(g_cheat_lock); + + if (HasActiveCheatProcess()) { + return 0; + } + + /* Close the current application, if it's open. */ + CloseActiveCheatProcess(); } - /* Close the current application, if it's open. */ - CloseActiveCheatProcess(); + /* Intentionally yield the cheat lock to the debug events thread. */ /* Wait to not have debug events thread. */ WaitDebugEventsThread(); + /* At this point, we can re-acquire the lock for the rest of the function. */ + std::scoped_lock lk(g_cheat_lock); + /* Get the current application process ID. */ if (R_FAILED((rc = pmdmntGetApplicationPid(&g_cheat_process_metadata.process_id)))) { return rc; @@ -762,7 +774,6 @@ Result DmntCheatManager::ForceOpenCheatProcess() { if (R_FAILED((rc = svcDebugActiveProcess(&g_cheat_process_debug_hnd, g_cheat_process_metadata.process_id)))) { return rc; } - /* Start debug events thread. */ StartDebugEventsThread(); @@ -773,15 +784,22 @@ Result DmntCheatManager::ForceOpenCheatProcess() { } void DmntCheatManager::OnNewApplicationLaunch() { - std::scoped_lock lk(g_cheat_lock); + std::scoped_lock attach_lk(g_attach_lock); Result rc; - /* Close the current application, if it's open. */ - CloseActiveCheatProcess(); + { + std::scoped_lock lk(g_cheat_lock); + /* Close the current application, if it's open. */ + CloseActiveCheatProcess(); + } + /* Intentionally yield the cheat lock to the debug events thread. */ /* Wait to not have debug events thread. */ WaitDebugEventsThread(); + /* At this point, we can re-acquire the lock for the rest of the function. */ + std::scoped_lock lk(g_cheat_lock); + /* Get the new application's process ID. */ if (R_FAILED((rc = pmdmntGetApplicationPid(&g_cheat_process_metadata.process_id)))) { fatalSimple(rc); @@ -909,13 +927,15 @@ void DmntCheatManager::VmThread(void *arg) { } void DmntCheatManager::DebugEventsThread(void *arg) { - while (R_SUCCEEDED(svcWaitSynchronizationSingle(g_cheat_process_debug_hnd, U64_MAX))) { + + while (R_SUCCEEDED(svcWaitSynchronizationSingle(g_cheat_process_debug_hnd, U64_MAX))) { std::scoped_lock lk(g_cheat_lock); /* Handle any pending debug events. */ if (HasActiveCheatProcess()) { ContinueCheatProcess(); } + } } @@ -955,6 +975,20 @@ void DmntCheatManager::InitializeCheatManager() { } } + /* WORKAROUND: On 6.0.0+, we must ensure that every core has at least one non-game thread. */ + /* This prevents a kernel deadlock when continuing debug events. */ + if (kernelAbove600()) { + for (size_t i = 0; i < sizeof(g_workaround_threads) / sizeof(g_workaround_threads[0]); i++) { + if (R_FAILED(g_workaround_threads[i].Initialize(&KernelWorkaroundThreadFunc, nullptr, 0x1000, 0, i))) { + std::abort(); + } + + if (R_FAILED(g_workaround_threads[i].Start())) { + std::abort(); + } + } + } + /* Spawn application detection thread, spawn cheat vm thread. */ if (R_FAILED(g_detect_thread.Initialize(&DmntCheatManager::DetectThread, nullptr, 0x4000, 39))) { std::abort(); diff --git a/stratosphere/dmnt/source/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/dmnt_cheat_vm.cpp index 9eaa0ffe6..82c4d95cc 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_vm.cpp +++ b/stratosphere/dmnt/source/dmnt_cheat_vm.cpp @@ -24,7 +24,7 @@ void DmntCheatVm::OpenDebugLogFile() { #ifdef DMNT_CHEAT_VM_DEBUG_LOG CloseDebugLogFile(); - this->debug_log_file = fopen("cheat_vm_log.txt", "wb"); + this->debug_log_file = fopen("cheat_vm_log.txt", "ab"); #endif }