There are regions of virtual memory that start below the WoW user address limit but end beyond it (e.g. a large empty region between the end of 32-bit allocations and 64-bit ntdll). When queried from WoW via `NtQueryVirtualMemory(MemoryBasicInformation)`, the returned struct for such a region will have `BaseAddress + RegionSize` past the highest user address (or even worse - it may overflow 32 bits). These patches make WoW `NtQueryVirtualMemory` report a truncated size for such regions, so that they appear to end exactly at the highest user address. This fixes programs that, e.g., walk their address space using iterated calls to `VirtualQuery`.
Also, make `MemoryRegionInformation` queries return `STATUS_INVALID_PARAMETER` when passed an address beyond the user address limit, like `MemoryBasicInformation` already does.
From: Tim Clem tclem@codeweavers.com
BaseAddress + RegionSize should not exceed the highest WoW user address. --- dlls/wow64/virtual.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c index 4d3bb90386a..779b02f9c4e 100644 --- a/dlls/wow64/virtual.c +++ b/dlls/wow64/virtual.c @@ -399,6 +399,13 @@ NTSTATUS WINAPI wow64_NtQueryVirtualMemory( UINT *args ) info32->State = info.State; info32->Protect = info.Protect; info32->Type = info.Type; + + if ((ULONG_PTR)info.BaseAddress + info.RegionSize > highest_user_address) + { + info32->RegionSize = highest_user_address - (ULONG_PTR)info.BaseAddress; + TRACE( "region %p-%p ends past the WoW address limit %08llx; truncating its size to 0x%08lx\n", + info.BaseAddress, (char *)info.BaseAddress + info.RegionSize, highest_user_address, info32->RegionSize ); + } } } res_len = sizeof(MEMORY_BASIC_INFORMATION32);
From: Tim Clem tclem@codeweavers.com
--- dlls/wow64/virtual.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c index 779b02f9c4e..bad78d0e46f 100644 --- a/dlls/wow64/virtual.c +++ b/dlls/wow64/virtual.c @@ -431,7 +431,11 @@ NTSTATUS WINAPI wow64_NtQueryVirtualMemory( UINT *args )
case MemoryRegionInformation: /* MEMORY_REGION_INFORMATION */ { - if (len >= sizeof(MEMORY_REGION_INFORMATION32)) + if (len < sizeof(MEMORY_REGION_INFORMATION32)) + status = STATUS_INFO_LENGTH_MISMATCH; + if ((ULONG_PTR)addr > highest_user_address) + status = STATUS_INVALID_PARAMETER; + else { MEMORY_REGION_INFORMATION info; MEMORY_REGION_INFORMATION32 *info32 = ptr; @@ -447,7 +451,6 @@ NTSTATUS WINAPI wow64_NtQueryVirtualMemory( UINT *args ) info32->NodePreference = info.NodePreference; } } - else status = STATUS_INFO_LENGTH_MISMATCH; res_len = sizeof(MEMORY_REGION_INFORMATION32); break; }
From: Tim Clem tclem@codeweavers.com
AllocationBase + RegionSize should not exceed the highest WoW user address. --- dlls/wow64/virtual.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c index bad78d0e46f..ac8291ae869 100644 --- a/dlls/wow64/virtual.c +++ b/dlls/wow64/virtual.c @@ -449,6 +449,13 @@ NTSTATUS WINAPI wow64_NtQueryVirtualMemory( UINT *args ) info32->CommitSize = info.CommitSize; info32->PartitionId = info.PartitionId; info32->NodePreference = info.NodePreference; + + if ((ULONG_PTR)info.AllocationBase + info.RegionSize > highest_user_address) + { + info32->RegionSize = highest_user_address - (ULONG_PTR)info.AllocationBase; + TRACE( "region %p-%p ends past the WoW address limit %08llx; truncating its size to 0x%08lx\n", + info.AllocationBase, (char *)info.AllocationBase + info.RegionSize, highest_user_address, info32->RegionSize ); + } } } res_len = sizeof(MEMORY_REGION_INFORMATION32);