From d1ca2e987f9f69a45fe7ea9e8af0052e35a3a1cd Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:51:16 +0200 Subject: [PATCH] PPCRec: Fix function scanning reading unmapped memory Scanning code near the start or end of a codecave could read past its memory region and crash. --- .../Recompiler/PPCFunctionBoundaryTracker.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h b/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h index 96b5143e..45c5001d 100644 --- a/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h +++ b/src/Cafe/HW/Espresso/Recompiler/PPCFunctionBoundaryTracker.h @@ -76,6 +76,9 @@ public: private: void addBranchDestination(PPCRange_t* sourceRange, MPTR address) { + // data that gets scanned as code can decode as a branch to unmapped memory + if (!memory_isAddressRangeAccessible(address, 4)) + return; map_queuedBranchTargets.emplace(address); map_branchTargetsAll.emplace(address); } @@ -131,8 +134,8 @@ private: // if the next two or previous two instructions are branch instructions, we assume that they are destinations of a jump table // todo - can we make this more reliable by checking for BCTR or similar instructions first? // example: The Swapper 0x01B1FC04 - if (PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(memory_readU32(address + 4)) && PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(memory_readU32(address + 8)) || - PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(memory_readU32(address - 8)) && PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(memory_readU32(address - 4))) + if (PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(address + 4) && PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(address + 8) || + PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(address - 8) && PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(address - 4)) { return true; } @@ -165,7 +168,7 @@ private: // Currently we only end the function if the BCTR is followed by a NOP (alignment) or invalid instruction // todo: improve robustness, find better ways to handle false positives - uint32 nextOpcode = memory_readU32(address + 4); + uint32 nextOpcode = memory_isAddressRangeAccessible(address + 4, 4) ? memory_readU32(address + 4) : 0; if (nextOpcode == 0x60000000 || PPCRecompilerCalcFuncSize_isValidInstruction(nextOpcode) == false) { @@ -221,6 +224,9 @@ private: MPTR endAddress = 0xFFFFFFFF; if (nextRange) endAddress = nextRange->startAddress; + // don't scan past the end of the mapped region + if (MMURange* mmuRange = memory_getMMURangeByAddress(startAddress); mmuRange && mmuRange->getEnd() < endAddress) + endAddress = mmuRange->getEnd(); while (currentAddress < endAddress) { if (!processInstruction(newRange, currentAddress)) @@ -293,8 +299,12 @@ private: } private: - bool PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(uint32 opcode) + bool PPCRecompilerCalcFuncSize_isUnconditionalBranchInstruction(MPTR address) { + // neighbours can be unmapped + if (!memory_isAddressRangeAccessible(address, 4)) + return false; + uint32 opcode = memory_readU32(address); if (Espresso::GetPrimaryOpcode(opcode) == Espresso::PrimaryOpcode::B) { uint32 LI;