libretro: relax retro_unserialize size check to permit cross-version state loads

The previous `size != retro_serialize_size()` guard rejected any state
whose serialized length didn't exactly match the current build's
SFORMAT-derived size.  That sounds defensive, but the file format
already carries its own 16-byte header with an explicit `totalsize`
and `ReadStateChunk()` already skips unknown chunk tags safely (size-
bounded `memstream_seek` past the payload), so the outer check adds
no parser safety - it just turns every SFORMAT change into a hard
break of existing saves with no user recourse.

This came up in the recent FDS audio rewrite for #560: the backport
added/removed audio-register chunk tags, leaving pre-rewrite states
240 bytes smaller than the new core's expected size, and users with
in-progress FDS games hit "unserialize FAIL" on first reload after
updating the core.

Accept any buffer that's at least the 16-byte header and at most
4x the current serialize size (a sanity cap against pathological
input).  ReadStateChunk does the actual work of validating each
chunk's size against the header's totalsize, and the FCS magic in
the header rejects non-fceumm states.

Verified on Vs. Excitebike (Japan):
  - pre-rewrite state -> new core: load OK, audio plays normally
  - new-rewrite state -> new core: round-trip OK (same audio as before)
  - 15-byte input:                 rejected (< header size)
  - 4 MB input:                    rejected (> 4 * serialize_size)
This commit is contained in:
libretroadmin
2026-06-14 18:17:57 +00:00
committed by U-DESKTOP-SPFP6AQ\twistedtechre
parent bc5a7a9fec
commit fef3de8bd9

View File

@@ -3185,7 +3185,17 @@ bool retro_unserialize(const void * data, size_t size)
if (geniestage == 1)
return false;
if (!data || size != retro_serialize_size())
/* The state file's own 16-byte header carries an explicit totalsize
* and ReadStateChunk already skips unknown chunk tags, so a strict
* size-equality check against the current build's serialize_size is
* not necessary for parser safety - and is actively harmful when
* SFORMAT contents change between builds (recent example: the FDS
* audio rewrite for #560 added/removed chunk tags, leaving older
* savestates a fixed delta smaller than the new core's expected
* size, with no recourse for the user). Accept any buffer at least
* as large as the header; cap the upper end to a generous multiple
* of the current size as a sanity guard against pathological input. */
if (!data || size < 16 || size > retro_serialize_size() * 4)
return false;
memstream_set_buffer((uint8_t*)data, size);