Commit Graph
50389 Commits
Author SHA1 Message Date
Henrik Rydgård 5327f828ec Merge pull request #22940 from hrydgard/hle-hardening
HLE: Range-check buffers and structs from game arguments
2026-11-06 10:01:54 +01:00
Henrik Rydgård cca8b518ca Merge pull request #22953 from hrydgard/libretro-cache-iso-in-ram
libretro: Make "Cache Full ISO in RAM" work again, only offer it on desktops
2026-11-06 09:49:14 +01:00
Henrik Rydgård df65149a08 Merge pull request #22952 from hrydgard/zlib-1.4
Update our vendored copy of zlib to 1.4
2026-11-06 09:38:16 +01:00
Henrik RydgårdandClaude Opus 5.5 674bf9d29d libretro: Only offer "Cache Full ISO in RAM" where it works
The option is left out of the core options on mobile, Switch, UWP and
32-bit builds, where the cache isn't used. One macro decides both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 09:27:08 +01:00
Henrik RydgårdandClaude Opus 5.5 5f6db5a178 libretro: Make "Cache ISO in RAM" work again on desktops
Since b05d6171d4, the RAM cache is only used when the platform reports
SYSPROP_ENOUGH_RAM_FOR_FULL_ISO, which the libretro core never did, so
the ppsspp_cache_iso option silently did nothing. Report it on 64-bit
desktops, like the standalone app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 08:38:28 +01:00
Henrik RydgårdandClaude Opus 5.5 009faf8f61 Update our vendored copy of zlib to 1.4
Mostly security fixes: a gzwrite() buffer overflow (CVE-2026-85091),
and several out-of-bounds reads and writes in inflate, inflateBack and
deflate on unusual call sequences or invalid data.

Taken from the signed release tarball. Our only local change, the
<unistd.h> include in gzguts.h, is carried over. The library source
file list is unchanged, so no build files needed updating.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:17:04 +01:00
Henrik Rydgård ee714567ee Merge pull request #22947 from hrydgard/gpu-hardening
GE, texture packs, shaders: Bound memory access from GE commands and shared content
2026-11-06 01:07:57 +01:00
Henrik Rydgård 8cbde31a5a Merge pull request #22949 from Nemoumbra/nocash-map-fix
Wrong file type for the Symbol Map in ImDebugger
2026-11-06 01:04:35 +01:00
Henrik Rydgård 75c35f99e2 Merge pull request #22950 from hrydgard/imdebugger-func-scan
ImDebugger: Add scan/remove functions in range, reapply replacements
2026-11-06 01:04:22 +01:00
Henrik Rydgård dbbc14efc8 Merge pull request #22948 from hrydgard/more-ui-work
More UI polish
2026-11-06 00:48:31 +01:00
Henrik RydgårdandClaude Opus 5.5 8f64422929 ImDebugger: Add scan/remove functions in range, reapply replacements
The Win32 Edit Symbols dialog was the only UI that could do a function
scan or a range removal, and so the only way to rerun ReplaceFunctions().
The disassembly context menu now has a range popup (prefilled from the
selection or the current function) with Scan, Remove and Remove and
rescan, plus a "Reapply function replacements" item.

