From 940066db68ffa10d143b37c6bfacaecf1eb34cf5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 12 Jul 2020 21:59:08 -0700 Subject: [PATCH] Core: Track exception on bad jumps. --- Core/Core.cpp | 26 ++++++++++++++++++++++++++ Core/Core.h | 9 +++++++++ Core/HLE/sceKernelThread.cpp | 34 ++++++++++++++++++++++++++++++++++ Core/MIPS/MIPSInt.cpp | 3 +++ Core/MIPS/MIPSTables.cpp | 3 ++- UI/EmuScreen.cpp | 9 +++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index e0434bb721..ecc3d08259 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -404,6 +404,15 @@ const char *MemoryExceptionTypeAsString(MemoryExceptionType type) { } } +const char *ExecExceptionTypeAsString(ExecExceptionType type) { + switch (type) { + case ExecExceptionType::JUMP: return "CPU Jump"; + case ExecExceptionType::THREAD: return "Thread switch"; + default: + return "N/A"; + } +} + void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type) { const char *desc = MemoryExceptionTypeAsString(type); // In jit, we only flush PC when bIgnoreBadMemAccess is off. @@ -426,6 +435,23 @@ void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type) { } } +void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) { + const char *desc = ExecExceptionTypeAsString(type); + WARN_LOG(MEMMAP, "%s: Invalid destination %08x PC %08x LR %08x", desc, address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); + + if (!g_Config.bIgnoreBadMemAccess) { + ExceptionInfo &e = g_exceptionInfo; + e = {}; + e.type = ExceptionType::BAD_EXEC_ADDR; + e.info = ""; + e.exec_type = type; + e.address = address; + e.pc = pc; + Core_EnableStepping(true); + host->SetDebugMode(true); + } +} + void Core_Break() { ERROR_LOG(CPU, "BREAK!"); diff --git a/Core/Core.h b/Core/Core.h index ef428595bd..6cb8004cf4 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -85,8 +85,13 @@ enum class MemoryExceptionType { READ_BLOCK, WRITE_BLOCK, }; +enum class ExecExceptionType { + JUMP, + THREAD, +}; void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type); +void Core_ExecException(u32 address, u32 pc, ExecExceptionType type); void Core_Break(); enum class ExceptionType { @@ -104,9 +109,13 @@ struct ExceptionInfo { MemoryExceptionType memory_type; uint32_t pc; uint32_t address; + + // Reuses pc and address from memory type, where address is the failed destination. + ExecExceptionType exec_type; }; const ExceptionInfo &Core_GetExceptionInfo(); const char *ExceptionTypeAsString(ExceptionType type); const char *MemoryExceptionTypeAsString(MemoryExceptionType type); +const char *ExecExceptionTypeAsString(ExecExceptionType type); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 427d537edf..2b4e998ba6 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -31,6 +31,7 @@ #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/MemMapHelpers.h" #include "Core/MIPS/JitCommon/JitCommon.h" @@ -1142,6 +1143,10 @@ bool __KernelSwitchToThread(SceUID threadID, const char *reason) if (current && current->isRunning()) __KernelChangeReadyState(current, currentThread, true); + if (!Memory::IsValidAddress(t->context.pc)) { + Core_ExecException(t->context.pc, currentMIPS->pc, ExecExceptionType::THREAD); + } + __KernelSwitchContext(t, reason); return true; } @@ -1463,6 +1468,12 @@ void __KernelLoadContext(PSPThreadContext *ctx, bool vfpuEnabled) { memcpy(currentMIPS->vfpuCtrl, ctx->vfpuCtrl, sizeof(ctx->vfpuCtrl)); } +#ifdef _DEBUG + if (!Memory::IsValidAddress(ctx->pc)) { + Core_ExecException(ctx->pc, currentMIPS->pc, ExecExceptionType::THREAD); + } +#endif + memcpy(currentMIPS->other, ctx->other, sizeof(ctx->other)); if (MIPSComp::jit) { // When thread switching, we must update the rounding mode if cached in the jit. @@ -1912,6 +1923,10 @@ SceUID __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int strcpy(thread->nt.name, "root"); + if (!Memory::IsValidAddress(thread->context.pc)) { + Core_ExecException(thread->context.pc, currentMIPS->pc, ExecExceptionType::THREAD); + } + __KernelLoadContext(&thread->context, (attr & PSP_THREAD_ATTR_VFPU) != 0); currentMIPS->r[MIPS_REG_A0] = args; currentMIPS->r[MIPS_REG_SP] -= (args + 0xf) & ~0xf; @@ -2041,6 +2056,9 @@ int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bo // Smaller is better for priority. Only switch if the new thread is better. if (cur && cur->nt.currentPriority > startThread->nt.currentPriority) { + if (!Memory::IsValidAddress(startThread->context.pc)) { + Core_ExecException(startThread->context.pc, currentMIPS->pc, ExecExceptionType::THREAD); + } __KernelChangeReadyState(cur, currentThread, true); hleReSchedule("thread started"); } @@ -2903,6 +2921,10 @@ u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter) Memory::Write_U32(currentMIPS->r[MIPS_REG_SP], thread->currentStack.end - 8); Memory::Write_U32(currentMIPS->pc, thread->currentStack.end - 12); + if (!Memory::IsValidAddress(entryAddr)) { + Core_ExecException(entryAddr, currentMIPS->pc, ExecExceptionType::THREAD); + } + currentMIPS->pc = entryAddr; currentMIPS->r[MIPS_REG_A0] = entryParameter; currentMIPS->r[MIPS_REG_RA] = extendReturnHackAddr; @@ -2935,6 +2957,10 @@ void __KernelReturnFromExtendStack() return; } + if (!Memory::IsValidAddress(restorePC)) { + Core_ExecException(restorePC, currentMIPS->pc, ExecExceptionType::THREAD); + } + DEBUG_LOG(SCEKERNEL, "__KernelReturnFromExtendStack()"); currentMIPS->r[MIPS_REG_RA] = restoreRA; currentMIPS->r[MIPS_REG_SP] = restoreSP; @@ -3215,6 +3241,10 @@ bool __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter) call->savedId = cur->currentMipscallId; call->reschedAfter = reschedAfter; + if (!Memory::IsValidAddress(call->entryPoint)) { + Core_ExecException(call->entryPoint, currentMIPS->pc, ExecExceptionType::THREAD); + } + // Set up the new state currentMIPS->pc = call->entryPoint; currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress(); @@ -3264,6 +3294,10 @@ void __KernelReturnFromMipsCall() currentMIPS->r[MIPS_REG_RA] = Memory::Read_U32(sp + MIPS_REG_RA * 4); sp += 32 * 4; + if (!Memory::IsValidAddress(call->savedPc)) { + Core_ExecException(call->savedPc, currentMIPS->pc, ExecExceptionType::THREAD); + } + currentMIPS->pc = call->savedPc; // This is how we set the return value. currentMIPS->r[MIPS_REG_V0] = call->savedV0; diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index b8d99c078d..d562f97cde 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -54,6 +54,9 @@ static inline void DelayBranchTo(u32 where) { + if (!Memory::IsValidAddress(where)) { + Core_ExecException(where, PC, ExecExceptionType::JUMP); + } PC += 4; mipsr4k.nextPC = where; mipsr4k.inDelaySlot = true; diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 69e5d28afa..49f5b752b4 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -1019,13 +1019,14 @@ int MIPSInterpret_RunUntil(u64 globalTicks) if (curMips->inDelaySlot) { + curMips->downcount -= 1; // The reason we have to check this is the delay slot hack in Int_Syscall. if (wasInDelaySlot) { curMips->pc = curMips->nextPC; curMips->inDelaySlot = false; + continue; } - curMips->downcount -= 1; goto again; } } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 28c2159670..a17d0e4e65 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1305,6 +1305,15 @@ PC: %08x)", info.pc); draw2d->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); y += 120; + } else if (info.type == ExceptionType::BAD_EXEC_ADDR) { + snprintf(statbuf, sizeof(statbuf), R"( +Destination: %s to %08x +PC: %08x)", + ExecExceptionTypeAsString(info.exec_type), + info.address, + info.pc); + draw2d->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + y += 120; } std::string kernelState = __KernelStateSummary();