Skip to content

Reset after rejected non-transactional state loads - #188

Closed
acts-1631 wants to merge 1 commit into
libretro:mainfrom
acts-1631:fix-rejected-netplay-state
Closed

acts-1631 wants to merge 1 commit into
libretro:mainfrom
acts-1631:fix-rejected-netplay-state

Conversation

@acts-1631

Copy link
Copy Markdown
Contributor

Rollback netplay states are loaded through retro_unserialize() without the backup used for normal state loads. A crafted state can be rejected after System::DoState() has already replaced fields in earlier subsystems, leaving those rejected values live.

For example, CDROM::DoState() rejects a sector buffer size above RAW_SECTOR_OUTPUT_SIZE, but the oversized value remains installed. A later CD data request passes it from CDROM::LoadDataFIFO() to FIFOQueue::PushRange(), causing out-of-bounds heap reads and writes in a connected client. A malicious netplay host can supply this state.

Make System::LoadState() reset after a failed load when restore_on_failure is false. This preserves the fast path for successful runahead and netplay loads while ensuring rejected partial state cannot execute. CPU::Reset() now clears delayed-load register indices before SetPC() flushes the pipeline, making reset safe if rejection followed a crafted CPU section.

Rollback netplay states may be rejected after deserialization has
already modified earlier subsystems. Returning in that state can leave
unvalidated indices and sizes live, including a CD-ROM sector size later
passed to an unbounded FIFO copy.

Reset after a failed load when no backup was requested, so partial state
cannot execute. Clear CPU delayed-load register indices before SetPC()
flushes the pipeline, making reset safe after a crafted partial load.
@LibretroAdmin

Copy link
Copy Markdown
Contributor

Merged manually by hand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants