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;