Every decrypt type reads the tag at 0xD0, the compressed size at 0xB0 and key
data as far as 0x150, and writes a KIRK header into outbuf at a fixed offset
derived from sizeof(PSP_Header) - all without checking that either buffer is
that big. A PRX declaring a tiny psp_size therefore read past the end of its
input, wrote a 0xE0-byte header past the end of an equally tiny output buffer,
and handed KIRK "size - offset" as an unsigned underflow. The header write sits
behind the SHA-1 check, but the tag keys are compiled in and every hashed input
comes from the file, so that's arithmetic rather than luck. One size check at
the top of pspDecryptPRX covers all five types.
The module loader needed two things to go with it. Its "maybe it just isn't
encrypted" fallback checked for ELF magic at 0x150 of the *output* buffer, which
on the paths where decryption bails early has nothing written to it yet - so it
read uninitialized heap to decide, and then, if psp_size was under 0x150,
memcpy'd a negative length. It reads the input buffer now, which is what it goes
on to copy from anyway, and only when psp_size is big enough to hold what's
being tested.
Second, the returned size is just comp_size out of the file header, checked
against the allocated buffer by a _dbg_assert_ that isn't there in release. That
check is a real one now, folded into the existing sanity test next to it.
pspautotests cpu 11/11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
* Rename LogType to Log
* Explicitly use the Log:: enum when logging. Allows for autocomplete when editing.
* Mac/ARM64 buildfix
* Do the same with the hle result log macros
* Rename the log names to mixed case while at it.
* iOS buildfix
* Qt buildfix attempt, ARM32 buildfix