Prevent invalid SaveData writes from damaging guest memory (#444)

Add an optional write monitor so the team can find future memory damage on each supported desktop system.
This commit is contained in:
StealUrKill
2026-07-19 12:27:17 -05:00
committed by GitHub
parent d7f6e3f578
commit bc51cc2c4d
8 changed files with 437 additions and 21 deletions
@@ -2984,6 +2984,7 @@ public static partial class KernelMemoryCompatExports
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
GuestWriteWatch.OnDirectMapping(mappedAddress, length, protection);
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
+17 -20
View File
@@ -792,28 +792,15 @@ public static class SaveDataExports
var id = (uint)Interlocked.Increment(ref _nextTransactionResource);
// The resource-out pointer's argument slot varies by SDK revision: some
// callers pass it in rdx, others in rcx (a 4-arg form where rdx holds a
// count/flag). Void Terrarium passes rdx=0x1 (not a pointer) and the
// real out-pointer in rcx. Probe the plausible candidates and write the
// handle to the first writable one instead of faulting on a bad rdx.
// This is a stub-level create (matches shadPS4's return-OK semantics);
// never return MEMORY_FAULT for it, or the guest treats savedata init as
// failed and never advances.
// A small RDX value is a flag, and RCX contains the output address.
// A larger RDX value is the output address for the older ABI.
var resourceAddress = 0UL;
foreach (var candidate in new[]
{
ctx[CpuRegister.Rdx],
ctx[CpuRegister.Rcx],
ctx[CpuRegister.R8],
ctx[CpuRegister.R9],
})
var selectedAddress = SelectTransactionResourceAddress(
ctx[CpuRegister.Rdx],
ctx[CpuRegister.Rcx]);
if (selectedAddress != 0 && TryWriteUInt32(ctx, selectedAddress, id))
{
if (candidate != 0 && TryWriteUInt32(ctx, candidate, id))
{
resourceAddress = candidate;
break;
}
resourceAddress = selectedAddress;
}
TraceSaveData(
@@ -822,6 +809,16 @@ public static class SaveDataExports
return SetReturn(ctx, 0);
}
internal static ulong SelectTransactionResourceAddress(ulong rdx, ulong rcx)
{
if (rdx == 0)
{
return 0;
}
return rdx <= ushort.MaxValue ? rcx : rdx;
}
[SysAbiExport(
Nid = "lJUQuaKqoKY",
ExportName = "sceSaveDataDeleteTransactionResource",