The range logic moves into MIPSAnalyst::ScanFunctionsInRange() and
RemoveFunctionsInRange(), shared with the WebSocket debugger and the
Win32 dialog. That also fixes the dialog scanning to address + size - 1,
an unaligned end that ScanForFunctions() asserts on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 00:43:56 +01:00
Nemoumbra a78b5fc2af fix: .ppmap -> .sym in ImDebugger 2026-11-06 02:32:03 +03:00
Henrik Rydgård 3039c4ea96 Merge pull request #22946 from hrydgard/misc-hardening
Lang files, 7z, sound effects: Bound what downloaded content can do
2026-11-05 22:49:58 +01:00
Henrik Rydgård fa395a4f31 PauseScreen layout tweaks 2026-11-05 22:39:56 +01:00
Henrik Rydgård c3c6327294 More layout tweaks 2026-11-05 22:28:26 +01:00
Henrik Rydgård 89a24813b3 Merge pull request #22944 from hrydgard/adhoc-hardening
Ad hoc: Bound what the server and peers can make us store
2026-11-05 22:12:48 +01:00
Henrik Rydgård 6c624dbdda Fix displayed icons for homebrew without icons 2026-11-05 22:10:48 +01:00
Henrik RydgårdandClaude Opus 5.5 d7d42ad95a UI: Keep squeezed text from wrapping due to float rounding
The squeeze scale is availWidth / width, and multiplying the width back by it
could land a hair above availWidth (189.00002 for "PPSSPP Forums" on the credits
screen), so the text got wrapped despite being scaled to fit. Shave off 0.1%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:03:38 +01:00
Henrik Rydgård 1bce9352fb Layout tweaks 2026-11-05 21:58:56 +01:00
Henrik RydgårdandClaude Opus 5.5 ceab9b7a3f Texture packs, shaders: Bound what shared content can ask for
- textures.ini hashranges with a source over 1024 are rejected. A height
  of 65536 or more spilled into the width bits of the key, matching a real
  texture with a range bigger than it, which overflowed the upload buffer
  in CopyLevelTo and the save buffer in NotifyTextureDecoded. Both now
  also check the sizes themselves.
- A '|' in a textures.ini filename split into another mip level after the
  parent dir check, so it's rejected.
- Post-processing and texture shader inis can't point outside their own
  folder, and a texture shader's ConstantBuffer must be a valid uniform
  buffer size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:21:22 +01:00
Henrik RydgårdandClaude Opus 5.5 05cca98c31 GE: Bound block transfers, indexed draws and list reads to valid memory
- Block transfer sizes counted the stride in pixels rather than bytes, so
  the range check covered a quarter of a 32-bit copy and the memcpy wrote
  past the end of RAM or VRAM.
- Indexed draws, curves and bounding boxes now check the index buffer and
  every vertex the indices can reach (IsValidDrawRange). Only the start
  address was checked before.
- The run loop's downcount stops at the end of memory, and a list that
  runs off the end errors out. The PRIM fast loop, its CALL, the matrix
  fast-loaders and END's look at the previous word are bounded the same way.
- sceGeListUpdateStallAddr with an unaligned address no longer hangs the
  emulator, since the stall is now word aligned like the PC.
- The software renderer leaves out rows that would reach past the end of
  VRAM's mirrors with a wide 32-bit framebuffer stride.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:21:22 +01:00
Henrik RydgårdandClaude Opus 5.5 f039116483 WordWrapper: Count the space before a word when checking if it fits
DirectWrite leaves trailing whitespace out of measured widths, so x_ (measured
on a line ending in a space) came out one space short. A word overflowing by
less than that stayed on the line, and the hard newline after it then wrapped
too, leaving an empty line. Re-measure the line with the word on it, and don't
wrap before a word with no width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:14:33 +01:00
Henrik RydgårdandClaude Opus 5.5 2d30b81543 7z, sound effects: Bound memory a crafted file can take
- A 7z folder that can't be streamed (BCJ2, filter chains) is extracted
  whole into memory at the size its header claims. Refuse folders over
  256 MB rather than run out of memory.
- When streaming, the LZMA and LZMA2 dictionaries are clamped to the
  folder size. The header can ask for up to 4 GB.
- Custom MP3 sound effects decode at most 60 seconds, and a sample file
  shorter than 4 bytes (or too large) is rejected without reading past it
  or leaking it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:12:21 +01:00
Henrik RydgårdandClaude Opus 5.5 5a569135dd Lang files: Don't use translations as printf formats
Translated strings that were printf formats now use %1, %2, %3 and go
through ApplySafeSubstitutions, which fills them in without interpreting
anything else. Lang files can come from downloads, and a value like
"%s%s%s%n" would otherwise crash or write memory. The lang file keys and
values are converted to match, and "%0.2f Hz" is merged into "%1 Hz".

