SaveState: Fixes from a review of the kernel savestates

- A partition memory block restored its allocator through
  BlockAllocatorFromID, whose privilege check asked the thread current
  before the load, already deleted with the kernel objects. Loading a
  kernel-partition block under a user thread left it without an allocator,
  crashing when it was freed. The saved id maps straight back now, and the
  current thread is cleared along with the objects.
- The thread LoadExec leaves waiting (with no timeout) for plugins and VSH
  modules to start wasn't saved, so a state saved while one was starting
  hung the game thread after loading. The VSH modules still to start
  weren't saved either, nor cleared.
- A state without the exit callback id left the previous session's.
- From a broken or crafted state: thread ids that GetFast trusts (current,
  idle and ready), interrupt handler numbers, FPL and TLSPL indices, and a
  MIPS call or action slot that isn't there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-20 00:20:24 +02:00
1 parent d40137a007
commit 30cab94840
8 files changed
+91 -6

No files matched your search

+18 -2
View File
@@ -239,7 +239,7 @@ u32 PSPModule::GetMissingErrorCode() {
}
void PSPModule::DoState(PointerWrap &p) {
auto s = p.Section("Module", 1, 6);
auto s = p.Section("Module", 1, 7);
if (!s)
return;
@@ -285,6 +285,14 @@ void PSPModule::DoState(PointerWrap &p) {
if (s >= 5) {
Do(p, modulePtr.ptr);
}
if (s >= 7) {
// LoadExec's thread waits for plugins (and VSH modules) to start, with no timeout.
Do(p, pluginWaitingThread);
Do(p, startingPlugins);
} else if (p.mode == p.MODE_READ) {
pluginWaitingThread = 0;
startingPlugins.clear();
}
ModuleWaitingThread mwt = { 0 };
Do(p, waitingThreads, mwt);
@@ -555,7 +563,7 @@ std::vector<std::pair<u32, u32>> __KernelModuleCodeRanges() {
}
void __KernelModuleDoState(PointerWrap &p) {
auto s = p.Section("sceKernelModule", 1, 5);
auto s = p.Section("sceKernelModule", 1, 6);
if (!s)
return;
@@ -596,6 +604,14 @@ void __KernelModuleDoState(PointerWrap &p) {
}
CoreTiming::RestoreRegisterEvent(eventModuleLoadDone, "LoadModuleDone", &__KernelLoadModuleDone);
if (s >= 6) {
Do(p, g_vshModulesToStart);
Do(p, g_vshModuleStarting);
} else if (p.mode == p.MODE_READ) {
g_vshModulesToStart.clear();
g_vshModuleStarting = 0;
}
if (p.mode == p.MODE_READ) {
u32 error;
// We process these late, since they depend on loadedModules for interlinking.