RawSPU: bound ELF loads; PPUInterpreter: fix modulo 127->128 (#18797)

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
This commit is contained in:
Arsh Kumar Singh 2026-05-27 20:57:58 +05:30 committed by GitHub
parent a87d175295
commit b41b10a031
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 13 additions and 1 deletions

View File

@ -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<u32>(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))
{

View File

@ -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<u32>(SPU_LS_SIZE));
continue;
}
std::memcpy(spu->_ptr<void>(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<u32>(SPU_LS_SIZE));
break;
}
std::memcpy(spu->_ptr<void>(offs), shdr.get_bin().data(), shdr.sh_size);
offs = utils::align<u32>(offs + shdr.sh_size, 4);
}