memory: back the free pages of a partially-overlapping fixed mapping (#458)

A SCE_KERNEL_MAP_FIXED request whose window partially overlaps an
existing allocation was failing outright: AllocateAt reserves the whole
range in one all-or-nothing VirtualAlloc, which returns 0 on partial
overlap. The mapping call then returned NOT_FOUND while leaving the free
tail unmapped, so the guest faulted (0xC0000005) writing into it.

Add IGuestAddressSpace.TryBackFixedRange, which walks the range via the
host Query (VirtualQuery reports contiguous same-state runs) and fills
only the free sub-ranges, leaving already-backed pages untouched. This
matches the fixed-mapping contract on hardware. Route the fixed
reservation path through it via a new backPartialOverlap flag.

Co-authored-by: slick-daddy <slick-daddy@users.noreply.github.com>
This commit is contained in:
Slick Daddy
2026-07-20 09:08:29 +03:00
committed by GitHub
parent 184e24fbb6
commit 33be88bdf9
5 changed files with 215 additions and 2 deletions
+11
View File
@@ -15,6 +15,17 @@ public interface IGuestAddressSpace : IGuestMemoryAllocator
{
ulong AllocateAt(ulong desiredAddress, ulong size, bool executable = true, bool allowAlternative = true);
/// <summary>
/// Backs an entire fixed-address range, matching the guest's
/// <c>SCE_KERNEL_MAP_FIXED</c> contract. Unlike <see cref="AllocateAt"/>, which
/// reserves the range in one all-or-nothing host call, this walks the range and
/// fills only the sub-ranges that are not already backed. That keeps a fixed
/// mapping whole when part of the requested window is already occupied — the
/// partial-overlap case where the single-call reservation fails outright and
/// leaves the remainder unmapped for the guest to fault into.
/// </summary>
bool TryBackFixedRange(ulong address, ulong size, bool executable);
bool TryAllocateAtOrAbove(ulong desiredAddress, ulong size, bool executable, ulong alignment, out ulong actualAddress);
bool TryProtect(ulong address, ulong size, GuestPageProtection protection);