From 7e79334c18cda217bb68f7774bce6bbbdd533bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 19 Aug 2026 18:53:48 +0200 Subject: [PATCH] ELF: bound the segment table, and reject ELFs with no loadable segment segmentVAddr is a 32-entry array, but LoadInto filled it from e_phnum, which is a u16 - so an ELF declaring 65535 program headers wrote 65535 u32s into it, straight through the rest of the ElfReader object. The only check standing in front of that verified the program headers fit in the file, which a ~2MB crafted PRX satisfies. Rejected up front now: LoadRelocations already ignores segment numbers at or past the array size, so a module with more than that couldn't be relocated correctly anyway. Real modules have a handful - PSP_Header::nsegments is a u8 and no more than 4 are ever used. Second one from the same loop: with no PT_LOAD segment at all, totalStart stayed 0xFFFFFFFF and totalEnd 0, so totalSize came out as 1 (0 - 0xFFFFFFFF) and the loader went on to allocate a 1-byte block at 0xFFFFFFFF. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9 --- Core/ELF/ElfReader.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Core/ELF/ElfReader.cpp b/Core/ELF/ElfReader.cpp index 770bc7f155..91fbb39cbb 100644 --- a/Core/ELF/ElfReader.cpp +++ b/Core/ELF/ElfReader.cpp @@ -438,6 +438,14 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop) { return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED; } + // e_phnum is a u16, but we can only record the load address of ARRAY_SIZE(segmentVAddr) segments, + // and the relocation code can't refer to segments beyond that either (see LoadRelocations). Real + // PSP modules have a handful - PSP_Header::nsegments is a u8 and no more than 4 are ever used. + if (GetNumSegments() > (int)ARRAY_SIZE(segmentVAddr)) { + ERROR_LOG(Log::Loader, "ELF has %d segments, we support at most %d", GetNumSegments(), (int)ARRAY_SIZE(segmentVAddr)); + return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED; + } + // e_ident[EI_VERSION] is ignored // Should we relocate? @@ -467,9 +475,11 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop) { entryPoint = header->e_entry; u32 totalStart = 0xFFFFFFFF; u32 totalEnd = 0; + int numLoadSegments = 0; for (int i = 0; i < header->e_phnum; i++) { const Elf32_Phdr *p = &segments[i]; if (p->p_type == PT_LOAD) { + numLoadSegments++; if (p->p_vaddr < totalStart) { totalStart = p->p_vaddr; firstSegAlign = p->p_align; @@ -478,6 +488,12 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop) { totalEnd = p->p_vaddr + p->p_memsz; } } + // Without this, totalStart stays 0xFFFFFFFF and totalEnd 0, so totalSize would come out as 1 + // and we'd go on to allocate at 0xFFFFFFFF. + if (numLoadSegments == 0) { + ERROR_LOG(Log::Loader, "ELF has no loadable segments"); + return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED; + } totalSize = totalEnd - totalStart; // If a load address is specified that's in regular RAM, override kernel module status