From fb976d4f612f326fa3ae9df9deaa22a3ef16c743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 7 Sep 2026 11:27:29 -0600 Subject: [PATCH] Clamp the module function scan to the text range it already validated The no-code-sections path validates textStart..textEnd, then derives its actual scan boundaries from modinfo->libent/libstub without checking those land inside it. flash0:/kd/sysmem.prx and loadcore.prx from a real firmware dump put them tens of megabytes past the end of the text, so the scan walked off into unmapped memory - a debug assert in Read_Instruction, and a pointless 134MB scan in release builds. For a well-formed module every boundary is already inside the range, so this is a no-op there. Co-Authored-By: Claude Opus 5 (1M context) --- Core/HLE/sceKernelModule.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 057f821174..66ad4b05fc 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1695,16 +1695,29 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load u32 scanEnd = module->textEnd; if (Memory::IsValid4AlignedRange(scanStart, scanEnd - scanStart)) { + // libent/libstub come from the module's own header, and nothing has checked that + // they land inside the range validated just above. flash0:/kd/sysmem.prx and + // loadcore.prx from a real firmware dump put them tens of megabytes past the end + // of the text. So let's clamp. + const u32 textStart = scanStart; + auto clampToText = [textStart, scanEnd](u32 addr) { + return std::min(std::max(addr, textStart), scanEnd); + }; + auto scanRange = [&insertSymbols](u32 from, u32 to) { + if (from < to) { + insertSymbols = MIPSAnalyst::ScanForFunctions(from, to, insertSymbols); + } + }; // Skip the exports and imports sections, they're not code. if (scanEnd >= std::min(modinfo->libent, modinfo->libstub)) { - insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::min(modinfo->libent, modinfo->libstub), insertSymbols); - scanStart = std::min(modinfo->libentend, modinfo->libstubend); + scanRange(scanStart, clampToText(std::min(modinfo->libent, modinfo->libstub))); + scanStart = clampToText(std::min(modinfo->libentend, modinfo->libstubend)); } if (scanEnd >= std::max(modinfo->libent, modinfo->libstub)) { - insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, std::max(modinfo->libent, modinfo->libstub), insertSymbols); - scanStart = std::max(modinfo->libentend, modinfo->libstubend); + scanRange(scanStart, clampToText(std::max(modinfo->libent, modinfo->libstub))); + scanStart = clampToText(std::max(modinfo->libentend, modinfo->libstubend)); } - insertSymbols = MIPSAnalyst::ScanForFunctions(scanStart, scanEnd, insertSymbols); + scanRange(scanStart, scanEnd); } else { ERROR_LOG(Log::Loader, "Bad text scan range %08x-%08x", scanStart, scanEnd); }