From b41b10a031f5e5070f52e20b8228ff4f4d389ebf Mon Sep 17 00:00:00 2001 From: Arsh Kumar Singh Date: Wed, 27 May 2026 20:57:58 +0530 Subject: [PATCH] RawSPU: bound ELF loads; PPUInterpreter: fix modulo 127->128 (#18797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small fixes that stop the emulator from writing past buffer boundaries. RawSPU: when loading a program onto an SPU processor, it now checks that the program actually fits in 256 KB before copying it. Before this, a corrupted file could write past the buffer into random host memory. PPUInterpreter: a one-character typo fix. A modulo operation used % 127 instead of % 128 when computing cross-boundary data sizes. This caused reservation checks to compare the wrong number of bytes on every load that crossed a cache line. This fix was pointed out by @AniLeo in the spam PR #18795 (Discovered by Opus 4.7) The PS3 hardware enforces these limits: - Each SPU has exactly 256 KB of memory (from Cell Broadband Engine Handbook v1.1) - The PPE cache line is 128 bytes — all reservation logic uses this granularity Neither fix should affect the usual games. They only applying to corrupted or malformed files. Will highly appreciate feedback and suggestions for this PR Tested on CI: CI passes on my fork 9 of 10 platforms (Mac Intel failed downloading a dependency). Title and Desc written by Codex --- rpcs3/Emu/Cell/PPUInterpreter.cpp | 2 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index a3c6611b15..3fa6d09f7b 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -501,7 +501,7 @@ auto ppu_feed_data(ppu_thread& ppu, u64 addr) if (raddr / 128 == addr / 128) src = &ppu.rdata[addr & 127], size = std::min(128 - (addr % 128), sizeof(T)); else - src = &ppu.rdata[0], size = (addr + u32{sizeof(T)}) % 127, offs = sizeof(T) - size; + src = &ppu.rdata[0], size = (addr + u32{sizeof(T)}) % 128, offs = sizeof(T) - size; if (std::memcmp(buffer + offs, src, size)) { diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 81bb6fd89e..1d1f659be0 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -427,6 +427,12 @@ void spu_load_exec(const spu_exec_object& elf) { if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) { + if (prog.p_vaddr >= SPU_LS_SIZE || prog.p_filesz > SPU_LS_SIZE - prog.p_vaddr) + { + spu_log.error("spu_load_exec: skipping segment with vaddr=0x%x filesz=0x%x (exceeds LS=0x%x)", prog.p_vaddr, prog.p_filesz, static_cast(SPU_LS_SIZE)); + continue; + } + std::memcpy(spu->_ptr(prog.p_vaddr), prog.bin.data(), prog.p_filesz); } } @@ -496,6 +502,12 @@ void spu_load_rel_exec(const spu_rel_object& elf) { if (shdr.sh_type == sec_type::sht_progbits && shdr.sh_flags().all_of(sh_flag::shf_alloc)) { + if (offs >= SPU_LS_SIZE || shdr.sh_size > SPU_LS_SIZE - offs) + { + spu_log.error("spu_load_rel_exec: skipping section at offs=0x%x sh_size=0x%x (exceeds LS=0x%x)", offs, shdr.sh_size, static_cast(SPU_LS_SIZE)); + break; + } + std::memcpy(spu->_ptr(offs), shdr.get_bin().data(), shdr.sh_size); offs = utils::align(offs + shdr.sh_size, 4); }