Slider format strings still go through snprintf, so their check now
requires exactly one conversion of the expected type, which keeps out
%n and friends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:12:21 +01:00
Henrik RydgårdandClaude Opus 5.5 dacccd29bb UI: Wrap RadioButton's title text
Measure it with MeasureTextRect against the available width, like Choice does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:07:27 +01:00
Henrik Rydgård bcfcdb9974 Improve the look of the MemstickScreen 2026-11-05 20:03:37 +01:00
Henrik Rydgård f82a875d17 Merge pull request #22945 from hrydgard/net-hardening
Web server, HTTP client, inet: Check Origin and bound what the network can make us do
2026-11-05 19:41:22 +01:00
Henrik RydgårdandClaude Opus 5.5 0b43ea3272 Ad hoc: Bound what the server and peers can make us store
The friend finder server and other players decide how long several of
our lists get, and none of them had a limit:

- Free the group and friend lists and the matching stacks iteratively.
  A server could send enough scan results to overflow the stack.
- Cap the friend list and scan results at 256, and game mode members
  at ADHOCCTL_GAMEMODE_MAX_MEMBERS.
- Game mode: drop datagrams larger than the receive buffer (one would
  sit at the head of the queue forever) and cap the queue.
- Bound each pass draining a PDP socket, and NetSim's held datagrams,
  so a flood can't stall the emulator thread.
- Matching: cap the peer list and the per-MAC port map, and ignore a
  BIRTH that would take the parent past maxpeers.
- Server: don't add every unknown product code to the product table,
  allow 16 connections per IP, give a connection 30 s to log in, and
  terminate client nicknames before logging them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:49:10 +01:00
Henrik RydgårdandClaude Opus 5.5 59b42924b5 Inet, resolver: Check restored waits, bound lookups
- A wait restored from a savestate is retried with arguments nobody
  checked: validate them like the entry points do before each retry.
- Fail recvmsg with EMSGSIZE beyond 1024 iovecs (UIO_MAXIOV).
- Windows: a failed socket() is INVALID_SOCKET, which isn't < 0.
- infra-dns.json: skip non-string domain entries, which crashed.
- Resolver: read the host name only up to the end of memory, and try at
  most twice, since each attempt blocks the emulator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:49:10 +01:00
Henrik RydgårdandClaude Opus 5.5 63a9a53bf9 HTTP client: Cap response size and redirects
A server could make us buffer a response until we ran out of memory,
inflate a small gzip body to 256 MB, or redirect forever.

- Responses are capped at 64 MB in memory and 1 GB for downloads to a
  file (which are buffered in memory too), over both the plain and the
  HTTPS client. A larger Content-Length fails up front.
- Gzip output gets the same cap.
- Remote ISO range reads accept no more than the range asked for.
- Stop after 10 redirects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:49:10 +01:00
Henrik RydgårdandClaude Opus 5.5 bea3fcdeab Web server, debugger: Check Origin, bound connections, headers and buffers
Browsers let any page open a WebSocket to localhost, and the debugger can
write memory and save states to any path. Accept only pages from this
machine, from this server, or the official web debugger; clients that
send no Origin are unaffected.

- Cap request headers at 100 lines, 64 KB and 10 seconds, and stop
  leaking repeated User-Agent and Referer values. Header names are now
  matched exactly, not by prefix.
- Run at most 64 connections at once, and drop accepted sockets that
  select() can't take (fd >= FD_SETSIZE).
- Grow a WebSocket message as its data arrives, instead of to the
  length the frame claims.
- Drop a WebSocket client that stops reading, once 128 MB is queued,
  and stop waiting for a close frame that can't go out after 2 seconds.
- Uploads wait for data instead of spinning, and range downloads stop
  when the client stops reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:49:10 +01:00
