Merge pull request #21988 from Arkadyzja/savestate-forget-stale-helper-threads

Savestate: don't let stale HLE helper threads mutate restored kernel state
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-07-30 17:21:54 +02:00
commit e3975c7aae
2 files changed
+22

No files matched your search

+12
View File
@@ -701,6 +701,18 @@ void __PsmfPlayerDoState(PointerWrap &p) {
if (!s)
return;
if (p.mode == p.MODE_READ) {
// The map serializer deletes every existing host PsmfPlayer before
// loading. ~PsmfPlayer -> AbortFinish() deletes its helper thread
// without Forget(), mutating the freshly restored kernel state with
// stale ids/blocks from before the load (see the matching fix in
// __UtilityDoState). Forget the helpers first; the kernel-side
// objects belong to the restored state, not to these host wrappers.
for (auto &it : psmfPlayerMap) {
if (it.second && it.second->finishThread)
it.second->finishThread->Forget();
}
}
Do(p, psmfPlayerMap);
Do(p, videoPixelMode);
Do(p, videoLoopStatus);
+10
View File
@@ -361,6 +361,16 @@ void __UtilityDoState(PointerWrap &p) {
if (s >= 4) {
Do(p, hasAccessThread);
if (hasAccessThread) {
if (p.mode == p.MODE_READ && accessThread) {
// Do() below would delete the stale host object without Forget(),
// letting ~HLEHelperThread run __KernelDeleteThread and free kernel
// memory using pre-load ids/blocks against the restored kernel
// state. If an id or block was recycled, that kills a live thread
// or frees a live allocation. Same pattern as __IoDoState.
accessThread->Forget();
delete accessThread;
accessThread = nullptr;
}
Do(p, accessThread);
if (p.mode == p.MODE_READ)
accessThreadState = "from save state";