Commit Graph
22 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 b42a03e095 Add savestate serializer tests, fix three bounds-check bugs
PointerWrap and the Do() overloads around it are how every savestate is
written and read, and had no direct coverage. Everything read back came off
disk, so the corrupt-input paths matter as much as the round trips.

Three bugs, all in the bounds checking added in 58d4759ceb:

1. sizeof(T) is not a lower bound on how many bytes an element serializes to.
   It only holds for the types DoHelper_ writes out raw. A std::string is 32-40
   bytes in memory and serializes to as few as five; a T* serializes to whatever
   T::DoState() writes. So DoVector/DoList/DoSet/DoMap could reject a perfectly
   valid savestate whenever count * sizeof(element) exceeded the bytes left in
   the buffer. That is not hypothetical: pspFileSystem is serialized dead last
   in SaveStart::DoState, and MetaFileSystem::DoState does Do(p, currentDir) on
   a std::map<int, std::string>, so the check runs with only a few hundred bytes
   remaining and claims 44 bytes per entry against roughly 22 actual. Added
   SerializeMinElemSize<T>(), mirroring DoHelper_'s own condition, and used it
   in all five containers. The bound is only loosened, so nothing that loaded
   before can stop loading.

2. Do(p, std::map<K, T *> &) deletes every value before reading the new ones,
   and DoMap then returned on a bad count without clearing - leaving the map
   full of freed pointers to be used or deleted again. Six live maps go through
   this (sceMpeg, sceMp3, sceAac, sceFont, sceHeap, sceKernelThread's pending
   calls), so a corrupt savestate meant a use-after-free. Clear before the guard
   can bail out, in DoMap, DoMultimap and DoSet.

3. The wstring and u16string overloads validated stringLen < 0 but not 0, and
   didn't require a whole number of characters. read() computes
   stringLen / sizeof(char) - 1, so a length of 0 resized to SIZE_MAX and
   memcpy'd with a wrapped-around size. PSPOskDialog::DoState serializes both
   (inputChars at v2, a legacy wstring below that), so this was reachable: the
   test aborts the process without the fix.

The test covers round trips of PODs, strings (empty, embedded NUL), vector,
map, set, list and map-of-pointers, section titles and version gating in both
directions, marker mismatches, measure-vs-write checkpoint disagreement, the
error latch dropping to MODE_NOOP, every truncation of a valid buffer, and
hand-corrupted counts and lengths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 14:50:57 +02:00
Henrik RydgårdandClaude Sonnet 5 60796e08f9 Serializer: fix OOB read loading a std::string from a savestate
MODE_READ built the string via x = (char*)*p.ptr, which strlen()s for
a NUL terminator. CheckRead(stringLen) only guarantees stringLen bytes
are available in the buffer, not that one of them is a NUL - a
corrupted savestate missing the terminator caused strlen to scan past
the checked region. Now uses a length-bounded assign(), matching how
the wstring/u16string siblings already do this correctly via memcpy.
Also tightens the stringLen validity check to require >= 1 (matching
what a real serialized string always has), so stringLen - 1 can't
go negative for a corrupted stringLen of 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4QAoxV2KY7ek4PcZw3WvY
2026-08-10 01:00:28 +02:00
Henrik Rydgård 58d4759ceb Add bounds checking to savestate deserialization
PointerWrap tracked no end-of-buffer, so DoState() implementations could
read past the end of a crafted or truncated savestate via DoVoid's
unchecked memcpy, and DoVector could resize to an attacker-controlled
size before reading.

- PointerWrap now tracks a read end; DoVoid/ExpectVoid fail (MODE_NOOP)
  before reading out of bounds.
- String reads are bounds-checked for the whole string including NUL.
- DoVector rejects sizes that can't fit in the remaining buffer.
- LoadPtr takes the buffer size and sets the read end.
- Capping the decompression buffer allocation in LoadFile.
2026-08-01 11:57:24 +02:00
Henrik Rydgård cf49ff04ca More accurate error reporting for savestate loads 2024-07-20 10:55:16 +02:00
Henrik Rydgård e01ca5b057 Logging API change (refactor) (#19324)
* 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
2024-07-14 14:42:59 +02:00
Unknown W. Brackets e49668eea7 SaveState: Correct missing switch case warnings. 2022-12-04 07:57:30 -08:00
Unknown W. Brackets 0556ee67fc SaveState: Fix a format type error. 2022-12-04 07:57:09 -08:00
Henrik Rydgård c7041d6e97 Reserve some space in the checkpoints vector 2022-12-02 22:46:39 +01:00
Henrik Rydgård f5a7661c51 Add a NOOP state to reduce logspam after error 2022-12-02 22:46:38 +01:00
Henrik Rydgård 52a684644d Verify that the Measure and Save passes match accurately through checkpointing
Adding this after seeing some very suspicious behavior in Ratchet &
Clank.
2022-12-02 22:46:38 +01:00
Henrik Rydgård 237fbca979 Savestate: Prepare some sanity checks 2022-12-02 22:46:38 +01:00
Henrik Rydgård 36ada6308d Sanity check string lengths in save state code 2021-10-07 21:08:46 +02:00
Unknown W. Brackets dc12b3a7c8 SaveState: Use default ZSTD compress level. 2021-08-06 23:46:07 -07:00
Unknown W. Brackets 0097772d3c SaveState: Ask to include xxhash in frames.
Hopefully this should detect any decompression errors better.
2021-08-06 23:42:43 -07:00
Unknown W. Brackets 1e9a391f65 SaveState: Add const for save compression. 2021-08-06 23:39:36 -07:00
Henrik Rydgård 025bcb1673 Introduce Path, start using it all over the place.
Still lots left to convert!

Convert GetSysDirectory to return Path.

More buildfixing

Remove unnecessary Path( constructors
2021-05-13 10:39:16 +02:00
Unknown W. Brackets 7a719dc245 SaveState: Switch to zstd.
Choose a level that is just slightly slower than snappy, but much smaller
typically.
2021-04-11 09:13:10 -07:00
Henrik Rydgård 5d62610a93 Merge pull request #14038 from unknownbrackets/savestate-str
SaveState: Load misaligned wide strings properly
2021-02-15 18:50:23 +01:00
Unknown W. Brackets 45f3f4554b SaveState: Prevent crash on bad cookie marker.
Just fail to load the save state.
2021-02-09 07:14:07 -08:00
Unknown W. Brackets be4c905373 SaveState: Load misaligned wide strings properly. 2021-01-31 08:55:54 -08:00
Henrik Rydgård 4f43cff5ca Move fileutil, net, image loaders, ui to Common. (#13506)
* Move and rename file_util/fd_util to Common/File/FileUtil and DirListing

Let's also move net while we're at it.

Move the ZIM/PNG loaders over to Common.

Move the UI framework into Common

iOS buildfix

* Buildfix

* Buildfixes

* Apple buildfix

* This typo again..

* UWP buildfix

* Fix build of PPSSPPQt, such as it is (it's not in good condition...)

* Guess what? Another buildfix.
2020-10-04 20:48:47 +02:00
Unknown W. Brackets b8342fb8ec SaveState: Rename ChunkFile files to Serialize.
Makes more sense and less weird than ChunkFileDoMap, etc.
2020-08-10 08:04:05 +00:00