Henrik RydgårdandClaude Opus 5.5 d756893c1c UI: Give RadioButton the same padding and height as CheckBox
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:48:11 +01:00
Henrik RydgårdandClaude Opus 5.5 28eddf4f6b rcheevos: Update the build files for the new source layout
Upstream moved the sources into api/, client/, runtime/ and util/
directories, renamed most of them, and split rc_client.c into several
files. The lists now match upstream's src/Makefile.common, which also
drops rc_validate.c (nothing calls it).

Also follow the API rename of core/unofficial achievements to
promoted/unpromoted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:35:42 +01:00
Henrik Rydgård c936b544cf rcheevos: Bump to top of tree to get security fixes 2026-11-05 18:28:39 +01:00
Henrik RydgårdandClaude Opus 5.5 70e0782da8 UI: Draw RadioButton with the I_RADIO_* atlas images
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:23:42 +01:00
Henrik RydgårdandClaude Opus 5.5 94a65a1ff7 UI: Let RadioButton show a hint, like CheckBox
RadioButton is now a ClickableItem, so it fills its parent's width by
default and draws its hint text below the title, lined up with the text
after the radio circle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:20:33 +01:00
Henrik Rydgård 1085fcc820 Fix icon size problem with directory, add icon for Restore Default button on speed hacks screen 2026-11-05 18:08:08 +01:00
Henrik RydgårdandClaude Opus 5.5 68f8c061ee docs: Note that sceNet_lib is experimental and out of scope for reviews
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:21:36 +01:00
Henrik RydgårdandClaude Opus 5.5 f4b99a6a99 Ad hoc, PMP: Bound guest-sized buffers and lists
- Game mode master and replica data was copied by the game's size after
  checking one byte, into host buffers allocated by that size (up to 2 GB).
  If the copy failed, the uninitialized buffer was sent to peers.
- sceNetAdhocctlCreateEnterGameMode read up to 16 member MACs after checking
  one byte.
- sceNetAdhocMatchingCreate allocated and cleared the game's receive buffer
  size on the host with no upper bound. Cap it at 1 MB.
- PMP video walked its block descriptors with only the first one checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:21:36 +01:00
Henrik RydgårdandClaude Opus 5.5 917607d8fa sceIo, savedata, fonts: Check whole structs and arrays, not first bytes
- Block-mode sceIoRead counts its size in sectors, but was clamped to memory
  in bytes, so a large count read size * 2048 bytes past the buffer.
- sceIoDread wrote the short and long names to d_private after checking one
  byte; the PGD key and sector seek ioctls read 16 bytes after one.
- Savedata: GetSize walked the game's entry arrays by its counts (unchecked),
  GetList and GetFilesList wrote entries past the first unchecked, and Save
  read the data and icon/pic/sound buffers by their sizes without checking
  them. Also keep the encrypted size from going negative.
- sceFont: font style, font info and char info structs, and the font list,
  were only checked at their first byte. Style names in logs needn't be
  terminated.
- PGF glyph rendering wrote 3-4 bytes per pixel after checking one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:21:36 +01:00
Henrik RydgårdandClaude Opus 5.5 94469b7592 GE, display: Range-check contexts and display strides
- sceGeSaveContext/RestoreContext and list contexts from sceGeListEnQueue
  save and restore 512 words, but only their first bytes were checked.
- The display range check computed (271 * stride + 480) * bpp in 32 bits.
  sceDisplaySetFramebuf allows any multiple of 64, so a huge stride wrapped
  the size to a few bytes, passed, and the display was then read far past
  memory. Compute it in 64 bits, here and in the software renderer, which
  also checks its debug framebuffer copy (used for screenshots) and doesn't
  create textures for strides over 4096.
- Frame dump replays check a DISPLAY command like sceDisplaySetFramebuf, and
  bound the drawn-area tracking a crafted dump could make quadratic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:21:36 +01:00
Henrik RydgårdandClaude Opus 5.5 1021767568 HLE utility libraries: Range-check buffers and contexts
These checked the first byte of a buffer the game sized, then read or wrote all
of it on the host:

