mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-31 03:26:47 +01:00
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:
1 parent
d40137a007
commit
30cab94840
8 files changed
+91
-6
No files matched your search
@@ -647,6 +647,10 @@ inline void __SetCurrentThread(PSPThread *thread, SceUID threadID, const char *n
|
||||
hleCurrentThreadName = name;
|
||||
}
|
||||
|
||||
void __KernelForgetCurrentThread() {
|
||||
__SetCurrentThread(nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
u32 __KernelCallbackReturnAddress() {
|
||||
return cbReturnHackAddr;
|
||||
}
|
||||
@@ -945,6 +949,25 @@ void __KernelThreadingDoState(PointerWrap &p)
|
||||
g_chanceAccumulator = 50;
|
||||
}
|
||||
|
||||
if (p.mode == p.MODE_READ) {
|
||||
// GetFast trusts all of these to be threads.
|
||||
u32 error;
|
||||
auto isThread = [&](SceUID id) {
|
||||
return id == 0 || kernelObjects.Get<PSPThread>(id, error) != nullptr;
|
||||
};
|
||||
bool valid = isThread(currentThread) && threadReadyQueue.all_of(isThread);
|
||||
for (SceUID id : threadIdleID) {
|
||||
valid = valid && isThread(id);
|
||||
}
|
||||
if (!valid) {
|
||||
ERROR_LOG(Log::SaveState, "Unable to load state: current, idle or ready thread isn't a thread");
|
||||
threadReadyQueue.clear();
|
||||
currentThread = 0;
|
||||
p.SetError(p.ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__SetCurrentThread(kernelObjects.GetFast<PSPThread>(currentThread), currentThread, __KernelGetThreadName(currentThread));
|
||||
lastSwitchCycles = CoreTiming::GetTicks(currentMIPS);
|
||||
|
||||
@@ -3574,6 +3597,10 @@ void __KernelReturnFromMipsCall() {
|
||||
|
||||
u32 callId = cur->currentMipscallId;
|
||||
MipsCall *call = mipsCalls.pop(callId);
|
||||
if (!call) {
|
||||
ERROR_LOG(Log::sceKernel, "__KernelReturnFromMipsCall(): No call %d", callId);
|
||||
return hleNoLogVoid();
|
||||
}
|
||||
|
||||
// Value returned by the callback function
|
||||
u32 retVal = currentMIPS->r[MIPS_REG_V0];
|
||||
|
||||
Reference in new issue
Block a user