diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs index 3e51f180..399c5fb9 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs @@ -1768,14 +1768,16 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I frameAddress = 0; transferStub = 0; error = null; - if (target.Rip < 65536 || target.Rsp == 0) + if (ActiveCpuContext is not { } activeContext) { - error = $"invalid guest context transfer target rip=0x{target.Rip:X16} rsp=0x{target.Rsp:X16}"; + error = "guest context transfer without an active CPU context"; return false; } - if (target.Rsp < sizeof(ulong) || - ActiveCpuContext is not { } activeContext || - !activeContext.TryWriteUInt64(target.Rsp - sizeof(ulong), target.Rip)) + if (!TryValidateGuestContextTransferTarget(activeContext.Memory, target, out error)) + { + return false; + } + if (!activeContext.TryWriteUInt64(target.Rsp - sizeof(ulong), target.Rip)) { error = $"guest context transfer slot is not writable at 0x{target.Rsp - sizeof(ulong):X16}"; return false; @@ -1819,6 +1821,30 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return true; } + internal static bool TryValidateGuestContextTransferTarget( + ICpuMemory memory, + in GuestCpuContinuation target, + out string? error) + { + if (target.Rip < 65536 || target.Rsp < sizeof(ulong)) + { + error = $"invalid guest context transfer target rip=0x{target.Rip:X16} rsp=0x{target.Rsp:X16}"; + return false; + } + + Span ripProbe = stackalloc byte[1]; + if (!memory.TryRead(target.Rip, ripProbe)) + { + error = + $"guest context transfer target rip=0x{target.Rip:X16} is not mapped guest memory " + + $"(rsp=0x{target.Rsp:X16})"; + return false; + } + + error = null; + return true; + } + private unsafe nint GetOrCreateGuestContextTransferStub() { if (Volatile.Read(ref _guestContextTransferStub) != 0) diff --git a/src/SharpEmu.Core/SharpEmu.Core.csproj b/src/SharpEmu.Core/SharpEmu.Core.csproj index ff676321..3376fe03 100644 --- a/src/SharpEmu.Core/SharpEmu.Core.csproj +++ b/src/SharpEmu.Core/SharpEmu.Core.csproj @@ -14,6 +14,10 @@ SPDX-License-Identifier: GPL-2.0-or-later + + + + $(NoWarn);1591 diff --git a/tests/SharpEmu.Libs.Tests/Cpu/GuestContextTransferValidationTests.cs b/tests/SharpEmu.Libs.Tests/Cpu/GuestContextTransferValidationTests.cs new file mode 100644 index 00000000..78bcf53c --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Cpu/GuestContextTransferValidationTests.cs @@ -0,0 +1,61 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.Core.Cpu.Native; +using SharpEmu.HLE; +using Xunit; + +namespace SharpEmu.Libs.Tests.Cpu; + +public sealed class GuestContextTransferValidationTests +{ + private const ulong MemoryBase = 0x1_0000_0000; + + [Fact] + public void MappedGuestInstructionPointer_IsAccepted() + { + var memory = new FakeCpuMemory(MemoryBase, 0x1000); + var continuation = CreateContinuation(MemoryBase + 0x100, MemoryBase + 0x800); + + Assert.True(DirectExecutionBackend.TryValidateGuestContextTransferTarget( + memory, + continuation, + out var error)); + Assert.Null(error); + } + + [Fact] + public void UnmappedGuestInstructionPointer_IsRejected() + { + var memory = new FakeCpuMemory(MemoryBase, 0x1000); + var continuation = CreateContinuation(MemoryBase + 0x2000, MemoryBase + 0x800); + + Assert.False(DirectExecutionBackend.TryValidateGuestContextTransferTarget( + memory, + continuation, + out var error)); + Assert.Contains("not mapped guest memory", error); + } + + [Theory] + [InlineData(0UL, MemoryBase + 0x800)] + [InlineData(MemoryBase + 0x100, 0UL)] + public void InvalidInstructionOrStackPointer_IsRejected(ulong rip, ulong rsp) + { + var memory = new FakeCpuMemory(MemoryBase, 0x1000); + var continuation = CreateContinuation(rip, rsp); + + Assert.False(DirectExecutionBackend.TryValidateGuestContextTransferTarget( + memory, + continuation, + out var error)); + Assert.Contains("invalid guest context transfer target", error); + } + + private static GuestCpuContinuation CreateContinuation(ulong rip, ulong rsp) => + new() + { + Rip = rip, + Rsp = rsp, + }; +}