dmnt-cheat: workaround for 6.0.0+ kernel bug.

This commit is contained in:
Michael Scire 2019-03-14 09:15:10 -07:00
parent b6d3df3335
commit aac64b1ded
2 changed files with 58 additions and 24 deletions

View file

@ -34,7 +34,7 @@ static Handle g_cheat_process_debug_hnd = 0;
static bool g_enable_cheats_by_default = true; static bool g_enable_cheats_by_default = true;
/* For debug event thread management. */ /* 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; static bool g_has_debug_events_thread = false;
/* To save some copying. */ /* To save some copying. */
@ -46,6 +46,10 @@ static CheatEntry g_cheat_entries[DmntCheatManager::MaxCheatCount];
/* Global frozen address storage. */ /* Global frozen address storage. */
static std::map<u64, FrozenAddressValue> g_frozen_addresses_map; static std::map<u64, FrozenAddressValue> 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() { void DmntCheatManager::StartDebugEventsThread() {
std::scoped_lock<HosMutex> lk(g_debug_event_thread_lock); std::scoped_lock<HosMutex> lk(g_debug_event_thread_lock);
@ -119,21 +123,19 @@ bool DmntCheatManager::HasActiveCheatProcess() {
} }
void DmntCheatManager::ContinueCheatProcess() { void DmntCheatManager::ContinueCheatProcess() {
if (HasActiveCheatProcess()) {
/* Loop getting debug events. */ /* Loop getting debug events. */
u64 debug_event_buf[0x50]; u8 debug_event_buf[0x50];
while (R_SUCCEEDED(svcGetDebugEvent((u8 *)debug_event_buf, g_cheat_process_debug_hnd))) { while (R_SUCCEEDED(svcGetDebugEvent((u8 *)debug_event_buf, g_cheat_process_debug_hnd))) {
/* ... */ /* ... */
} }
/* Continue the process. */ /* Continue the process, if needed. */
if (kernelAbove300()) { if (kernelAbove300()) {
svcContinueDebugEvent(g_cheat_process_debug_hnd, 5, nullptr, 0); svcContinueDebugEvent(g_cheat_process_debug_hnd, 5, nullptr, 0);
} else { } else {
svcLegacyContinueDebugEvent(g_cheat_process_debug_hnd, 5, 0); svcLegacyContinueDebugEvent(g_cheat_process_debug_hnd, 5, 0);
} }
} }
}
Result DmntCheatManager::ReadCheatProcessMemoryForVm(u64 proc_addr, void *out_data, size_t size) { Result DmntCheatManager::ReadCheatProcessMemoryForVm(u64 proc_addr, void *out_data, size_t size) {
if (HasActiveCheatProcess()) { if (HasActiveCheatProcess()) {
@ -690,18 +692,28 @@ static void StartDebugProcess(u64 pid) {
} }
Result DmntCheatManager::ForceOpenCheatProcess() { Result DmntCheatManager::ForceOpenCheatProcess() {
std::scoped_lock<HosMutex> lk(g_cheat_lock); std::scoped_lock<HosMutex> attach_lk(g_attach_lock);
Result rc; Result rc;
/* Acquire the cheat lock for long enough to close the process if needed. */
{
std::scoped_lock<HosMutex> lk(g_cheat_lock);
if (HasActiveCheatProcess()) { if (HasActiveCheatProcess()) {
return 0; return 0;
} }
/* Close the current application, if it's open. */ /* Close the current application, if it's open. */
CloseActiveCheatProcess(); CloseActiveCheatProcess();
}
/* Intentionally yield the cheat lock to the debug events thread. */
/* Wait to not have debug events thread. */ /* Wait to not have debug events thread. */
WaitDebugEventsThread(); WaitDebugEventsThread();
/* At this point, we can re-acquire the lock for the rest of the function. */
std::scoped_lock<HosMutex> lk(g_cheat_lock);
/* Get the current application process ID. */ /* Get the current application process ID. */
if (R_FAILED((rc = pmdmntGetApplicationPid(&g_cheat_process_metadata.process_id)))) { if (R_FAILED((rc = pmdmntGetApplicationPid(&g_cheat_process_metadata.process_id)))) {
return rc; 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)))) { if (R_FAILED((rc = svcDebugActiveProcess(&g_cheat_process_debug_hnd, g_cheat_process_metadata.process_id)))) {
return rc; return rc;
} }
/* Start debug events thread. */ /* Start debug events thread. */
StartDebugEventsThread(); StartDebugEventsThread();
@ -773,15 +784,22 @@ Result DmntCheatManager::ForceOpenCheatProcess() {
} }
void DmntCheatManager::OnNewApplicationLaunch() { void DmntCheatManager::OnNewApplicationLaunch() {
std::scoped_lock<HosMutex> lk(g_cheat_lock); std::scoped_lock<HosMutex> attach_lk(g_attach_lock);
Result rc; Result rc;
{
std::scoped_lock<HosMutex> lk(g_cheat_lock);
/* Close the current application, if it's open. */ /* Close the current application, if it's open. */
CloseActiveCheatProcess(); CloseActiveCheatProcess();
}
/* Intentionally yield the cheat lock to the debug events thread. */
/* Wait to not have debug events thread. */ /* Wait to not have debug events thread. */
WaitDebugEventsThread(); WaitDebugEventsThread();
/* At this point, we can re-acquire the lock for the rest of the function. */
std::scoped_lock<HosMutex> lk(g_cheat_lock);
/* Get the new application's process ID. */ /* Get the new application's process ID. */
if (R_FAILED((rc = pmdmntGetApplicationPid(&g_cheat_process_metadata.process_id)))) { if (R_FAILED((rc = pmdmntGetApplicationPid(&g_cheat_process_metadata.process_id)))) {
fatalSimple(rc); fatalSimple(rc);
@ -909,6 +927,7 @@ void DmntCheatManager::VmThread(void *arg) {
} }
void DmntCheatManager::DebugEventsThread(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<HosMutex> lk(g_cheat_lock); std::scoped_lock<HosMutex> lk(g_cheat_lock);
@ -916,6 +935,7 @@ void DmntCheatManager::DebugEventsThread(void *arg) {
if (HasActiveCheatProcess()) { if (HasActiveCheatProcess()) {
ContinueCheatProcess(); 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. */ /* Spawn application detection thread, spawn cheat vm thread. */
if (R_FAILED(g_detect_thread.Initialize(&DmntCheatManager::DetectThread, nullptr, 0x4000, 39))) { if (R_FAILED(g_detect_thread.Initialize(&DmntCheatManager::DetectThread, nullptr, 0x4000, 39))) {
std::abort(); std::abort();

View file

@ -24,7 +24,7 @@
void DmntCheatVm::OpenDebugLogFile() { void DmntCheatVm::OpenDebugLogFile() {
#ifdef DMNT_CHEAT_VM_DEBUG_LOG #ifdef DMNT_CHEAT_VM_DEBUG_LOG
CloseDebugLogFile(); CloseDebugLogFile();
this->debug_log_file = fopen("cheat_vm_log.txt", "wb"); this->debug_log_file = fopen("cheat_vm_log.txt", "ab");
#endif #endif
} }