Commit Graph
532 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 879baa6412 Add unit tests for BlockAllocator
BlockAllocator backs sceKernelAllocPartitionMemory and the various partition
allocators, and had no coverage at all. It's pure address bookkeeping - no real
memory - so it can be checked hard: after any sequence of operations the blocks
must still tile the range exactly, with no gaps, overlaps or strays, and
GetTotalFreeBytes/GetLargestFreeBlockSize must agree with what's really in the
list. ValidateAllocator() rebuilds the list through the public accessors and
asserts all of that, and it runs after every step of the randomised sections.

Covers bottom-up and top-down allocation, grain rounding, rejection of zero and
oversized requests, splitting and re-merging on free, double free and free of an
address that was never allocated, AllocAt including unaligned positions and
collisions, AllocAligned with alignments coarser than the allocator's grain,
filling the range completely and draining it, a range whose size isn't a
multiple of the grain, and two randomised churn loops - one plain alloc/free,
one mixing in aligned allocations and AllocAt to reach block layouts the simple
loop never produces. Fixed seeds, so a failure reproduces.

No bugs found - the allocator holds up. Verified the tests aren't vacuous by
injecting two plausible bugs: dropping the forward merge in MergeFreeBlocks, and
an off-by-one in the bottom-up fit check. Both are caught, the second pinpointed
to a specific churn iteration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 14:50:51 +02:00
Henrik Rydgård 5cffcd34ad Get rid of the confusing old USING_WIN_UI define. Make a more clear system property for headless. 2026-08-16 12:19:41 +02:00
Henrik Rydgård f5bd302694 Improve DescribeAddress, show the description of the currently selected line in disassembly 2026-08-14 14:38:33 +02:00
Henrik Rydgård c5e4d0d90d Rename the get-memory-pointer functions to make it clear where CPU exceptions can happen. 2026-08-12 14:06:16 +02:00
Henrik Rydgård 9315c0953a GamepadEmu: bounds-check touch pointer IDs before use
TouchInput::id was used directly to index the global primaryButton[]
array (MultiTouchButton::Touch) and to shift pointer bitmasks
(PSPDpad/PSPStick/PSPCustomStick/GestureGamepad::Touch), guarded only
by a debug-only assert in one of the five call sites - a no-op in
release builds. input.id isn't always a small sequential slot in
[0, TOUCH_MAX_POINTERS): SDL assigns SDL_FingerID values directly,
Android pointer IDs can go up to 31, and UWP's TouchMapper allocates
one more slot (11) than TOUCH_MAX_POINTERS (10) and can also return -1
when it runs out of slots - all reachable through ordinary multi-touch
use, no malicious input required.

Also apply bounds check to the PER_GAME gesture config ints
(iDoubleTapGesture/iSwipeUp/Down/Left/Right) before indexing
GestureKey::keyList[] with them.

