-- v2: ntdll/tests: Add more tests for placeholders. ntdll: Support MEM_PRESERVE_PLACEHOLDER in NtFreeVirtualMemory(). ntdll: Support MEM_REPLACE_PLACEHOLDER in NtAllocateVirtualMemoryEx(). ntdll: Support MEM_REPLACE_PLACEHOLDER in map_view(). ntdll: Support MEM_RESERVE_PLACEHOLDER in NtAllocateVirtualMemoryEx(). ntdll: Pass allocation type to map_view(). ntdll: Handle NULL process handle in MapViewOfFile3().
From: Paul Gofman pgofman@codeweavers.com
Based on a patch by Nikolay Sivov. --- dlls/kernelbase/memory.c | 2 ++ dlls/kernelbase/tests/process.c | 2 +- dlls/ntdll/tests/virtual.c | 12 ++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/dlls/kernelbase/memory.c b/dlls/kernelbase/memory.c index 4188eebf181..19381e00b31 100644 --- a/dlls/kernelbase/memory.c +++ b/dlls/kernelbase/memory.c @@ -265,6 +265,8 @@ LPVOID WINAPI DECLSPEC_HOTPATCH MapViewOfFile3( HANDLE handle, HANDLE process, P LARGE_INTEGER off; void *addr;
+ if (!process) process = GetCurrentProcess(); + addr = baseaddr; off.QuadPart = offset; if (!set_ntstatus( NtMapViewOfSectionEx( handle, process, &addr, &off, &size, alloc_type, protection, diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c index 2979af39551..14eed7d8fe9 100644 --- a/dlls/kernelbase/tests/process.c +++ b/dlls/kernelbase/tests/process.c @@ -123,7 +123,7 @@ static void test_MapViewOfFile3(void) ok( mapping != 0, "CreateFileMapping error %lu\n", GetLastError() );
SetLastError(0xdeadbeef); - ptr = pMapViewOfFile3( mapping, GetCurrentProcess(), NULL, 0, 4096, 0, PAGE_READONLY, NULL, 0); + ptr = pMapViewOfFile3( mapping, NULL, NULL, 0, 4096, 0, PAGE_READONLY, NULL, 0); ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ error %lu\n", GetLastError() ); UnmapViewOfFile( ptr );
diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 5dd53c55f54..2c8b1614b84 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -1146,6 +1146,12 @@ static void test_NtMapViewOfSection(void) process = create_target_process("sleep"); ok(process != NULL, "Can't start process\n");
+ ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, NULL, &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_INVALID_HANDLE, "NtMapViewOfSection returned %08lx\n", status); + ptr = NULL; size = 0; offset.QuadPart = 0; @@ -1419,6 +1425,12 @@ static void test_NtMapViewOfSectionEx(void) process = create_target_process("sleep"); ok(process != NULL, "Can't start process\n");
+ ptr = NULL; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, NULL, &ptr, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %08lx\n", status); + ptr = NULL; size = 0x1000; offset.QuadPart = 0;
From: Paul Gofman pgofman@codeweavers.com
Based on a patch by Nikolay Sivov. --- dlls/ntdll/unix/virtual.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 9ebede0b159..0ab9577d6a3 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1970,8 +1970,9 @@ static NTSTATUS map_fixed_area( void *base, size_t size, unsigned int vprot ) * virtual_mutex must be held by caller. */ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, - int top_down, unsigned int vprot, ULONG_PTR limit, size_t align_mask ) + unsigned int alloc_type, unsigned int vprot, ULONG_PTR limit, size_t align_mask ) { + int top_down = alloc_type & MEM_TOP_DOWN; void *ptr; NTSTATUS status;
@@ -2244,7 +2245,7 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot if (mmap_is_in_reserved_area( low_64k, dosmem_size - 0x10000 ) != 1) { addr = anon_mmap_tryfixed( low_64k, dosmem_size - 0x10000, unix_prot, 0 ); - if (addr == MAP_FAILED) return map_view( view, NULL, dosmem_size, FALSE, vprot, 0, 0 ); + if (addr == MAP_FAILED) return map_view( view, NULL, dosmem_size, 0, vprot, 0, 0 ); }
/* now try to allocate the low 64K too */ @@ -2724,9 +2725,9 @@ static NTSTATUS virtual_map_image( HANDLE mapping, ACCESS_MASK access, void **ad if ((ULONG_PTR)base != image_info->base) base = NULL;
if ((char *)base >= (char *)address_space_start) /* make sure the DOS area remains free */ - status = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, limit, 0 ); + status = map_view( &view, base, size, alloc_type, vprot, limit, 0 );
- if (status) status = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, limit, 0 ); + if (status) status = map_view( &view, NULL, size, alloc_type, vprot, limit, 0 ); if (status) goto done;
status = map_image_into_view( view, filename, unix_fd, base, image_info, @@ -2850,7 +2851,7 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
- res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, limit, 0 ); + res = map_view( &view, base, size, alloc_type, vprot, limit, 0 ); if (res) goto done;
TRACE( "handle=%p size=%lx offset=%s\n", handle, size, wine_dbgstr_longlong(offset.QuadPart) ); @@ -3419,7 +3420,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR limit, SIZE_T
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
- status = map_view( &view, NULL, size, FALSE, VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, limit, 0 ); + status = map_view( &view, NULL, size, 0, VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, limit, 0 ); if (status != STATUS_SUCCESS) goto done;
#ifdef VALGRIND_STACK_REGISTER @@ -4093,7 +4094,7 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
if (vprot & VPROT_WRITECOPY) status = STATUS_INVALID_PAGE_PROTECTION; else if (is_dos_memory) status = allocate_dos_memory( &view, vprot ); - else status = map_view( &view, base, size, type & MEM_TOP_DOWN, vprot, limit, + else status = map_view( &view, base, size, type, vprot, limit, align ? align - 1 : granularity_mask );
if (status == STATUS_SUCCESS) base = view->base;
From: Paul Gofman pgofman@codeweavers.com
Based on a patch by Nikolay Sivov. --- dlls/kernelbase/tests/process.c | 37 +++++++------ dlls/ntdll/tests/virtual.c | 95 +++++++++++++++------------------ dlls/ntdll/unix/virtual.c | 30 ++++++++++- 3 files changed, 90 insertions(+), 72 deletions(-)
diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c index 14eed7d8fe9..f7b321cc09f 100644 --- a/dlls/kernelbase/tests/process.c +++ b/dlls/kernelbase/tests/process.c @@ -168,9 +168,7 @@ static void test_VirtualAlloc2(void)
/* Placeholder splitting functionality */ placeholder1 = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE_PLACEHOLDER | MEM_RESERVE, PAGE_NOACCESS, NULL, 0); - todo_wine ok(!!placeholder1, "Failed to create a placeholder range.\n"); - if (!placeholder1) return;
memset(&info, 0, sizeof(info)); VirtualQuery(placeholder1, &info, sizeof(info)); @@ -180,14 +178,14 @@ static void test_VirtualAlloc2(void) ok(info.RegionSize == 2 * size, "Unexpected size.\n");
ret = VirtualFree(placeholder1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(ret, "Failed to split placeholder.\n"); + todo_wine ok(ret, "Failed to split placeholder.\n");
memset(&info, 0, sizeof(info)); VirtualQuery(placeholder1, &info, sizeof(info)); ok(info.AllocationProtect == PAGE_NOACCESS, "Unexpected protection %#lx.\n", info.AllocationProtect); ok(info.State == MEM_RESERVE, "Unexpected state %#lx.\n", info.State); ok(info.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", info.Type); - ok(info.RegionSize == size, "Unexpected size.\n"); + todo_wine ok(info.RegionSize == size, "Unexpected size.\n");
placeholder2 = (void *)((BYTE *)placeholder1 + size); memset(&info, 0, sizeof(info)); @@ -201,10 +199,10 @@ static void test_VirtualAlloc2(void) ok(!!section, "Failed to create a section.\n");
view1 = pMapViewOfFile3(section, NULL, placeholder1, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); - ok(!!view1, "Failed to map a section.\n"); + todo_wine ok(!!view1, "Failed to map a section.\n");
view2 = pMapViewOfFile3(section, NULL, placeholder2, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); - ok(!!view2, "Failed to map a section.\n"); + todo_wine ok(!!view2, "Failed to map a section.\n");
CloseHandle(section); UnmapViewOfFile(view1); @@ -220,16 +218,19 @@ static void test_VirtualAlloc2(void) p1 = p + size / 2; p2 = p1 + size / 4; ret = VirtualFree(p1, size / 4, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(ret, "Failed to split a placeholder.\n"); - check_region_size(p, size / 2); - check_region_size(p1, size / 4); + todo_wine ok(ret, "Failed to split a placeholder.\n"); + if (ret) + { + check_region_size(p, size / 2); + check_region_size(p1, size / 4); + } check_region_size(p2, 2 * size - size / 2 - size / 4); ret = VirtualFree(p, 0, MEM_RELEASE); ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p1, 0, MEM_RELEASE); - ok(ret, "Failed to release a region.\n"); + todo_wine ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p2, 0, MEM_RELEASE); - ok(ret, "Failed to release a region.\n"); + todo_wine ok(ret, "Failed to release a region.\n");
/* Split in two regions, specifying lower part. */ p = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE_PLACEHOLDER | MEM_RESERVE, PAGE_NOACCESS, NULL, 0); @@ -238,13 +239,14 @@ static void test_VirtualAlloc2(void) p1 = p; p2 = p + size / 2; ret = VirtualFree(p1, size / 2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(ret, "Failed to split a placeholder.\n"); - check_region_size(p1, size / 2); + todo_wine ok(ret, "Failed to split a placeholder.\n"); + if (ret) + check_region_size(p1, size / 2); check_region_size(p2, 2 * size - size / 2); ret = VirtualFree(p1, 0, MEM_RELEASE); ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p2, 0, MEM_RELEASE); - ok(ret, "Failed to release a region.\n"); + todo_wine ok(ret, "Failed to release a region.\n");
/* Split in two regions, specifying second half. */ p = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE_PLACEHOLDER | MEM_RESERVE, PAGE_NOACCESS, NULL, 0); @@ -253,13 +255,14 @@ static void test_VirtualAlloc2(void) p1 = p; p2 = p + size; ret = VirtualFree(p2, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(ret, "Failed to split a placeholder.\n"); - check_region_size(p1, size); + todo_wine ok(ret, "Failed to split a placeholder.\n"); + if (ret) + check_region_size(p1, size); check_region_size(p2, size); ret = VirtualFree(p1, 0, MEM_RELEASE); ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p2, 0, MEM_RELEASE); - ok(ret, "Failed to release a region.\n"); + todo_wine ok(ret, "Failed to release a region.\n"); }
static void test_VirtualAllocFromApp(void) diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 2c8b1614b84..604e234b100 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -334,15 +334,11 @@ static void test_NtAllocateVirtualMemoryEx(void)
status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
- if (addr1) - { - size = 0; - status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - } + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
/* Placeholder region splitting. */
@@ -351,79 +347,72 @@ static void test_NtAllocateVirtualMemoryEx(void) size = 0x10000; status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
+ p = addr1; + p1 = p + size / 2; + p2 = p1 + size / 4; + size2 = size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + if (status == STATUS_SUCCESS) { - p = addr1; - p1 = p + size / 2; - p2 = p1 + size / 4; - size2 = size / 4; - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - check_region_size(p, size / 2); check_region_size(p1, size / 4); - check_region_size(p2, size - size / 2 - size / 4); - - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); } + check_region_size(p2, size - size / 2 - size / 4); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
/* Split in two regions, specifying lower part. */ addr1 = NULL; size = 0x10000; status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
- if (status == STATUS_SUCCESS) - { - p1 = addr1; - p2 = p1 + size / 4; - size2 = size / 4; - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - ok(p1 == addr1, "Unexpected address.\n"); + p1 = addr1; + p2 = p1 + size / 4; + size2 = size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(p1 == addr1, "Unexpected address.\n");
+ if (status == STATUS_SUCCESS) check_region_size(p1, size / 4); - check_region_size(p2, size - size / 4); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - } + check_region_size(p2, size - size / 4); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
/* Split in two regions, specifying second half. */ addr1 = NULL; size = 0x10000; status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
- if (status == STATUS_SUCCESS) - { - p1 = addr1; - p2 = p1 + size / 2; + p1 = addr1; + p2 = p1 + size / 2;
- size2 = size / 2; - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - ok(p2 == p1 + size / 2, "Unexpected address.\n"); + size2 = size / 2; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(p2 == p1 + size / 2, "Unexpected address.\n"); + if (status == STATUS_SUCCESS) check_region_size(p1, size / 2); - check_region_size(p2, size / 2); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - } + check_region_size(p2, size / 2); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
memset( ext, 0, sizeof(ext) ); ext[0].Type = MemExtendedParameterAttributeFlags; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 0ab9577d6a3..2aadb1def00 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -124,6 +124,8 @@ struct file_view #define VPROT_WRITEWATCH 0x40 /* per-mapping protection flags */ #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */ +#define VPROT_PLACEHOLDER 0x0400 +#define VPROT_FREE_PLACEHOLDER 0x0800
/* Conversion from VPROT_* to Win32 flags */ static const BYTE VIRTUAL_Win32Flags[16] = @@ -1130,6 +1132,8 @@ static void dump_view( struct file_view *view ) TRACE( "View: %p - %p", addr, addr + view->size - 1 ); if (view->protect & VPROT_SYSTEM) TRACE( " (builtin image)\n" ); + else if (view->protect & VPROT_FREE_PLACEHOLDER) + TRACE( " (placeholder)\n" ); else if (view->protect & SEC_IMAGE) TRACE( " (image)\n" ); else if (view->protect & SEC_FILE) @@ -4071,8 +4075,7 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
/* Compute the alloc type flags */
- if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)) || - (type & ~(MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_RESET))) + if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_RESET))) { WARN("called with wrong alloc type flags (%08x) !\n", (int)type); return STATUS_INVALID_PARAMETER; @@ -4080,6 +4083,12 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
if (!arm64ec_map && (attributes & MEM_EXTENDED_PARAMETER_EC_CODE)) return STATUS_INVALID_PARAMETER;
+ if (type & MEM_RESERVE_PLACEHOLDER && (protect != PAGE_NOACCESS)) + { + WARN( "Wrong protect %#x for placeholder.\n", (unsigned int)protect ); + return STATUS_INVALID_PARAMETER; + } + /* Reserve the memory */
server_enter_uninterrupted_section( &virtual_mutex, &sigset ); @@ -4090,6 +4099,7 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ { if (type & MEM_COMMIT) vprot |= VPROT_COMMITTED; if (type & MEM_WRITE_WATCH) vprot |= VPROT_WRITEWATCH; + if (type & MEM_RESERVE_PLACEHOLDER) vprot |= VPROT_PLACEHOLDER | VPROT_FREE_PLACEHOLDER; if (protect & PAGE_NOCACHE) vprot |= SEC_NOCACHE;
if (vprot & VPROT_WRITECOPY) status = STATUS_INVALID_PAGE_PROTECTION; @@ -4109,6 +4119,7 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ { if (!(view = find_view( base, size ))) status = STATUS_NOT_MAPPED_VIEW; else if (view->protect & SEC_FILE) status = STATUS_ALREADY_COMMITTED; + else if (view->protect & VPROT_FREE_PLACEHOLDER) status = STATUS_CONFLICTING_ADDRESSES; else if (!(status = set_protection( view, base, size, protect )) && (view->protect & SEC_RESERVE)) { SERVER_START_REQ( add_mapping_committed_range ) @@ -4144,6 +4155,7 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR zero_bits, SIZE_T *size_ptr, ULONG type, ULONG protect ) { + static const ULONG type_mask = MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_RESET; ULONG_PTR limit;
TRACE("%p %p %08lx %x %08x\n", process, *ret, *size_ptr, (int)type, (int)protect ); @@ -4155,6 +4167,12 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z if (!is_old_wow64() && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; #endif
+ if (type & ~type_mask) + { + WARN("Called with wrong alloc type flags %08x.\n", (int)type); + return STATUS_INVALID_PARAMETER; + } + if (process != NtCurrentProcess()) { apc_call_t call; @@ -4265,6 +4283,8 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s ULONG protect, MEM_EXTENDED_PARAMETER *parameters, ULONG count ) { + static const ULONG type_mask = MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_WRITE_WATCH + | MEM_RESET | MEM_RESERVE_PLACEHOLDER; ULONG_PTR limit = 0; ULONG_PTR align = 0; ULONG attributes = 0; @@ -4277,6 +4297,12 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s status = get_extended_params( parameters, count, &limit, &align, &attributes, &machine ); if (status) return status;
+ if (type & ~type_mask) + { + WARN( "Called with wrong alloc type flags %08x.\n", (unsigned int)type ); + return STATUS_INVALID_PARAMETER; + } + if (*ret && (align || limit)) return STATUS_INVALID_PARAMETER; if (!*size_ptr) return STATUS_INVALID_PARAMETER;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/virtual.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 2aadb1def00..2f74e6e0896 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1980,6 +1980,36 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, void *ptr; NTSTATUS status;
+ if (alloc_type & MEM_REPLACE_PLACEHOLDER) + { + if (!(*view_ret = find_view( base, 0 ))) + { + TRACE( "MEM_REPLACE_PLACEHOLDER view not found.\n" ); + return STATUS_INVALID_PARAMETER; + } + TRACE( "found view %p, size %p, protect %#x.\n", + (*view_ret)->base, (void *)(*view_ret)->size, (*view_ret)->protect ); + + if ((*view_ret)->base != base || (*view_ret)->size != size) + { + WARN( "Wrond range.\n" ); + return STATUS_CONFLICTING_ADDRESSES; + } + + if (!((*view_ret)->protect & VPROT_FREE_PLACEHOLDER)) + { + TRACE( "Wrong protect %#x for MEM_REPLACE_PLACEHOLDER.\n", (*view_ret)->protect ); + return STATUS_INVALID_PARAMETER; + } + (*view_ret)->protect = vprot | VPROT_PLACEHOLDER; + + if (!set_vprot( *view_ret, base, size, vprot )) + ERR( "set_protection failed.\n" ); + if (vprot & VPROT_WRITEWATCH) + reset_write_watches( base, size ); + return STATUS_SUCCESS; + } + if (base) { if (is_beyond_limit( base, size, address_space_limit ))
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/virtual.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 2f74e6e0896..ecceca6405e 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4081,7 +4081,7 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
if (*ret) { - if (type & MEM_RESERVE) /* Round down to 64k boundary */ + if (type & MEM_RESERVE && !(type & MEM_REPLACE_PLACEHOLDER)) /* Round down to 64k boundary */ base = ROUND_ADDR( *ret, granularity_mask ); else base = ROUND_ADDR( *ret, page_mask ); @@ -4105,7 +4105,8 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ
/* Compute the alloc type flags */
- if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_RESET))) + if (!(type & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)) + || (type & MEM_REPLACE_PLACEHOLDER && !(type & MEM_RESERVE))) { WARN("called with wrong alloc type flags (%08x) !\n", (int)type); return STATUS_INVALID_PARAMETER; @@ -4314,7 +4315,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s ULONG count ) { static const ULONG type_mask = MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN | MEM_WRITE_WATCH - | MEM_RESET | MEM_RESERVE_PLACEHOLDER; + | MEM_RESET | MEM_RESERVE_PLACEHOLDER | MEM_REPLACE_PLACEHOLDER; ULONG_PTR limit = 0; ULONG_PTR align = 0; ULONG attributes = 0;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/tests/process.c | 35 +++++++++++------------- dlls/ntdll/tests/virtual.c | 19 +++++-------- dlls/ntdll/unix/virtual.c | 47 ++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 36 deletions(-)
diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c index f7b321cc09f..b629b61ea5d 100644 --- a/dlls/kernelbase/tests/process.c +++ b/dlls/kernelbase/tests/process.c @@ -178,14 +178,14 @@ static void test_VirtualAlloc2(void) ok(info.RegionSize == 2 * size, "Unexpected size.\n");
ret = VirtualFree(placeholder1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(ret, "Failed to split placeholder.\n"); + ok(ret, "Failed to split placeholder.\n");
memset(&info, 0, sizeof(info)); VirtualQuery(placeholder1, &info, sizeof(info)); ok(info.AllocationProtect == PAGE_NOACCESS, "Unexpected protection %#lx.\n", info.AllocationProtect); ok(info.State == MEM_RESERVE, "Unexpected state %#lx.\n", info.State); ok(info.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", info.Type); - todo_wine ok(info.RegionSize == size, "Unexpected size.\n"); + ok(info.RegionSize == size, "Unexpected size.\n");
placeholder2 = (void *)((BYTE *)placeholder1 + size); memset(&info, 0, sizeof(info)); @@ -199,10 +199,10 @@ static void test_VirtualAlloc2(void) ok(!!section, "Failed to create a section.\n");
view1 = pMapViewOfFile3(section, NULL, placeholder1, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); - todo_wine ok(!!view1, "Failed to map a section.\n"); + ok(!!view1, "Failed to map a section.\n");
view2 = pMapViewOfFile3(section, NULL, placeholder2, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); - todo_wine ok(!!view2, "Failed to map a section.\n"); + ok(!!view2, "Failed to map a section.\n");
CloseHandle(section); UnmapViewOfFile(view1); @@ -218,19 +218,16 @@ static void test_VirtualAlloc2(void) p1 = p + size / 2; p2 = p1 + size / 4; ret = VirtualFree(p1, size / 4, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(ret, "Failed to split a placeholder.\n"); - if (ret) - { - check_region_size(p, size / 2); - check_region_size(p1, size / 4); - } + ok(ret, "Failed to split a placeholder.\n"); + check_region_size(p, size / 2); + check_region_size(p1, size / 4); check_region_size(p2, 2 * size - size / 2 - size / 4); ret = VirtualFree(p, 0, MEM_RELEASE); ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p1, 0, MEM_RELEASE); - todo_wine ok(ret, "Failed to release a region.\n"); + ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p2, 0, MEM_RELEASE); - todo_wine ok(ret, "Failed to release a region.\n"); + ok(ret, "Failed to release a region.\n");
/* Split in two regions, specifying lower part. */ p = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE_PLACEHOLDER | MEM_RESERVE, PAGE_NOACCESS, NULL, 0); @@ -239,14 +236,13 @@ static void test_VirtualAlloc2(void) p1 = p; p2 = p + size / 2; ret = VirtualFree(p1, size / 2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(ret, "Failed to split a placeholder.\n"); - if (ret) - check_region_size(p1, size / 2); + ok(ret, "Failed to split a placeholder.\n"); + check_region_size(p1, size / 2); check_region_size(p2, 2 * size - size / 2); ret = VirtualFree(p1, 0, MEM_RELEASE); ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p2, 0, MEM_RELEASE); - todo_wine ok(ret, "Failed to release a region.\n"); + ok(ret, "Failed to release a region.\n");
/* Split in two regions, specifying second half. */ p = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE_PLACEHOLDER | MEM_RESERVE, PAGE_NOACCESS, NULL, 0); @@ -255,14 +251,13 @@ static void test_VirtualAlloc2(void) p1 = p; p2 = p + size; ret = VirtualFree(p2, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(ret, "Failed to split a placeholder.\n"); - if (ret) - check_region_size(p1, size); + ok(ret, "Failed to split a placeholder.\n"); + check_region_size(p1, size); check_region_size(p2, size); ret = VirtualFree(p1, 0, MEM_RELEASE); ok(ret, "Failed to release a region.\n"); ret = VirtualFree(p2, 0, MEM_RELEASE); - todo_wine ok(ret, "Failed to release a region.\n"); + ok(ret, "Failed to release a region.\n"); }
static void test_VirtualAllocFromApp(void) diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 604e234b100..1718aaf30f9 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -354,13 +354,10 @@ static void test_NtAllocateVirtualMemoryEx(void) p2 = p1 + size / 4; size2 = size / 4; status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
- if (status == STATUS_SUCCESS) - { - check_region_size(p, size / 2); - check_region_size(p1, size / 4); - } + check_region_size(p, size / 2); + check_region_size(p1, size / 4); check_region_size(p2, size - size / 2 - size / 4);
status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size2, MEM_RELEASE); @@ -381,11 +378,10 @@ static void test_NtAllocateVirtualMemoryEx(void) p2 = p1 + size / 4; size2 = size / 4; status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ok(p1 == addr1, "Unexpected address.\n");
- if (status == STATUS_SUCCESS) - check_region_size(p1, size / 4); + check_region_size(p1, size / 4); check_region_size(p2, size - size / 4); status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); @@ -404,10 +400,9 @@ static void test_NtAllocateVirtualMemoryEx(void)
size2 = size / 2; status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); - todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ok(p2 == p1 + size / 2, "Unexpected address.\n"); - if (status == STATUS_SUCCESS) - check_region_size(p1, size / 2); + check_region_size(p1, size / 2); check_region_size(p2, size / 2); status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index ecceca6405e..2560922df9d 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2201,18 +2201,43 @@ static NTSTATUS decommit_pages( struct file_view *view, size_t start, size_t siz }
+/*********************************************************************** + * view_make_placeholder + * + * Setup placeholder view. + * virtual_mutex must be held by caller. + */ +static void view_make_placeholder( struct file_view *view ) +{ + view->protect = VPROT_PLACEHOLDER | VPROT_FREE_PLACEHOLDER; + set_page_vprot( view->base, view->size, 0 ); + if (arm64ec_map) clear_arm64ec_range( view->base, view->size ); + if (anon_mmap_fixed( view->base, view->size, PROT_NONE, 0 ) != view->base) + ERR( "anon_mmap_fixed failed, err %s.\n", strerror( errno )); +} + + /*********************************************************************** * free_pages * * Free some pages of a given view. * virtual_mutex must be held by caller. */ -static NTSTATUS free_pages( struct file_view *view, char *base, size_t size ) +static NTSTATUS free_pages( struct file_view *view, char *base, size_t size, BOOL preserve_placeholder ) { + if (preserve_placeholder) + { + if (!size) return STATUS_INVALID_PARAMETER_3; + if (!(view->protect & VPROT_PLACEHOLDER)) return STATUS_CONFLICTING_ADDRESSES; + if (view->protect & VPROT_FREE_PLACEHOLDER && size == view->size) return STATUS_CONFLICTING_ADDRESSES; + } + else if (!size) size = view->size; + if (size == view->size) { assert( base == view->base ); - delete_view( view ); + if (preserve_placeholder) view_make_placeholder( view ); + else delete_view( view ); return STATUS_SUCCESS; } if (view->base != base && base + size != (char *)view->base + view->size) @@ -2250,6 +2275,20 @@ static NTSTATUS free_pages( struct file_view *view, char *base, size_t size ) VIRTUAL_DEBUG_DUMP_VIEW( view ); }
+ if (preserve_placeholder) + { + if (!(view = alloc_view())) + { + ERR( "Out of memory for %p-%p\n", base, base + size ); + return STATUS_NO_MEMORY; + } + view->base = base; + view->size = size; + view_make_placeholder( view ); + register_view( view ); + return STATUS_SUCCESS; + } + set_page_vprot( base, size, 0 ); if (arm64ec_map) clear_arm64ec_range( base, size ); unmap_area( base, size ); @@ -4423,10 +4462,10 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si else if (!size && base != view->base) status = STATUS_FREE_VM_NOT_AT_BASE; else if ((char *)view->base + view->size - base < size) status = STATUS_UNABLE_TO_FREE_VM; else if (type == MEM_DECOMMIT) status = decommit_pages( view, base - (char *)view->base, size ); - else if (type == MEM_RELEASE) + else if (type == MEM_RELEASE || (type == (MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER))) { + status = free_pages( view, base, size, type & MEM_PRESERVE_PLACEHOLDER ); if (!size) size = view->size; - status = free_pages( view, base, size ); } else status = STATUS_INVALID_PARAMETER;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/tests/process.c | 14 +++ dlls/ntdll/tests/virtual.c | 150 +++++++++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c index b629b61ea5d..e5185a2587d 100644 --- a/dlls/kernelbase/tests/process.c +++ b/dlls/kernelbase/tests/process.c @@ -204,6 +204,20 @@ static void test_VirtualAlloc2(void) view2 = pMapViewOfFile3(section, NULL, placeholder2, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); ok(!!view2, "Failed to map a section.\n");
+ memset(&info, 0, sizeof(info)); + VirtualQuery(placeholder1, &info, sizeof(info)); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %#lx.\n", info.AllocationProtect); + ok(info.State == MEM_COMMIT, "Unexpected state %#lx.\n", info.State); + ok(info.Type == MEM_MAPPED, "Unexpected type %#lx.\n", info.Type); + ok(info.RegionSize == size, "Unexpected size.\n"); + + memset(&info, 0, sizeof(info)); + VirtualQuery(placeholder2, &info, sizeof(info)); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %#lx.\n", info.AllocationProtect); + ok(info.State == MEM_COMMIT, "Unexpected state %#lx.\n", info.State); + ok(info.Type == MEM_MAPPED, "Unexpected type %#lx.\n", info.Type); + ok(info.RegionSize == size, "Unexpected size.\n"); + CloseHandle(section); UnmapViewOfFile(view1); UnmapViewOfFile(view2); diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 1718aaf30f9..45ba58868df 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -293,10 +293,14 @@ static void check_region_size_(void *p, SIZE_T s, unsigned int line)
static void test_NtAllocateVirtualMemoryEx(void) { + MEMORY_BASIC_INFORMATION mbi; MEM_EXTENDED_PARAMETER ext[2]; + void *addresses[16]; SIZE_T size, size2; char *p, *p1, *p2; + ULONG granularity; NTSTATUS status; + ULONG_PTR count; void *addr1;
if (!pNtAllocateVirtualMemoryEx) @@ -332,15 +336,129 @@ static void test_NtAllocateVirtualMemoryEx(void) status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS); ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status);
+ status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
- size = 0; - status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); - ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(!status, "Unexpected status %08lx.\n", status); + + memset(addr1, 0xcc, size); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(!status, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READONLY, NULL, 0); + ok(!status, "Unexpected status %08lx.\n", status); + + ok(!*(unsigned int *)addr1, "Got %#x.\n", *(unsigned int *)addr1); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr1, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(mbi.AllocationProtect == PAGE_READONLY, "Unexpected protection %#lx.\n", mbi.AllocationProtect); + ok(mbi.State == MEM_COMMIT, "Unexpected state %#lx.\n", mbi.State); + ok(mbi.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", mbi.Type); + ok(mbi.RegionSize == 0x10000, "Unexpected size.\n"); + + size = 0x10000; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(!status, "Unexpected status %08lx.\n", status); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr1, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(mbi.AllocationProtect == PAGE_NOACCESS, "Unexpected protection %#lx.\n", mbi.AllocationProtect); + ok(mbi.State == MEM_RESERVE, "Unexpected state %#lx.\n", mbi.State); + ok(mbi.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", mbi.Type); + ok(mbi.RegionSize == 0x10000, "Unexpected size.\n"); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size = 0x1000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_COMMIT, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, + MEM_WRITE_WATCH | MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_READONLY, NULL, 0); + ok(!status || broken(status == STATUS_INVALID_PARAMETER) /* Win10 1809, the version where + NtAllocateVirtualMemoryEx is introduced */, "Unexpected status %08lx.\n", status); + + if (!status) + { + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_COMMIT, PAGE_READWRITE, NULL, 0); + ok(!status, "Unexpected status %08lx.\n", status); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr1, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(mbi.AllocationProtect == PAGE_READONLY, "Unexpected protection %#lx.\n", mbi.AllocationProtect); + ok(mbi.State == MEM_COMMIT, "Unexpected state %#lx.\n", mbi.State); + ok(mbi.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", mbi.Type); + ok(mbi.RegionSize == 0x10000, "Unexpected size.\n"); + + size = 0x10000; + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( NtCurrentProcess(), WRITE_WATCH_FLAG_RESET, addr1, size, + addresses, &count, &granularity ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!count, "Unexpected count %u.\n", (unsigned int)count); + *((char *)addr1 + 0x1000) = 1; + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( NtCurrentProcess(), WRITE_WATCH_FLAG_RESET, addr1, size, + addresses, &count, &granularity ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(count == 1, "Unexpected count %u.\n", (unsigned int)count); + ok(addresses[0] == (char *)addr1 + 0x1000, "Unexpected address %p.\n", addresses[0]); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + }
/* Placeholder region splitting. */ + addr1 = NULL; + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + p = addr1; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + ok(p == addr1, "Unexpected addr %p, expected %p.\n", p, addr1); +
/* Split in three regions. */ addr1 = NULL; @@ -349,12 +467,17 @@ static void test_NtAllocateVirtualMemoryEx(void) PAGE_NOACCESS, NULL, 0); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
+ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + p = addr1; p1 = p + size / 2; p2 = p1 + size / 4; size2 = size / 4; status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == p + size / 2, "Unexpected addr %p, expected %p.\n", p, p + size / 2);
check_region_size(p, size / 2); check_region_size(p1, size / 4); @@ -362,10 +485,16 @@ static void test_NtAllocateVirtualMemoryEx(void)
status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p == addr1, "Unexpected addr %p, expected %p.\n", p, addr1); status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == p + size / 2, "Unexpected addr %p, expected %p.\n", p1, p + size / 2); status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 4, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 4);
/* Split in two regions, specifying lower part. */ addr1 = NULL; @@ -374,12 +503,19 @@ static void test_NtAllocateVirtualMemoryEx(void) PAGE_NOACCESS, NULL, 0); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status);
+ size2 = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_INVALID_PARAMETER_3, "Unexpected status %08lx.\n", status); + ok(!size2, "Unexpected size %#Ix.\n", size2); + p1 = addr1; p2 = p1 + size / 4; size2 = size / 4; status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ok(p1 == addr1, "Unexpected address.\n"); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1);
check_region_size(p1, size / 4); check_region_size(p2, size - size / 4); @@ -394,6 +530,7 @@ static void test_NtAllocateVirtualMemoryEx(void) status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size);
p1 = addr1; p2 = p1 + size / 2; @@ -401,13 +538,18 @@ static void test_NtAllocateVirtualMemoryEx(void) size2 = size / 2; status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); - ok(p2 == p1 + size / 2, "Unexpected address.\n"); + ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 2, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 2); check_region_size(p1, size / 2); check_region_size(p2, size / 2); status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 2, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 2);
memset( ext, 0, sizeof(ext) ); ext[0].Type = MemExtendedParameterAttributeFlags;
v2: - rebased on top of upstream changes.