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>
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>
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>
- 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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
new (std::nothrow) doesn't work with DbgNew.h's new macro in Windows debug
builds. The buffer is only ever freed in two places, so switch them to free().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picks up upstream's hardening against crafted CHD maps and codec payloads
(rtissera/libchdr#198), hunk CRC verification for v3/v4 maps, and RLE/sparse
map fixes.
Upstream split libchdr_chd.c into a file per codec, and its bundled LZMA is now
just a symbol-namespaced LzmaDec.c, so the source lists change in every build.
The zlib codec now defaults to a bundled miniz, so define CHDR_SYSTEM_ZLIB and
CHDR_SYSTEM_ZSTD to keep using our zlib and zstd.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- textures.ini skipLastDXT1Blocks* is subtracted from the hashed size after
the memory check, so a large value (or a small one with reduceHash)
underflowed it and hashed ~4 GB. Clamp it to the blocks there are.
- SND0.AT3 loops with a negative or backwards range pointed playback before
the data. Ignore those, and loops outside the data.
- impose.rsc sprite bounds were checked as x + w in 32 bits, which wraps.
- Store entries are also uninstalled by name, so require a plain filename
(. or / was the whole GAME directory).
- Headless screenshot compare read (and converted in place) w x h pixels of a
reference PNG that could be smaller.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6bb91bf2f0 removed the option along with lowering the default. The default stays at "Up to 1"; this only
brings back the choice, which the standalone app still has.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- npdrmRead clamped the read with a signed compare against data_size, so a
forged header with data_size >= 2^31 turned it into a ~2 GB copy past the
guest's buffer. Clamp to what's left of the file, unsigned.
- sceDrmBBMacUpdate/Final take a MAC key in guest memory, and only checked
pad_size > 16, so a negative one wrote at an arbitrary offset.
- Repeating the PGD ioctl leaked the previous descriptor, and pgd_open
didn't check its allocation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An extended opcode's length was added to the position unchecked, so a huge
one wrapped back to the opcode itself and looped forever (and, with a row
in the loop, grew the table until memory ran out). Lengths must now fit in
the unit. This runs on every loaded module and on a .elf next to the EBOOT.
- Cap the rows at a couple per instruction and the distinct files at 65536,
since one-byte opcodes and short names could otherwise make gigabytes of
tables from a large file.
- ELF and unit bounds checks in 64 bits (they wrapped on 32-bit hosts), the
section headers copied out rather than read unaligned, a bounded compare
for the section name, and line and SLEB arithmetic that can't overflow.
- Rows at the same address now keep the last, as the comment says.
std::unique kept the first, so an end-of-sequence row could hide the next
sequence's first row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Savestates: containers of non-raw elements (strings, classes) now grow as
their data is read, since a count bounded only by the bytes left could still
ask for gigabytes. Cap the decompressed size at 256 MB and fail cleanly if a
buffer can't be allocated, instead of terminating.
- Symbol maps: a line starting with a NUL underflowed the newline trimming,
which then walked back through the stack writing NULs. These load
automatically from next to the ISO.
- Cheats: stop if an IfAddr skip wraps the line index (32-bit hosts), which
ran the same code forever.
- Atrac: reject negative chunk sizes in ParseWaveAT3 (an infinite loop), and
check the AA3 header and fact chunk are long enough for what's read.
- SND0 loop count check divided rather than multiplied (wrapped on 32-bit).
- Replays: don't index an empty data vector, or report more bytes than were
copied.
- UTF-8: u8_nextchar read past the end when called there, and the emoji check
re-measured its string_view with strlen. StringWriter::F moved backwards on
vsnprintf errors. JSON getInt clamps instead of an undefined cast.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PSAR: an entry's chunk size could wrap the u32 position back to an earlier
entry, looping forever (this runs on a disc's DATA.BIN at boot). Entries
must now end inside the archive.
- MDS: the session offset check wrapped in u32, and the track name block
wasn't checked at all, both reading gigabytes past the file.
- PKG: check the data range without the u64 sum that could wrap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Zip VFS: cap entries at 512 MB (the old 4 GB cap let a 2^32 size wrap the
allocation to nothing on 32-bit hosts), fail reads that come up short
instead of returning uninitialized memory, and return 0 rather than
(size_t)-1 from Read on a corrupt stream, which crashed texture loading.
- Zipped disc images are capped at 4 GB and must fit a size_t before we
allocate them.
- Installing a zipped ISO took the name after the last '/' only, so a name
using backslashes could write outside the games folder on Windows. Require
a safe filename, and report failures instead of always succeeding (which
could delete the source zip after a failed extract).
- Disk cache files: reject block sizes we don't create (0 divided by zero),
drop index entries that share a block (eviction then never finished), and
stop evicting when no progress is possible. Negative or empty reads no
longer index the cache out of bounds.
- HTTP: reject implausible Content-Length values, and don't cache files in RAM
that can't fit (the size wrapped on 32-bit hosts, overflowing the buffer).
- Gzip-encoded HTTP responses are capped at 256 MB decompressed.
- Directory VFS: don't open directories as files, check Ftell, and bound the
case-insensitive index recursion.
- Skip zip entries without a name when identifying zips.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- JPEGs over 8192 pixels in either dimension are rejected (in jpgd, so it
covers sceJpeg too), and its allocation size is computed in size_t, since
the int product overflowed and gave a heap overflow. Image loading also
checks the caller's maximum before decoding.
- Image loading premultiplied a ZIM that failed to load (null pointer), and
treated 16-bit ZIMs as 32-bit (twice the buffer). Reject those, and ZIMs
over 8192, and a zlib stream that ends early.
- PNG: pngLoadPtr's error path could free a caller's uninitialized pointer,
and pngLoad didn't check its allocation or decode.
- Texture replacements: reject header sizes of 0 or over 8192 for every
format (DDS sizes overflowed an int), transcode KTX2 mips with their own
height, and don't copy more ZIM rows than were decoded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>