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:
kbe
2026-07-22 02:16:23 +02:00
parent ffd72b37ed
commit 1911514120
5 changed files with 261 additions and 16 deletions
@@ -251,4 +251,73 @@ public class AllocatedMemoryTests
Assert.Contains("does not exist", ex.Message);
}
[Fact]
public void Read_T_throws_when_value_exceeds_allocation()
{
using var reader = CreateReader();
using var allocated = new AllocatedMemory(reader, 100);
allocated.AddRegion("boundary", 99);
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
allocated.Read<int>("boundary"));
Assert.Equal("name", ex.ParamName);
}
[Fact]
public void Write_T_throws_when_value_exceeds_allocation()
{
using var reader = CreateReader();
using var allocated = new AllocatedMemory(reader, 100);
allocated.AddRegion("boundary", 99);
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
allocated.Write("boundary", 42));
Assert.Equal("name", ex.ParamName);
}
[Fact]
public void ReadBytes_throws_when_count_exceeds_allocation()
{
using var reader = CreateReader();
using var allocated = new AllocatedMemory(reader, 100);
allocated.AddRegion("boundary", 99);
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
allocated.ReadBytes("boundary", 2));
Assert.Equal("count", ex.ParamName);
}
[Fact]
public void WriteBytes_throws_when_span_exceeds_allocation()
{
using var reader = CreateReader();
using var allocated = new AllocatedMemory(reader, 100);
allocated.AddRegion("boundary", 99);
var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
allocated.WriteBytes("boundary", new byte[2]));
Assert.Equal("bytes", ex.ParamName);
}
[Fact]
public void Read_and_Write_at_exact_allocation_boundary_succeed()
{
using var reader = CreateReader();
using var allocated = new AllocatedMemory(reader, 100);
allocated.AddRegion("boundary", 96);
const int expected = unchecked((int)0xDEADBEEF);
Assert.True(allocated.Write("boundary", expected));
Assert.Equal(expected, allocated.Read<int>("boundary"));
}
}