Additionally, minor cleanup on Android and moves the TouchMapper helper
out from UWP to InputState.h.
2026-08-12 09:47:13 +02:00
Henrik Rydgård 4d8a5d74e7 Merge pull request #22086 from hrydgard/read-u32-more
Some fixes to Claude's paranoia, more memory access function cleanup
2026-08-11 23:52:17 +02:00
Henrik Rydgård 2d0e54f422 Headless (and main): Improve crash reporting 2026-08-11 22:36:47 +02:00
Henrik Rydgård 0596ee97f6 More memory access cleanup 2026-08-11 20:14:01 +02:00
Henrik Rydgård cd052ea640 Adjust the level of Claude-based paranoia here and there 2026-08-11 20:08:01 +02:00
Henrik Rydgård a3e0f915af Claude being too cautious. Let's just reject bad values fully. 2026-08-11 20:08:01 +02:00
Henrik Rydgård c5849ada33 GameDB: validate CSV structure before indexing into it
LoadIfNeeded() indexed lines[0] unconditionally after splitting the
bundled CSV into lines, crashing if the file were empty. It also used
GetColumnIndex()'s result directly as an index into each row's fields
without checking for its (size_t)-1 "not found" sentinel, which would
have produced a huge out-of-bounds index on every row if any of the
expected column headers were missing. Both require a corrupted or
replaced redump.csv asset to trigger, but are simple, cheap checks to
add.
2026-08-11 08:43:52 +02:00
Henrik Rydgård eb68bdba0b BlockAllocator: fix null deref in GetBlockTag, bound DoState's block count
GetBlockTag() dereferenced GetBlockFromAddress()'s result without a
null check, unlike every other accessor in this file. Callers
(NetAdhocCommon.cpp, sceNet.cpp, sceNetAdhocMatching.cpp) pass the
result straight into strcmp() while recovering from a stale address
left over from an old/corrupted savestate - precisely the situation
where the address may no longer resolve to a block. Return "" instead
of dereferencing a null block, so strcmp() simply reports a mismatch
(correctly triggering those callers' recovery path) instead of
crashing.

DoState() read the saved block count directly from the savestate with
no validation before looping that many times allocating Blocks - a
corrupt/malicious savestate claiming an enormous count would drive an
effectively unbounded allocation loop. Clamp it to how many block
records could plausibly still fit in the remaining stream data.
2026-08-11 08:43:52 +02:00
Henrik Rydgård 6a7c9d35aa GameManager: check libzip return values before using their results
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().
2026-08-11 08:43:52 +02:00
Henrik Rydgård afbc73bb68 AtracTrack: fix OOB reads and unbounded allocation in header parsing
These parsers run on fully game-controlled buffers (reachable via the
various sceAtracSetData*/sceAtracSetHalfwayBuffer* HLE calls), so a
malicious/malformed game can supply arbitrary bytes here:

- AnalyzeAtracTrack's RIFF chunk-walking loop computed `offset +=
  chunk + (chunk & 1)` (all in 32-bit) and only bounds-checked
  afterwards. A crafted chunk size could wrap `offset` (and the
  `offset + 12` check itself) around, bypassing the bounds check
  entirely - Read32(), whose offset parameter is a plain `int`, would
  then read from a wild pointer far outside the buffer. Do the
  validation in 64-bit before mutating offset, mirroring the pattern
  already used by the newer ParseWaveAT3 parser.
- AnalyzeAA3Track validated `size >= tagSize + 36` but then read up to
  relative index 35 after rebasing by 10+tagSize - i.e. absolute index
  tagSize+45, 10 bytes past what was actually checked.
- The SMPL chunk's loop count (checkNumLoops) was only checked for
  being negative, not bounded against the chunk's actual size, so a
  crafted value near INT_MAX would drive an unbounded (up to ~2
  billion entry) vector::resize() - an easy crash/OOM. The same
  unclamped value also let the fill loop below run past the end of
  the chunk, since its bound compares the loop counter to chunkSize
  rather than the byte offset actually being advanced (24 bytes/loop).
  Clamping checkNumLoops to what the chunk can actually hold fixes
  both.
- ParseAA3Headers checked for at least 9 bytes but the "ea3"/"id3"
  branch it guards reads up through byte index 9, needing 10.
2026-08-11 08:43:52 +02:00
Henrik Rydgård abb57c620f Adjust some log levels 2026-08-09 22:10:54 +02:00
Henrik Rydgård 07e63c345c Add SAVE_STATE to BrowseFileType 2026-08-09 19:03:08 +02:00
Henrik Rydgård db8ff4391e Enable ImDebugger everywhere, on top of the regular UI. 2026-08-07 17:35:15 +02:00
Henrik Rydgård c1310ae4a5 Rework UPnP service thread sync
No longer waking up twice per second. Instead a slow 5s poll plus
wakeups if needed.
2026-08-07 11:24:22 +02:00
Henrik Rydgård edd968062a Correct the Atrac3 security check that broke music in GTA
See 4cd611b71f
2026-08-05 00:15:38 +02:00
Henrik Rydgård 68ec34ad54 Build and warning fixes 2026-08-03 19:11:07 +02:00
Henrik Rydgård 55eee26c52 Assorted warning fixes 2026-08-03 18:55:35 +02:00
Henrik Rydgård 725cd691b3 Limit total extracted size when installing zips (zip bomb protection)
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.
2026-08-01 13:16:21 +02:00
Henrik Rydgård 0eef5d0164 Use shared HasPathTraversal utility for savedata name validation
Replace the inline lambdas in the savedata dialog with the new
HasPathTraversal() helper in Core/Util/PathUtil.
2026-08-01 13:16:21 +02:00
Henrik Rydgård 4cd611b71f Fix out-of-bounds reads in ATRAC track parsing
AnalyzeAtracTrack used max(fileSize, size) as the chunk-parse bound with
fileSize taken from the file's RIFF header, so a crafted inflated RIFF
size could push reads past the end of the buffer. Keep the real-library
behavior of tolerating a too-low size, but clamp the parse bound to the
actual mapped guest memory at the buffer.

Also guard ParseWaveAT3's RIFF scan against a blockSize < 4 underflow
that could make the offset negative and bypass the loop bounds check, and
clamp readSize to the mapped region in Atrac2::SetData before parsing.
2026-08-01 11:57:27 +02:00
Henrik Rydgård 5d10f281ef Fix integer overflow in PMF video frame buffer allocation
pmf_init reported stream dimensions without a cap, and PMFView::Draw
allocated width * height * 4 with 32-bit int arithmetic, so a crafted
ICON1.PMF could overflow the allocation to a small buffer while
sws_scale wrote the full frame.

- Reject videos with dimensions outside 1..720x480 in pmf_init (PMFs on
  the PSP never exceed 720x480).
- Use size_t arithmetic for the frame buffer allocation.
2026-08-01 11:57:27 +02:00
Henrik Rydgård f3d7d8bc0c Fix Zip Slip in zip extraction and add unit test
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.
2026-07-31 20:35:12 +02:00
Henrik Rydgård d6e0308c11 Merge pull request #21990 from Arkadyzja/savestate-compact-blockallocator
Savestate: compact BlockAllocator serialization (section v2)
2026-07-30 17:23:25 +02:00
Henrik Rydgård 2b509e4b1e Minor cleanups, revert change in sceKernelMbx 2026-07-29 20:30:17 +02:00
Ren 32a1b80977 Savestate: compact BlockAllocator serialization (section v2)
Serializing the kernel memory block lists (userMemory, kernelMemory,
volatileMemory) wrote a full Section header per block and re-zeroed
each block's tag padding with strlen+memset on every save. Games keep
on the order of a thousand blocks alive, so the per-block overhead is
both measurable save time and wasted payload bytes.

Zero-pad tags once at write time instead (Block constructor and
SetAllocated), so the v2 form can store blocks raw: start, size, taken,
tag - no per-block section machinery, no per-save tag scrubbing.
Uninitialized padding still never reaches the stream, since every path
that writes a tag now clears it first.

v1 states still load through the old per-block-Section form, which is
kept unchanged.
2026-07-27 16:21:34 +02:00
Henrik Rydgård 95ce95e489 Add a new "Net" log category, do assorted cleanup 2026-06-13 14:43:59 +02:00
Henrik Rydgård f60e27a9b7 Just some refactoring of the GPUStatistics struct, and more use of StringWriter 2026-05-29 14:40:31 +02:00
Henrik Rydgård bfe6debcfd Support auto-installing plugins from ZIPs with a deeper directory structure 2026-04-14 11:55:13 -06:00
Henrik Rydgård 288b72cb16 Add 7z streaming support, so we can have a progress bar 2026-03-27 14:34:18 -06:00
Henrik Rydgård 5e0a9a0113 Add really simple 7z extraction, using the 7z VFS 2026-03-27 14:34:18 -06:00
Henrik Rydgård 64c05a5ddf Atrac3: Allow sending 0xFFFFFFFF as data size. Death JR does this on audio loop for some reason. 2026-03-26 11:59:43 -06:00
Henrik Rydgård 0e55129fab Prepare for dumping NPDRM isos, use shared_ptr to manage lifetime of BlockDevice 2026-03-19 13:59:04 +01:00
Eric Warmenhoven 9b26df8283 libretro: fix ios/tvos builds 2026-03-08 12:16:52 -04:00
Henrik Rydgård a2de96a4d1 iOS: Try to avoid leaking file bookmarks. Don't try to install zips into iCloud folder. 2026-03-08 10:20:24 +01:00
Henrik Rydgård 14ef83f06a Implement iOS "bookmark" support, to preserve permissions to recent files from iCloud 2026-03-07 15:04:49 +01:00
Henrik Rydgård d6ca0378b6 Move DarwinFileSystemServices.mm/h to Core/Util 2026-03-07 11:35:34 +01:00
Henrik Rydgård 66b8e866fb iOS: Fix opening files from iCloud and other secure places. Note: Not compatible with "Recents" yet. 2026-03-07 09:19:31 +01:00
Henrik Rydgård e8d648e91d InstallZip: Fix problem where the delete-after option didn't work
The file was held open.
2026-03-06 10:57:22 +01:00
Henrik Rydgård e50199ad0a Fix the gemini-written video player to actually work 2026-02-26 09:33:26 +01:00
Henrik Rydgård c74e21243d Add support for installing plugins directly from zip files 2026-02-25 14:28:57 +01:00
Henrik Rydgård cbcec3e27f Bugfix GetFriendlyPath, use in more places 2026-01-29 23:15:32 +01:00
Henrik Rydgård 1ed2f53385 Improve the InstallZipScreen a little 2026-01-29 22:51:28 +01:00
Henrik Rydgård 0f85b06908 Add basic support for installing savestate ZIPs (useful for iOS testing) 2026-01-29 22:10:46 +01:00
刘皓 51e3eb69af Remove all calls to zip_close() from Core/Util/GameManager.cpp
The zip file will be closed automatically when the `ZipContainer` goes
out of scope, or when `.close()` is called on the `ZipContainer`.
2026-01-08 11:49:05 -05:00
Henrik Rydgård fb521408c7 Avoid calling IsValidRange with zero length 2025-12-30 20:31:07 +01:00
Henrik Rydgård 1ea93e2913 Small refactor of text rect measurement. Add move/resize icons 2025-11-27 11:50:22 +01:00