Opening a .pkg now offers to install it, the way a .zip does - see the new
InstallPkgScreen, which shows what the update patches, what it'll take up
on disk (exact, since package contents aren't compressed) and where it's
going. The package's PS3-style USRDIR/CONTENT wrapping is stripped so the
files land where the PSP expects them, in PSP/GAME/<DISC_ID>.
Booting a disc then looks for PSP/GAME/<DISC_ID>/PBOOT.PBP and boots that
instead of the disc's own EBOOT, leaving the disc mounted - so the update
overrides the files it ships and the disc supplies the rest. The update's
DISC_ID has to match; a DISC_VERSION mismatch only warns, since updates do
get used with slightly different dumps in practice.
Verified against the whole corpus: every one installs, and the
digital NP* update/base-image pairs that could be assembled all boot the
patch rather than the disc's executable. That includes Super Robot Taisen
Operation Extend from a real NPUMDIMG EBOOT.PBP, which settles that
ISO.BIN.EDAT does not re-key the PBOOT - a digital title's patched EBOOT is
encrypted exactly like a UMD one. On the UMD side, the patched
LittleBigPlanet reads PATCH.ARC out of the install alongside the disc's own
archive.
The DISC_VERSION warning turns out to be load-bearing: many of the pairs
mismatch, because the dumps in circulation are later disc revisions than the
updates were built against. docs/pkg_notes.md has the numbers, and the one
thing that doesn't work - PGD-wrapped .sprx modules, which
sceKernelLoadModuleNpDrm can't load.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GameManager::GetISOGameID() and GameInfoCache::ReadFileToString() resized host
buffers directly from the size reported by the ISO directory record, which is
untrusted. A crafted image can advertise a huge PARAM.SFO or icon and cause an
excessive allocation just from inspecting the file in the game list.
Cap both at a size well above anything real, and skip the read instead. Follows
the pattern PSARUnpack::ReadWholeFile already uses.
Taken from #22209.
zip_get_name() returns NULL on a corrupted central directory entry;
several call sites passed its result straight into endsWith(),
std::string construction, or the fileAllowed() lambda (which calls
HasParentDirComponent()/strchr()/strrchr() on it) without checking -
undefined behavior on a malformed/malicious zip (game/homebrew/texture
pack installs, or a downloaded ISO zip).
zip_stat_index() can likewise fail, leaving its output zip_stat
uninitialized; ZipReadFileByIndex, DetectTexturePackDest, and
ExtractFile all used the result (zstat.size, to size a buffer or check
a texture-pack size limit) without checking the call succeeded first,
using an initialized zip_stat via zip_stat_init() so the failure case
reads a known-zero size instead of stack garbage that could drive a
huge buffer.resize() or bypass the texture-pack size limit check.
ZipReadFileByIndex also didn't check zip_fopen_index()'s return before
passing it to zip_fread().
ExtractZipContents wrote every entry's declared size with no ceiling, so
a small high-ratio zip could decompress to fill the storage device.
- Add a maxTotalSize parameter to ExtractZipContents (default 4GB) and
ExtractFile.
- Bail out in the size-summation pass when the total declared size
exceeds the limit, and again per write chunk in case declared sizes
are inaccurate.
A crafted zip with a parent-directory ("..") entry name could escape the
destination directory during extraction, writing arbitrary files on the
host (e.g. into startup/autostart folders). ExtractZipContents built the
output path by concatenating the raw zip entry name onto the destination
with no traversal check.
Changes:
- Add HasParentDirComponent() utility in Core/Util/PathUtil and use it in
GameManager::ExtractZipContents to reject entries with a ".." component.
Guard both the directory-creation and file-writing passes.
- Expose ExtractZipContents as public for testing.
- Add unittest/TestZipSlip which crafts a zip with a "../evil.txt" entry
and verifies it is not written outside the destination directory.
* 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