Fix bounds, memory protection, and completion race in core helpers
- AllocatedMemory.Read<T>/Write<T>/ReadBytes/WriteBytes now validate that the requested byte range stays within the allocated block before calling into the memory accessor. - Patch.Apply/Remove temporarily changes the target page to read-write and restores the original protection, mirroring the Detour behavior. - MainThreadPump.WorkItem uses TrySetResult/TrySetException and swallows the InvalidOperationException raised when a completion source is already completed, preventing Dispose from failing during concurrent pump drainage. Regression tests added for all three fixes. Tests: 206 passing, 4 skipped.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using WhiteMagic.Native;
|
||||
|
||||
namespace WhiteMagic.Hooking;
|
||||
|
||||
@@ -53,7 +55,31 @@ public sealed class Patch : IDisposable
|
||||
return;
|
||||
|
||||
OriginalBytes = _memory.ReadBytes(Address, PatchBytes.Length);
|
||||
_memory.WriteBytes(Address, PatchBytes);
|
||||
|
||||
if (!NativeMethods.VirtualProtectEx(
|
||||
_memory.Handle,
|
||||
Address,
|
||||
PatchBytes.Length,
|
||||
MemoryProtectionType.ExecuteReadWrite,
|
||||
out MemoryProtectionType oldProtect))
|
||||
{
|
||||
int error = Marshal.GetLastPInvokeError();
|
||||
throw new InvalidOperationException($"Failed to change target memory protection: error {error}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_memory.WriteBytes(Address, PatchBytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.VirtualProtectEx(
|
||||
_memory.Handle,
|
||||
Address,
|
||||
PatchBytes.Length,
|
||||
oldProtect,
|
||||
out _);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Restores the original bytes if they were captured.</summary>
|
||||
@@ -62,7 +88,31 @@ public sealed class Patch : IDisposable
|
||||
if (OriginalBytes is null)
|
||||
return;
|
||||
|
||||
_memory.WriteBytes(Address, OriginalBytes);
|
||||
if (!NativeMethods.VirtualProtectEx(
|
||||
_memory.Handle,
|
||||
Address,
|
||||
OriginalBytes.Length,
|
||||
MemoryProtectionType.ExecuteReadWrite,
|
||||
out MemoryProtectionType oldProtect))
|
||||
{
|
||||
int error = Marshal.GetLastPInvokeError();
|
||||
throw new InvalidOperationException($"Failed to change target memory protection: error {error}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_memory.WriteBytes(Address, OriginalBytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMethods.VirtualProtectEx(
|
||||
_memory.Handle,
|
||||
Address,
|
||||
OriginalBytes.Length,
|
||||
oldProtect,
|
||||
out _);
|
||||
}
|
||||
|
||||
OriginalBytes = null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user