From fc5cb9b577a66eab38ea61290bdac60ec36f80ec Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Tue, 28 Jun 2022 17:13:47 +0300 Subject: [PATCH] ntdll: Partially implement MemoryRegionInformation query. Signed-off-by: Nikolay Sivov --- dlls/ntdll/tests/virtual.c | 97 +++++++++++++++++++++++++++++++ dlls/ntdll/unix/virtual.c | 115 +++++++++++++++++++++++++------------ dlls/wow64/struct32.h | 11 ++++ dlls/wow64/virtual.c | 23 ++++++++ include/winternl.h | 24 ++++++++ 5 files changed, 233 insertions(+), 37 deletions(-) diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 83ea35590f1..72d15c84f32 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -1574,6 +1574,102 @@ static void test_prefetch(void) "NtSetInformationVirtualMemory unexpected status on 2 page-aligned entries: %08lx\n", status); } +static void test_query_region_information(void) +{ + MEMORY_REGION_INFORMATION info; + LARGE_INTEGER offset; + SIZE_T len, size; + NTSTATUS status; + HANDLE mapping; + void *ptr; + + size = 0x10000; + ptr = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size, MEM_RESERVE, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + +#ifdef _WIN64 + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, + FIELD_OFFSET(MEMORY_REGION_INFORMATION, PartitionId), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, + FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, + FIELD_OFFSET(MEMORY_REGION_INFORMATION, RegionSize), &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %08lx.\n", status); +#endif + + len = 0; + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == size, "Unexpected region size.\n"); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + /* Committed size */ + size = 0x10000; + ptr = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == size, "Unexpected region size.\n"); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + /* Pagefile mapping */ + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, NULL); + ok(mapping != 0, "CreateFileMapping failed\n"); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READONLY, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == 4096, "Unexpected region size.\n"); + + status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + NtClose(mapping); +} + START_TEST(virtual) { HMODULE mod; @@ -1619,4 +1715,5 @@ START_TEST(virtual) test_prefetch(); test_user_shared_data(); test_syscalls(); + test_query_region_information(); } diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index d8b888aa49e..422d717b00d 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4080,48 +4080,13 @@ static int get_free_mem_state_callback( void *start, SIZE_T size, void *arg ) return 1; } -/* get basic information about a memory block */ -static NTSTATUS get_basic_memory_info( HANDLE process, LPCVOID addr, - MEMORY_BASIC_INFORMATION *info, - SIZE_T len, SIZE_T *res_len ) +static NTSTATUS fill_basic_memory_info( const void *addr, MEMORY_BASIC_INFORMATION *info ) { - struct file_view *view; char *base, *alloc_base = 0, *alloc_end = working_set_limit; struct wine_rb_entry *ptr; + struct file_view *view; sigset_t sigset; - if (len < sizeof(MEMORY_BASIC_INFORMATION)) - return STATUS_INFO_LENGTH_MISMATCH; - - if (process != NtCurrentProcess()) - { - NTSTATUS status; - apc_call_t call; - apc_result_t result; - - memset( &call, 0, sizeof(call) ); - - call.virtual_query.type = APC_VIRTUAL_QUERY; - call.virtual_query.addr = wine_server_client_ptr( addr ); - status = server_queue_process_apc( process, &call, &result ); - if (status != STATUS_SUCCESS) return status; - - if (result.virtual_query.status == STATUS_SUCCESS) - { - info->BaseAddress = wine_server_get_ptr( result.virtual_query.base ); - info->AllocationBase = wine_server_get_ptr( result.virtual_query.alloc_base ); - info->RegionSize = result.virtual_query.size; - info->Protect = result.virtual_query.prot; - info->AllocationProtect = result.virtual_query.alloc_prot; - info->State = (DWORD)result.virtual_query.state << 12; - info->Type = (DWORD)result.virtual_query.alloc_type << 16; - if (info->RegionSize != result.virtual_query.size) /* truncated */ - return STATUS_INVALID_PARAMETER; /* FIXME */ - if (res_len) *res_len = sizeof(*info); - } - return result.virtual_query.status; - } - base = ROUND_ADDR( addr, page_mask ); if (is_beyond_limit( base, 1, working_set_limit )) return STATUS_INVALID_PARAMETER; @@ -4195,6 +4160,79 @@ static NTSTATUS get_basic_memory_info( HANDLE process, LPCVOID addr, } server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + return STATUS_SUCCESS; +} + +/* get basic information about a memory block */ +static NTSTATUS get_basic_memory_info( HANDLE process, LPCVOID addr, + MEMORY_BASIC_INFORMATION *info, + SIZE_T len, SIZE_T *res_len ) +{ + NTSTATUS status; + + if (len < sizeof(*info)) + return STATUS_INFO_LENGTH_MISMATCH; + + if (process != NtCurrentProcess()) + { + NTSTATUS status; + apc_call_t call; + apc_result_t result; + + memset( &call, 0, sizeof(call) ); + + call.virtual_query.type = APC_VIRTUAL_QUERY; + call.virtual_query.addr = wine_server_client_ptr( addr ); + status = server_queue_process_apc( process, &call, &result ); + if (status != STATUS_SUCCESS) return status; + + if (result.virtual_query.status == STATUS_SUCCESS) + { + info->BaseAddress = wine_server_get_ptr( result.virtual_query.base ); + info->AllocationBase = wine_server_get_ptr( result.virtual_query.alloc_base ); + info->RegionSize = result.virtual_query.size; + info->Protect = result.virtual_query.prot; + info->AllocationProtect = result.virtual_query.alloc_prot; + info->State = (DWORD)result.virtual_query.state << 12; + info->Type = (DWORD)result.virtual_query.alloc_type << 16; + if (info->RegionSize != result.virtual_query.size) /* truncated */ + return STATUS_INVALID_PARAMETER; /* FIXME */ + if (res_len) *res_len = sizeof(*info); + } + return result.virtual_query.status; + } + + if ((status = fill_basic_memory_info( addr, info ))) return status; + + if (res_len) *res_len = sizeof(*info); + return STATUS_SUCCESS; +} + +static NTSTATUS get_memory_region_info( HANDLE process, LPCVOID addr, MEMORY_REGION_INFORMATION *info, + SIZE_T len, SIZE_T *res_len ) +{ + MEMORY_BASIC_INFORMATION basic_info; + NTSTATUS status; + + if (len < FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize)) + return STATUS_INFO_LENGTH_MISMATCH; + + if (process != NtCurrentProcess()) + { + FIXME("Unimplemented for other processes.\n"); + return STATUS_NOT_IMPLEMENTED; + } + + if ((status = fill_basic_memory_info( addr, &basic_info ))) return status; + + info->AllocationBase = basic_info.AllocationBase; + info->AllocationProtect = basic_info.AllocationProtect; + info->RegionType = 0; /* FIXME */ + if (len >= FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize)) + info->RegionSize = basic_info.RegionSize; + if (len >= FIELD_OFFSET(MEMORY_REGION_INFORMATION, PartitionId)) + info->CommitSize = basic_info.State == MEM_COMMIT ? basic_info.RegionSize : 0; + if (res_len) *res_len = sizeof(*info); return STATUS_SUCCESS; } @@ -4371,6 +4409,9 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr, case MemoryMappedFilenameInformation: return get_memory_section_name( process, addr, buffer, len, res_len ); + case MemoryRegionInformation: + return get_memory_region_info( process, addr, buffer, len, res_len ); + case MemoryWineUnixFuncs: case MemoryWineUnixWow64Funcs: if (len != sizeof(unixlib_handle_t)) return STATUS_INFO_LENGTH_MISMATCH; diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index 91acc26436f..0a0324a22f3 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -156,6 +156,17 @@ typedef struct DWORD Type; } MEMORY_BASIC_INFORMATION32; +typedef struct +{ + ULONG AllocationBase; + ULONG AllocationProtect; + ULONG RegionType; + ULONG RegionSize; + ULONG CommitSize; + ULONG PartitionId; + ULONG NodePreference; +} MEMORY_REGION_INFORMATION32; + typedef struct { UNICODE_STRING32 SectionFileName; diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c index 8b7d022301f..4d3bb90386a 100644 --- a/dlls/wow64/virtual.c +++ b/dlls/wow64/virtual.c @@ -422,6 +422,29 @@ NTSTATUS WINAPI wow64_NtQueryVirtualMemory( UINT *args ) break; } + case MemoryRegionInformation: /* MEMORY_REGION_INFORMATION */ + { + if (len >= sizeof(MEMORY_REGION_INFORMATION32)) + { + MEMORY_REGION_INFORMATION info; + MEMORY_REGION_INFORMATION32 *info32 = ptr; + + if (!(status = NtQueryVirtualMemory( handle, addr, class, &info, sizeof(info), &res_len ))) + { + info32->AllocationBase = PtrToUlong( info.AllocationBase ); + info32->AllocationProtect = info.AllocationProtect; + info32->RegionType = info.RegionType; + info32->RegionSize = info.RegionSize; + info32->CommitSize = info.CommitSize; + info32->PartitionId = info.PartitionId; + info32->NodePreference = info.NodePreference; + } + } + else status = STATUS_INFO_LENGTH_MISMATCH; + res_len = sizeof(MEMORY_REGION_INFORMATION32); + break; + } + case MemoryWorkingSetExInformation: /* MEMORY_WORKING_SET_EX_INFORMATION */ { MEMORY_WORKING_SET_EX_INFORMATION32 *info32 = ptr; diff --git a/include/winternl.h b/include/winternl.h index 5c564373fed..c64ff0f9082 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1971,6 +1971,30 @@ typedef struct _MEMORY_WORKING_SET_EX_INFORMATION { MEMORY_WORKING_SET_EX_BLOCK VirtualAttributes; } MEMORY_WORKING_SET_EX_INFORMATION, *PMEMORY_WORKING_SET_EX_INFORMATION; +typedef struct _MEMORY_REGION_INFORMATION +{ + PVOID AllocationBase; + ULONG AllocationProtect; + union + { + ULONG RegionType; + struct + { + ULONG Private : 1; + ULONG MappedDataFile : 1; + ULONG MappedImage : 1; + ULONG MappedPageFile : 1; + ULONG MappedPhysical : 1; + ULONG DirectMapped : 1; + ULONG Reserved : 26; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + SIZE_T RegionSize; + SIZE_T CommitSize; + ULONG_PTR PartitionId; + ULONG_PTR NodePreference; +} MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; + typedef enum _MUTANT_INFORMATION_CLASS { MutantBasicInformation