- sceDeflt: the output length went straight to zlib.
- sceSfmt19937: array end checks wrapped in 32 bits (FillArray32 with a huge
  count wrote gigabytes), and the 2.5 KB state struct was only checked at its
  start. The same for the MT19937 contexts in sceMt19937 and
  sceKernelUtilsMt19937*.
- sceChnnlsv: sceSdRemoveValue took any length (a negative one passed the
  key length check), sceSdSetMember decrypted alignedLen bytes in place, and
  the 16-byte hash, key and data buffers were checked for one byte.
- MD5, SHA-1 and SHA-256 digests: check the data length and digest size
  (SHA-256 checked data + len, which wraps).
- sceCcc: clamp the destination size to memory, and require the source to be
  terminated in mapped memory, since the converters walk it to its end.
- sceRtc: the RFC formatters write 32 bytes, the Win32 file time functions a
  whole date struct, and the date parser needs a terminated string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:21:36 +01:00
Henrik RydgårdandClaude Opus 5.5 b1ffcde9fc Audio, PSMF: Check the whole range the decoders touch
- Atrac: the partial-frame buffer was sized from the codec field in the guest's
  context, which a game can change after SetData, so an Atrac3+ decoder could
  write 2048 samples into a 1024-sample buffer. Size it for any frame, and
  decide from the decoder whether a frame fits the output. Check the output
  buffer holds a frame and the input holds the frame read, not just their
  first bytes, and range-check the streaming buffer copy.
- Atrac track parsing is bounded by mapped memory, not only the game's size.
- sceMp3LowLevelDecode and sceAudiocodecDecode check their output holds a
  frame, and the MP3 input is clamped. FindMp3Header read 3 bytes past its
  range.
- PSMF: the EP map address could wrap around to a valid one while the entries
  were read from the unwrapped host pointer, and scePsmfSetPsmf checked one
  byte of the 0x82-byte header it reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:21:36 +01:00
Henrik Rydgård 5b3ddcc062 Merge pull request #22942 from hrydgard/update-libchdr
Update libchdr to the latest master
2026-11-05 17:19:57 +01:00
Henrik Rydgård 4866f356b5 Merge pull request #22943 from hrydgard/psx-graphics
POPS: Upload images the emulator writes to VRAM with the CPU
2026-11-05 17:19:15 +01:00
Henrik Rydgård c338f76ec9 Merge pull request #22939 from hrydgard/parser-hardening
Harden file format parsers against malformed input
2026-11-05 16:29:51 +01:00
Henrik RydgårdandClaude Opus 5.5 0097c2a3d8 Build all of lzma-sdk in Common on Windows, UWP and Android
libchdr's bundled LZMA now builds only LzmaDec, with its symbols renamed
to CHDR_*, so the 7z reader no longer gets Bra, Bra86, Delta and LzmaDec
from it. Build the same lzma-sdk sources ext/lzma-sdk/CMakeLists.txt does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:28:45 +01:00
Henrik Rydgård 697396c08b Merge pull request #22938 from hrydgard/libretro-inflight-3
libretro: Offer "Up to 2" buffered frames of graphics commands again
2026-11-05 16:08:32 +01:00
Henrik RydgårdandClaude Opus 5.5 ac4fb587fe POPS: Find the GP0 image load's row ends in every emulator version
The registers and the gp offset of the loop's state differ between
firmware versions and models (pops.prx and pops_0Xg.prx from 3.80 to
6.61), so it's matched by the shape of its four instructions, and the
offset is taken from them and kept in savestates. The exact words only
matched 6.60 and 6.61's 01g to 04g.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:08:25 +01:00
Henrik RydgårdandClaude Opus 5.5 05de369578 POPS: Upload images the emulator writes to VRAM with the CPU
The PS1 emulator writes an image that comes through the GP0 data port
(rather than by DMA) into VRAM one pixel at a time with the CPU. The
hardware renderers never saw those pixels, so a later copy out of the PS1
VRAM framebuffer read stale texels.

The two row ends of that loop now go to an HLE call, which uploads the
finished rectangle with a new PerformWriteRectFromMemory().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:08:25 +01:00