diff --git a/patches/ntdll-Placeholders/0001-ntdll-tests-Add-tests-for-freeing-a-part-of-view.patch b/patches/ntdll-Placeholders/0001-ntdll-tests-Add-tests-for-freeing-a-part-of-view.patch new file mode 100644 index 00000000..94590314 --- /dev/null +++ b/patches/ntdll-Placeholders/0001-ntdll-tests-Add-tests-for-freeing-a-part-of-view.patch @@ -0,0 +1,105 @@ +From 7c13335d926a1b47f7cf88065d641c4d5fa80a59 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 17:51:26 -0600 +Subject: [PATCH] ntdll/tests: Add tests for freeing a part of view. + +--- + dlls/ntdll/tests/virtual.c | 70 ++++++++++++++++++++++++++++++++++++-- + 1 file changed, 67 insertions(+), 3 deletions(-) + +diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c +index 6831fe3c522..8e94566fb7f 100644 +--- a/dlls/ntdll/tests/virtual.c ++++ b/dlls/ntdll/tests/virtual.c +@@ -1639,21 +1639,85 @@ static void test_syscalls(void) + + static void test_NtFreeVirtualMemory(void) + { ++ void *addr1, *addr; + NTSTATUS status; +- void *addr1; + SIZE_T size; + + size = 0x10000; + addr1 = NULL; +- status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, MEM_RESERVE, PAGE_READWRITE); ++ status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size = 0; + status = NtFreeVirtualMemory(NULL, &addr1, &size, MEM_RELEASE); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %08lx.\n", status); + ++ addr = (char *)addr1 + 0x1000; ++ size = 0; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); ++ ok(status == STATUS_FREE_VM_NOT_AT_BASE, "Unexpected status %08lx.\n", status); ++ ++ size = 0x11000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); +- ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ++ addr = (char *)addr1 + 0x1001; ++ size = 0xffff; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ok(size == 0xffff, "Unexpected size %p.\n", (void *)size); ++ ok(addr == (char *)addr1 + 0x1001, "Got addr %p, addr1 %p.\n", addr, addr1); ++ ++ size = 0xfff; ++ addr = (char *)addr1 + 0x1001; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ *(volatile char *)addr1 = 1; ++ *((volatile char *)addr1 + 0x2000) = 1; ++ todo_wine ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); ++ todo_wine ok(addr == (char *)addr1 + 0x1000, "Got addr %p, addr1 %p.\n", addr, addr1); ++ ++ size = 0xfff; ++ addr = (char *)addr1 + 1; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ *((volatile char *)addr1 + 0x2000) = 1; ++ todo_wine ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); ++ todo_wine ok(addr == addr1, "Got addr %p, addr1 %p.\n", addr, addr1); ++ ++ size = 0x1000; ++ addr = addr1; ++ status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); ++ todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ok(addr == addr1, "Unexpected addr %p, addr1 %p.\n", addr, addr1); ++ ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); ++ ++ size = 0x10000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_DECOMMIT); ++ todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ++ size = 0x10000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ++ size = 0; ++ addr = (char *)addr1 + 0x1000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); ++ ++ size = 0x1000; ++ addr = (char *)addr1 + 0x1000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_DECOMMIT); ++ todo_wine ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); ++ ++ size = 0; ++ addr = (char *)addr1 + 0x2000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ++ size = 0x1000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); ++ todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + } + + static void test_prefetch(void) +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0002-kernelbase-Validate-nonzero-size-for-MEM_RELEASE-in-.patch b/patches/ntdll-Placeholders/0002-kernelbase-Validate-nonzero-size-for-MEM_RELEASE-in-.patch new file mode 100644 index 00000000..6b731c98 --- /dev/null +++ b/patches/ntdll-Placeholders/0002-kernelbase-Validate-nonzero-size-for-MEM_RELEASE-in-.patch @@ -0,0 +1,30 @@ +From bc937422e5f600be650e21de5f6c9b8656d5c23c Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 17:56:42 -0600 +Subject: [PATCH] kernelbase: Validate nonzero size for MEM_RELEASE in + VirtualFreeEx(). + +--- + dlls/kernelbase/memory.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/dlls/kernelbase/memory.c b/dlls/kernelbase/memory.c +index 2a503587e93..c01fe817972 100644 +--- a/dlls/kernelbase/memory.c ++++ b/dlls/kernelbase/memory.c +@@ -447,6 +447,12 @@ BOOL WINAPI DECLSPEC_HOTPATCH VirtualFree( void *addr, SIZE_T size, DWORD type ) + */ + BOOL WINAPI DECLSPEC_HOTPATCH VirtualFreeEx( HANDLE process, void *addr, SIZE_T size, DWORD type ) + { ++ if (type == MEM_RELEASE && size) ++ { ++ WARN( "Trying to release memory with specified size.\n" ); ++ SetLastError( ERROR_INVALID_PARAMETER ); ++ return FALSE; ++ } + return set_ntstatus( NtFreeVirtualMemory( process, &addr, &size, type )); + } + +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0003-ntdll-Fix-size-validation-in-NtFreeVirtualMemory.patch b/patches/ntdll-Placeholders/0003-ntdll-Fix-size-validation-in-NtFreeVirtualMemory.patch new file mode 100644 index 00000000..a34731c3 --- /dev/null +++ b/patches/ntdll-Placeholders/0003-ntdll-Fix-size-validation-in-NtFreeVirtualMemory.patch @@ -0,0 +1,117 @@ +From 22dc17764c3b231ebd226e479686a00c7aaaf01a Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 18:12:47 -0600 +Subject: [PATCH] ntdll: Fix size validation in NtFreeVirtualMemory(). + +--- + dlls/ntdll/tests/virtual.c | 16 ++++++++-------- + dlls/ntdll/unix/virtual.c | 26 ++++++++++++++++++-------- + 2 files changed, 26 insertions(+), 16 deletions(-) + +diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c +index 8e94566fb7f..81e9bd0bda3 100644 +--- a/dlls/ntdll/tests/virtual.c ++++ b/dlls/ntdll/tests/virtual.c +@@ -1659,12 +1659,12 @@ static void test_NtFreeVirtualMemory(void) + + size = 0x11000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + addr = (char *)addr1 + 0x1001; + size = 0xffff; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + ok(size == 0xffff, "Unexpected size %p.\n", (void *)size); + ok(addr == (char *)addr1 + 0x1001, "Got addr %p, addr1 %p.\n", addr, addr1); + +@@ -1674,16 +1674,16 @@ static void test_NtFreeVirtualMemory(void) + todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *(volatile char *)addr1 = 1; + *((volatile char *)addr1 + 0x2000) = 1; +- todo_wine ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); +- todo_wine ok(addr == (char *)addr1 + 0x1000, "Got addr %p, addr1 %p.\n", addr, addr1); ++ ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); ++ ok(addr == (char *)addr1 + 0x1000, "Got addr %p, addr1 %p.\n", addr, addr1); + + size = 0xfff; + addr = (char *)addr1 + 1; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *((volatile char *)addr1 + 0x2000) = 1; +- todo_wine ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); +- todo_wine ok(addr == addr1, "Got addr %p, addr1 %p.\n", addr, addr1); ++ ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); ++ ok(addr == addr1, "Got addr %p, addr1 %p.\n", addr, addr1); + + size = 0x1000; + addr = addr1; +@@ -1703,12 +1703,12 @@ static void test_NtFreeVirtualMemory(void) + size = 0; + addr = (char *)addr1 + 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); + + size = 0x1000; + addr = (char *)addr1 + 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_DECOMMIT); +- todo_wine ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); + + size = 0; + addr = (char *)addr1 + 0x2000; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 24d5a4e2da8..4cb0f349ffc 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -4127,26 +4127,36 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + if (addr == (void *)1 && !size && type == MEM_RELEASE) virtual_release_address_space(); + else status = STATUS_INVALID_PARAMETER; + } +- else if (!(view = find_view( base, size )) || !is_view_valloc( view )) +- { +- status = STATUS_INVALID_PARAMETER; +- } ++ else if (!(view = find_view( base, 0 ))) status = STATUS_MEMORY_NOT_ALLOCATED; ++ else if (!is_view_valloc( view )) status = STATUS_INVALID_PARAMETER; + else if (type == MEM_RELEASE) + { + /* Free the pages */ + +- if (size) status = STATUS_INVALID_PARAMETER; +- else if (base != view->base) status = STATUS_FREE_VM_NOT_AT_BASE; ++ if (size && (char *)view->base + view->size - base < size) status = STATUS_UNABLE_TO_FREE_VM; ++ else if (!size && base != view->base) status = STATUS_FREE_VM_NOT_AT_BASE; + else + { ++ if (!size) size = view->size; ++ ++ if (size == view->size) ++ { ++ assert( base == view->base ); ++ delete_view( view ); ++ } ++ else ++ { ++ FIXME( "Parial view release is not supported.\n" ); ++ status = STATUS_INVALID_PARAMETER; ++ } + *addr_ptr = base; +- *size_ptr = view->size; +- delete_view( view ); ++ *size_ptr = size; + } + } + else if (type == MEM_DECOMMIT) + { + if (!size && base != view->base) status = STATUS_FREE_VM_NOT_AT_BASE; ++ else if (base - (char *)view->base + size > view->size) status = STATUS_UNABLE_TO_FREE_VM; + else status = decommit_pages( view, base - (char *)view->base, size ); + if (status == STATUS_SUCCESS) + { +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0004-ntdll-Fully-support-unaligned-views-in-free-ranges-m.patch b/patches/ntdll-Placeholders/0004-ntdll-Fully-support-unaligned-views-in-free-ranges-m.patch new file mode 100644 index 00000000..dd4fae63 --- /dev/null +++ b/patches/ntdll-Placeholders/0004-ntdll-Fully-support-unaligned-views-in-free-ranges-m.patch @@ -0,0 +1,81 @@ +From c97697f6bbc3c9671210e480f2fcfe761b54893d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 18:30:20 -0600 +Subject: [PATCH] ntdll: Fully support unaligned views in free ranges + management. + +--- + dlls/ntdll/unix/virtual.c | 41 ++++++++++++++++++++------------------- + 1 file changed, 21 insertions(+), 20 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 4cb0f349ffc..2dca5dfa45f 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -749,18 +749,19 @@ static void free_ranges_insert_view( struct file_view *view ) + assert( range != free_ranges_end ); + assert( range->end > view_base || next != free_ranges_end ); + +- /* this happens because virtual_alloc_thread_stack shrinks a view, then creates another one on top, +- * or because AT_ROUND_TO_PAGE was used with NtMapViewOfSection to force 4kB aligned mapping. */ +- if ((range->end > view_base && range->base >= view_end) || +- (range->end == view_base && next->base >= view_end)) +- { +- /* on Win64, assert that it's correctly aligned so we're not going to be in trouble later */ +-#ifdef _WIN64 +- assert( view->base == view_base ); +-#endif +- WARN( "range %p - %p is already mapped\n", view_base, view_end ); ++ /* Free ranges addresses are aligned at granularity_mask while the views may be not. */ ++ ++ if (range->base > view_base) ++ view_base = range->base; ++ if (range->end < view_end) ++ view_end = range->end; ++ if (range->end == view_base && next->base >= view_end) ++ view_end = view_base; ++ ++ TRACE( "%p - %p, aligned %p - %p.\n", view->base, (char *)view->base + view->size, view_base, view_end ); ++ ++ if (view_end <= view_base) + return; +- } + + /* this should never happen */ + if (range->base > view_base || range->end < view_end) +@@ -810,9 +811,7 @@ static void free_ranges_remove_view( struct file_view *view ) + struct range_entry *range = free_ranges_lower_bound( view_base ); + struct range_entry *next = range + 1; + +- /* It's possible to use AT_ROUND_TO_PAGE on 32bit with NtMapViewOfSection to force 4kB alignment, +- * and this breaks our assumptions. Look at the views around to check if the range is still in use. */ +-#ifndef _WIN64 ++ /* Free ranges addresses are aligned at granularity_mask while the views may be not. */ + struct file_view *prev_view = RB_ENTRY_VALUE( rb_prev( &view->entry ), struct file_view, entry ); + struct file_view *next_view = RB_ENTRY_VALUE( rb_next( &view->entry ), struct file_view, entry ); + void *prev_view_base = prev_view ? ROUND_ADDR( prev_view->base, granularity_mask ) : NULL; +@@ -820,13 +819,15 @@ static void free_ranges_remove_view( struct file_view *view ) + void *next_view_base = next_view ? ROUND_ADDR( next_view->base, granularity_mask ) : NULL; + void *next_view_end = next_view ? ROUND_ADDR( (char *)next_view->base + next_view->size + granularity_mask, granularity_mask ) : NULL; + +- if ((prev_view_base < view_end && prev_view_end > view_base) || +- (next_view_base < view_end && next_view_end > view_base)) +- { +- WARN( "range %p - %p is still mapped\n", view_base, view_end ); ++ if (prev_view_end && prev_view_end > view_base && prev_view_base < view_end) ++ view_base = prev_view_end; ++ if (next_view_base && next_view_base < view_end && next_view_end > view_base) ++ view_end = next_view_base; ++ ++ TRACE( "%p - %p, aligned %p - %p.\n", view->base, (char *)view->base + view->size, view_base, view_end ); ++ ++ if (view_end <= view_base) + return; +- } +-#endif + + /* free_ranges initial value is such that the view is either inside range or before another one. */ + assert( range != free_ranges_end ); +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0005-ntdll-Factor-out-some-view-manipulation-functions.patch b/patches/ntdll-Placeholders/0005-ntdll-Factor-out-some-view-manipulation-functions.patch new file mode 100644 index 00000000..e161f688 --- /dev/null +++ b/patches/ntdll-Placeholders/0005-ntdll-Factor-out-some-view-manipulation-functions.patch @@ -0,0 +1,85 @@ +From 4baf5566da8495ef3b260783ed88f175e43b4f70 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 18:37:43 -0600 +Subject: [PATCH] ntdll: Factor out some view manipulation functions. + +--- + dlls/ntdll/unix/virtual.c | 47 +++++++++++++++++++++++++++++++++------ + 1 file changed, 40 insertions(+), 7 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 2dca5dfa45f..bdc410472ec 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -1563,6 +1563,31 @@ static struct file_view *alloc_view(void) + } + + ++/*********************************************************************** ++ * free_view ++ * ++ * Free memory for view structure. virtual_mutex must be held by caller. ++ */ ++static void free_view( struct file_view *view ) ++{ ++ *(struct file_view **)view = next_free_view; ++ next_free_view = view; ++} ++ ++ ++/*********************************************************************** ++ * unregister_view ++ * ++ * Remove view from the tree and update free ranges. virtual_mutex must be held by caller. ++ */ ++static void unregister_view( struct file_view *view ) ++{ ++ if (mmap_is_in_reserved_area( view->base, view->size )) ++ free_ranges_remove_view( view ); ++ wine_rb_remove( &views_tree, &view->entry ); ++} ++ ++ + /*********************************************************************** + * delete_view + * +@@ -1572,11 +1597,21 @@ static void delete_view( struct file_view *view ) /* [in] View */ + { + if (!(view->protect & VPROT_SYSTEM)) unmap_area( view->base, view->size ); + set_page_vprot( view->base, view->size, 0 ); ++ unregister_view( view ); ++ free_view( view ); ++} ++ ++ ++/*********************************************************************** ++ * register_view ++ * ++ * Add view to the tree and update free ranges. virtual_mutex must be held by caller. ++ */ ++static void register_view( struct file_view *view ) ++{ ++ wine_rb_put( &views_tree, view->base, &view->entry ); + if (mmap_is_in_reserved_area( view->base, view->size )) +- free_ranges_remove_view( view ); +- wine_rb_remove( &views_tree, &view->entry ); +- *(struct file_view **)view = next_free_view; +- next_free_view = view; ++ free_ranges_insert_view( view ); + } + + +@@ -1620,9 +1655,7 @@ static NTSTATUS create_view( struct file_view **view_ret, void *base, size_t siz + view->protect = vprot; + set_page_vprot( base, size, vprot ); + +- wine_rb_put( &views_tree, view->base, &view->entry ); +- if (mmap_is_in_reserved_area( view->base, view->size )) +- free_ranges_insert_view( view ); ++ register_view( view ); + + *view_ret = view; + +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0006-ntdll-Support-partial-view-release-in-NtFreeVirtualM.patch b/patches/ntdll-Placeholders/0006-ntdll-Support-partial-view-release-in-NtFreeVirtualM.patch new file mode 100644 index 00000000..392a0adc --- /dev/null +++ b/patches/ntdll-Placeholders/0006-ntdll-Support-partial-view-release-in-NtFreeVirtualM.patch @@ -0,0 +1,123 @@ +From 261ba5e0003f13c83f9f08e5b3931862586f46d3 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 18:41:50 -0600 +Subject: [PATCH] ntdll: Support partial view release in NtFreeVirtualMemory(). + +--- + dlls/ntdll/tests/virtual.c | 14 ++++++------- + dlls/ntdll/unix/virtual.c | 42 ++++++++++++++++++++++++++++++++++++-- + 2 files changed, 47 insertions(+), 9 deletions(-) + +diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c +index 81e9bd0bda3..0dccb35bd27 100644 +--- a/dlls/ntdll/tests/virtual.c ++++ b/dlls/ntdll/tests/virtual.c +@@ -1671,7 +1671,7 @@ static void test_NtFreeVirtualMemory(void) + size = 0xfff; + addr = (char *)addr1 + 0x1001; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *(volatile char *)addr1 = 1; + *((volatile char *)addr1 + 0x2000) = 1; + ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); +@@ -1680,7 +1680,7 @@ static void test_NtFreeVirtualMemory(void) + size = 0xfff; + addr = (char *)addr1 + 1; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *((volatile char *)addr1 + 0x2000) = 1; + ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); + ok(addr == addr1, "Got addr %p, addr1 %p.\n", addr, addr1); +@@ -1688,17 +1688,17 @@ static void test_NtFreeVirtualMemory(void) + size = 0x1000; + addr = addr1; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +- todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(addr == addr1, "Unexpected addr %p, addr1 %p.\n", addr, addr1); + ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); + + size = 0x10000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_DECOMMIT); +- todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + size = 0; + addr = (char *)addr1 + 0x1000; +@@ -1713,11 +1713,11 @@ static void test_NtFreeVirtualMemory(void) + size = 0; + addr = (char *)addr1 + 0x2000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size = 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); +- todo_wine ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + } + + static void test_prefetch(void) +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index bdc410472ec..5775287cd19 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -4180,8 +4180,46 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + } + else + { +- FIXME( "Parial view release is not supported.\n" ); +- status = STATUS_INVALID_PARAMETER; ++ struct file_view *new_view = NULL; ++ ++ if (view->base != base && base + size != (char *)view->base + view->size ++ && !(new_view = alloc_view())) ++ { ++ ERR( "out of memory for %p-%p\n", base, (char *)base + size ); ++ return STATUS_NO_MEMORY; ++ } ++ unregister_view( view ); ++ ++ if (new_view) ++ { ++ new_view->base = base + size; ++ new_view->size = (char *)view->base + view->size - (char *)new_view->base; ++ new_view->protect = view->protect; ++ ++ view->size = base - (char *)view->base; ++ register_view( view ); ++ register_view( new_view ); ++ ++ VIRTUAL_DEBUG_DUMP_VIEW( view ); ++ VIRTUAL_DEBUG_DUMP_VIEW( new_view ); ++ } ++ else ++ { ++ if (view->base == base) ++ { ++ view->base = base + size; ++ view->size -= size; ++ } ++ else ++ { ++ view->size = base - (char *)view->base; ++ } ++ register_view( view ); ++ VIRTUAL_DEBUG_DUMP_VIEW( view ); ++ } ++ ++ set_page_vprot( base, size, 0 ); ++ unmap_area( base, size ); + } + *addr_ptr = base; + *size_ptr = size; +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0007-ntdll-Add-logging-for-free-ranges.patch b/patches/ntdll-Placeholders/0007-ntdll-Add-logging-for-free-ranges.patch new file mode 100644 index 00000000..9e16726f --- /dev/null +++ b/patches/ntdll-Placeholders/0007-ntdll-Add-logging-for-free-ranges.patch @@ -0,0 +1,99 @@ +From aa5b49a7893ddb97b11251c65506d242e2e160f9 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Tue, 4 Oct 2022 20:26:39 -0500 +Subject: [PATCH] ntdll: Add logging for free ranges. + +--- + dlls/ntdll/unix/virtual.c | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 5775287cd19..7142d2adf79 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -76,6 +76,7 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(virtual); + WINE_DECLARE_DEBUG_CHANNEL(module); ++WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges); + + struct preload_info + { +@@ -186,6 +187,7 @@ static struct list teb_list = LIST_INIT( teb_list ); + #define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask) + + #define VIRTUAL_DEBUG_DUMP_VIEW(view) do { if (TRACE_ON(virtual)) dump_view(view); } while (0) ++#define VIRTUAL_DEBUG_DUMP_RANGES() do { if (TRACE_ON(virtual_ranges)) dump_free_ranges(); } while (0) + + #ifndef MAP_NORESERVE + #define MAP_NORESERVE 0 +@@ -732,6 +734,12 @@ static struct range_entry *free_ranges_lower_bound( void *addr ) + return begin; + } + ++static void dump_free_ranges(void) ++{ ++ struct range_entry *r; ++ for (r = free_ranges; r != free_ranges_end; ++r) ++ TRACE_(virtual_ranges)("%p - %p.\n", r->base, r->end); ++} + + /*********************************************************************** + * free_ranges_insert_view +@@ -761,7 +769,10 @@ static void free_ranges_insert_view( struct file_view *view ) + TRACE( "%p - %p, aligned %p - %p.\n", view->base, (char *)view->base + view->size, view_base, view_end ); + + if (view_end <= view_base) ++ { ++ VIRTUAL_DEBUG_DUMP_RANGES(); + return; ++ } + + /* this should never happen */ + if (range->base > view_base || range->end < view_end) +@@ -789,16 +800,19 @@ static void free_ranges_insert_view( struct file_view *view ) + else + range->base = view_end; + +- if (range->base < range->end) return; +- ++ if (range->base < range->end) ++ { ++ VIRTUAL_DEBUG_DUMP_RANGES(); ++ return; ++ } + /* and possibly remove it if it's now empty */ + memmove( range, next, (free_ranges_end - next) * sizeof(struct range_entry) ); + free_ranges_end -= 1; + assert( free_ranges_end - free_ranges > 0 ); + } ++ VIRTUAL_DEBUG_DUMP_RANGES(); + } + +- + /*********************************************************************** + * free_ranges_remove_view + * +@@ -827,8 +841,10 @@ static void free_ranges_remove_view( struct file_view *view ) + TRACE( "%p - %p, aligned %p - %p.\n", view->base, (char *)view->base + view->size, view_base, view_end ); + + if (view_end <= view_base) ++ { ++ VIRTUAL_DEBUG_DUMP_RANGES(); + return; +- ++ } + /* free_ranges initial value is such that the view is either inside range or before another one. */ + assert( range != free_ranges_end ); + assert( range->end > view_base || next != free_ranges_end ); +@@ -870,6 +886,7 @@ static void free_ranges_remove_view( struct file_view *view ) + range->base = view_base; + range->end = view_end; + } ++ VIRTUAL_DEBUG_DUMP_RANGES(); + } + + +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0008-ntdll-Handle-NULL-process-handle-in-MapViewOfFile3.patch b/patches/ntdll-Placeholders/0008-ntdll-Handle-NULL-process-handle-in-MapViewOfFile3.patch new file mode 100644 index 00000000..e68deab4 --- /dev/null +++ b/patches/ntdll-Placeholders/0008-ntdll-Handle-NULL-process-handle-in-MapViewOfFile3.patch @@ -0,0 +1,58 @@ +From 72dcf52735e6306fa67f25f49fd78da24d7d89cb Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 18:40:18 -0600 +Subject: [PATCH] ntdll: Handle NULL process handle in MapViewOfFile3(). + +Based on a patch by Nikolay Sivov. +--- + dlls/kernelbase/memory.c | 2 ++ + dlls/ntdll/tests/virtual.c | 13 +++++++++++++ + 2 files changed, 15 insertions(+) + +diff --git a/dlls/kernelbase/memory.c b/dlls/kernelbase/memory.c +index c01fe817972..ee12f6abb91 100644 +--- a/dlls/kernelbase/memory.c ++++ b/dlls/kernelbase/memory.c +@@ -255,6 +255,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/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c +index 0dccb35bd27..486efabaf70 100644 +--- a/dlls/ntdll/tests/virtual.c ++++ b/dlls/ntdll/tests/virtual.c +@@ -1060,6 +1060,13 @@ 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); ++ ok(!((ULONG_PTR)ptr & 0xffff), "returned memory %p is not aligned to 64k\n", ptr); ++ + ptr = NULL; + size = 0; + offset.QuadPart = 0; +@@ -1303,6 +1310,12 @@ static void test_NtMapViewOfSectionEx(void) + process = create_target_process("sleep"); + ok(process != NULL, "Can't start process\n"); + ++ ptr = NULL; ++ size = 0; ++ 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 = 0; + offset.QuadPart = 0; +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0009-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtFreeVirt.patch b/patches/ntdll-Placeholders/0009-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtFreeVirt.patch new file mode 100644 index 00000000..b5c1a09b --- /dev/null +++ b/patches/ntdll-Placeholders/0009-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtFreeVirt.patch @@ -0,0 +1,119 @@ +From 5275b219783f0a85e3fad17ac0f999c98d4b7b1c Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 18:43:05 -0600 +Subject: [PATCH] ntdll: Support MEM_PRESERVE_PLACEHOLDER in + NtFreeVirtualMemory(). + +Based on a patch by Nikolay Sivov. +--- + dlls/ntdll/unix/virtual.c | 46 +++++++++++++++++++++++++++++++++------ + 1 file changed, 39 insertions(+), 7 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 7142d2adf79..85c4ab878b3 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -123,6 +123,7 @@ struct file_view + #define VPROT_WRITTEN 0x80 + /* per-mapping protection flags */ + #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */ ++#define VPROT_PLACEHOLDER 0x0400 + + /* Conversion from VPROT_* to Win32 flags */ + static const BYTE VIRTUAL_Win32Flags[16] = +@@ -1115,6 +1116,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_PLACEHOLDER) ++ TRACE( " (placeholder)\n" ); + else if (view->protect & SEC_IMAGE) + TRACE( " (image)\n" ); + else if (view->protect & SEC_FILE) +@@ -4180,7 +4183,7 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + } + else if (!(view = find_view( base, 0 ))) status = STATUS_MEMORY_NOT_ALLOCATED; + else if (!is_view_valloc( view )) status = STATUS_INVALID_PARAMETER; +- else if (type == MEM_RELEASE) ++ else if (type == MEM_RELEASE || (type == (MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER))) + { + /* Free the pages */ + +@@ -4190,14 +4193,15 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + { + if (!size) size = view->size; + +- if (size == view->size) ++ if (type == MEM_RELEASE && size == view->size) + { + assert( base == view->base ); + delete_view( view ); + } + else + { +- struct file_view *new_view = NULL; ++ struct file_view *new_view = NULL, *preserve_view = NULL; ++ int preserve_whole; + + if (view->base != base && base + size != (char *)view->base + view->size + && !(new_view = alloc_view())) +@@ -4205,7 +4209,8 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + ERR( "out of memory for %p-%p\n", base, (char *)base + size ); + return STATUS_NO_MEMORY; + } +- unregister_view( view ); ++ preserve_whole = (size == view->size); ++ if (!preserve_whole) unregister_view( view ); + + if (new_view) + { +@@ -4220,7 +4225,7 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + VIRTUAL_DEBUG_DUMP_VIEW( view ); + VIRTUAL_DEBUG_DUMP_VIEW( new_view ); + } +- else ++ else if (!preserve_whole) + { + if (view->base == base) + { +@@ -4235,8 +4240,35 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + VIRTUAL_DEBUG_DUMP_VIEW( view ); + } + +- set_page_vprot( base, size, 0 ); +- unmap_area( base, size ); ++ if (type & MEM_PRESERVE_PLACEHOLDER) ++ { ++ if (preserve_whole) ++ { ++ view->protect = VPROT_PLACEHOLDER; ++ preserve_view = view; ++ } ++ else ++ { ++ if (!(preserve_view = alloc_view())) ++ { ++ ERR( "out of memory for %p-%p\n", base, (char *)base + size ); ++ return STATUS_NO_MEMORY; ++ } ++ preserve_view->base = base; ++ preserve_view->size = size; ++ preserve_view->protect = VPROT_PLACEHOLDER; ++ register_view( preserve_view ); ++ } ++ set_page_vprot( base, size, 0 ); ++ if (anon_mmap_fixed(base, size, 0, 0) != base) ++ ERR("anon_mmap_fixed failed, err %s.\n", strerror(errno)); ++ VIRTUAL_DEBUG_DUMP_VIEW( preserve_view ); ++ } ++ else ++ { ++ set_page_vprot( base, size, 0 ); ++ unmap_area( base, size ); ++ } + } + *addr_ptr = base; + *size_ptr = size; +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0010-ntdll-Pass-allocation-type-to-map_view.patch b/patches/ntdll-Placeholders/0010-ntdll-Pass-allocation-type-to-map_view.patch new file mode 100644 index 00000000..2fb22593 --- /dev/null +++ b/patches/ntdll-Placeholders/0010-ntdll-Pass-allocation-type-to-map_view.patch @@ -0,0 +1,46 @@ +From 0a8dca419f3f5eacab2e9e11903cfc4c0a58dc93 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 18:48:14 -0600 +Subject: [PATCH] ntdll: Pass allocation type to map_view(). + +Based on a patch by Nikolay Sivov. +--- + 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 85c4ab878b3..eb1d186e11c 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2007,8 +2007,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; + +@@ -2223,7 +2224,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 */ +@@ -3236,7 +3237,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); + +- if ((status = map_view( &view, NULL, size + extra_size, FALSE, ++ if ((status = map_view( &view, NULL, size + extra_size, 0, + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, get_zero_bits_mask( zero_bits ), 0 )) + != STATUS_SUCCESS) + goto done; +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0011-ntdll-Support-MEM_RESERVE_PLACEHOLDER-in-NtAllocateV.patch b/patches/ntdll-Placeholders/0011-ntdll-Support-MEM_RESERVE_PLACEHOLDER-in-NtAllocateV.patch new file mode 100644 index 00000000..6cd17a73 --- /dev/null +++ b/patches/ntdll-Placeholders/0011-ntdll-Support-MEM_RESERVE_PLACEHOLDER-in-NtAllocateV.patch @@ -0,0 +1,97 @@ +From 3bf6197ee235a2686b026cd3f544919d06d31743 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 18:53:10 -0600 +Subject: [PATCH] ntdll: Support MEM_RESERVE_PLACEHOLDER in + NtAllocateVirtualMemoryEx(). + +Based on a patch by Nikolay Sivov. +--- + dlls/ntdll/unix/virtual.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index eb1d186e11c..27de38f8dfd 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -3922,13 +3922,17 @@ 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", type); + return STATUS_INVALID_PARAMETER; + } + ++ if (type & MEM_RESERVE_PLACEHOLDER && (protect != PAGE_NOACCESS)) ++ { ++ WARN("Wrong protect %#x for placeholder.\n", protect); ++ return STATUS_INVALID_PARAMETER; ++ } + /* Reserve the memory */ + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); +@@ -3939,6 +3943,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; + if (protect & PAGE_NOCACHE) vprot |= SEC_NOCACHE; + + if (vprot & VPROT_WRITECOPY) status = STATUS_INVALID_PAGE_PROTECTION; +@@ -3958,6 +3963,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_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 ) +@@ -3991,6 +3997,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, type, protect ); +@@ -4002,6 +4009,12 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z + if (!is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; + #endif + ++ if (type & ~type_mask) ++ { ++ WARN("Called with wrong alloc type flags %08x.\n", type); ++ return STATUS_INVALID_PARAMETER; ++ } ++ + if (process != NtCurrentProcess()) + { + apc_call_t call; +@@ -4044,6 +4057,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; + +@@ -4051,6 +4066,12 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s + + if (count && !parameters) return STATUS_INVALID_PARAMETER; + ++ if (type & ~type_mask) ++ { ++ WARN("Called with wrong alloc type flags %08x.\n", type); ++ return STATUS_INVALID_PARAMETER; ++ } ++ + if (count) + { + MEM_ADDRESS_REQUIREMENTS *r = NULL; +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0012-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-NtAllocateV.patch b/patches/ntdll-Placeholders/0012-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-NtAllocateV.patch new file mode 100644 index 00000000..ea129dac --- /dev/null +++ b/patches/ntdll-Placeholders/0012-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-NtAllocateV.patch @@ -0,0 +1,107 @@ +From ef6b2b0ec45bbbb34dc8ced866c0e4c442157aa5 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 18:58:26 -0600 +Subject: [PATCH] ntdll: Support MEM_REPLACE_PLACEHOLDER in + NtAllocateVirtualMemoryEx(). + +--- + dlls/ntdll/unix/virtual.c | 41 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 37 insertions(+), 4 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 27de38f8dfd..3019c3699be 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -124,6 +124,7 @@ struct file_view + /* per-mapping protection flags */ + #define VPROT_SYSTEM 0x0200 /* system view (underlying mmap not under our control) */ + #define VPROT_PLACEHOLDER 0x0400 ++#define VPROT_FROMPLACEHOLDER 0x0800 + + /* Conversion from VPROT_* to Win32 flags */ + static const BYTE VIRTUAL_Win32Flags[16] = +@@ -2013,6 +2014,31 @@ 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( "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) ++ return STATUS_CONFLICTING_ADDRESSES; ++ if (!((*view_ret)->protect & VPROT_PLACEHOLDER)) ++ { ++ TRACE("Wrong protect %#x for MEM_REPLACE_PLACEHOLDER.\n", (*view_ret)->protect); ++ return STATUS_INVALID_PARAMETER; ++ } ++ (*view_ret)->protect = vprot | VPROT_FROMPLACEHOLDER; ++ ++ if (!set_vprot( *view_ret, base, size, vprot | VPROT_COMMITTED )) ++ ERR("set_protection failed.\n"); ++ if (vprot & VPROT_WRITEWATCH) ++ reset_write_watches( base, size ); ++ return STATUS_SUCCESS; ++ } ++ TRACE("MEM_REPLACE_PLACEHOLDER view not found.\n"); ++ return STATUS_INVALID_PARAMETER; ++ } ++ + if (base) + { + if (is_beyond_limit( base, size, address_space_limit )) +@@ -3898,7 +3924,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 ); +@@ -3922,7 +3948,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", type); + return STATUS_INVALID_PARAMETER; +@@ -3948,7 +3975,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 & (MEM_TOP_DOWN | MEM_REPLACE_PLACEHOLDER), vprot, limit, + align ? align - 1 : granularity_mask ); + + if (status == STATUS_SUCCESS) base = view->base; +@@ -4058,7 +4085,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; + +@@ -4211,6 +4238,12 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + + if (size && (char *)view->base + view->size - base < size) status = STATUS_UNABLE_TO_FREE_VM; + else if (!size && base != view->base) status = STATUS_FREE_VM_NOT_AT_BASE; ++ else if (type == (MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER) && !size) status = STATUS_INVALID_PARAMETER_3; ++ else if (type == (MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER) && !((view->protect & VPROT_FROMPLACEHOLDER) ++ || (view->protect & VPROT_PLACEHOLDER && size != view->size))) ++ { ++ status = STATUS_CONFLICTING_ADDRESSES; ++ } + else + { + if (!size) size = view->size; +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0013-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-virtual_map.patch b/patches/ntdll-Placeholders/0013-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-virtual_map.patch new file mode 100644 index 00000000..e281d61a --- /dev/null +++ b/patches/ntdll-Placeholders/0013-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-virtual_map.patch @@ -0,0 +1,28 @@ +From e37d37263677edfded8696ff09bc76768582e24b Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 19:01:50 -0600 +Subject: [PATCH] ntdll: Support MEM_REPLACE_PLACEHOLDER in + virtual_map_section(). + +Based on a patch by Nikolay Sivov. +--- + dlls/ntdll/unix/virtual.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 3019c3699be..d07e980f5a7 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -2721,7 +2721,8 @@ static NTSTATUS virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_PTR z + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); + +- res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ), 0 ); ++ res = map_view( &view, base, size, alloc_type & (MEM_TOP_DOWN | MEM_REPLACE_PLACEHOLDER), ++ vprot, get_zero_bits_mask( zero_bits ), 0 ); + if (res) goto done; + + TRACE( "handle=%p size=%lx offset=%x%08x\n", handle, size, offset.u.HighPart, offset.u.LowPart ); +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0014-ntdll-tests-Add-more-tests-for-placeholders.patch b/patches/ntdll-Placeholders/0014-ntdll-tests-Add-more-tests-for-placeholders.patch new file mode 100644 index 00000000..63629e55 --- /dev/null +++ b/patches/ntdll-Placeholders/0014-ntdll-tests-Add-more-tests-for-placeholders.patch @@ -0,0 +1,324 @@ +From e47dfda03cf4f2e4c1676273da4ee8f54fabbf01 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Wed, 9 Nov 2022 21:23:19 -0600 +Subject: [PATCH] ntdll/tests: Add more tests for placeholders. + +--- + dlls/kernelbase/tests/process.c | 15 ++- + dlls/ntdll/tests/virtual.c | 227 +++++++++++++++++++++++--------- + 2 files changed, 179 insertions(+), 63 deletions(-) + +diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c +index ed213f1f7b6..0d75c6e2dda 100644 +--- a/dlls/kernelbase/tests/process.c ++++ b/dlls/kernelbase/tests/process.c +@@ -168,7 +168,6 @@ 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; + +@@ -206,6 +205,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 486efabaf70..b41f42ac9d1 100644 +--- a/dlls/ntdll/tests/virtual.c ++++ b/dlls/ntdll/tests/virtual.c +@@ -291,9 +291,13 @@ static void check_region_size_(void *p, SIZE_T s, unsigned int line) + + static void test_NtAllocateVirtualMemoryEx(void) + { ++ MEMORY_BASIC_INFORMATION mbi; ++ void *addresses[16]; + SIZE_T size, size2; + char *p, *p1, *p2; ++ ULONG granularity; + NTSTATUS status; ++ ULONG_PTR count; + void *addr1; + + if (!pNtAllocateVirtualMemoryEx) +@@ -329,98 +333,197 @@ 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); +- 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 = 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, "Unexpected status %08lx.\n", 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); ++ trace("addr1 %p, addresses[0] %p.\n", addr1, addresses[0]); ++ *((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); ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size, MEM_RELEASE); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ + + /* Split in three regions. */ + 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) +- { +- 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); +- } ++ 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); ++ ++ 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); + + /* 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"); +- +- 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); +- } ++ size2 = 0; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); ++ ok(status == STATUS_INVALID_PARAMETER_3, "Unexpected status %08lx.\n", status); ++ ++ 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"); ++ ++ 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); + + /* 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; +- +- 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"); +- 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); +- } ++ 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"); ++ 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); + } + + static void test_NtAllocateVirtualMemoryEx_address_requirements(void) +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0015-ntdll-Support-MEM_COALESCE_PLACEHOLDERS-in-NtFreeVir.patch b/patches/ntdll-Placeholders/0015-ntdll-Support-MEM_COALESCE_PLACEHOLDERS-in-NtFreeVir.patch new file mode 100644 index 00000000..15f9a7b9 --- /dev/null +++ b/patches/ntdll-Placeholders/0015-ntdll-Support-MEM_COALESCE_PLACEHOLDERS-in-NtFreeVir.patch @@ -0,0 +1,117 @@ +From af2c08ba11cf85941505fc8016d4d4811575be31 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Thu, 10 Nov 2022 19:02:50 -0600 +Subject: [PATCH] ntdll: Support MEM_COALESCE_PLACEHOLDERS in + NtFreeVirtualMemory(). + +--- + dlls/ntdll/tests/virtual.c | 53 +++++++++++++++++++++++++++++++++++++- + dlls/ntdll/unix/virtual.c | 24 +++++++++++++++++ + 2 files changed, 76 insertions(+), 1 deletion(-) + +diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c +index b41f42ac9d1..11ccca2ffb4 100644 +--- a/dlls/ntdll/tests/virtual.c ++++ b/dlls/ntdll/tests/virtual.c +@@ -499,11 +499,62 @@ static void test_NtAllocateVirtualMemoryEx(void) + + check_region_size(p1, size / 4); + check_region_size(p2, size - size / 4); +- status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ++ ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); ++ ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_INVALID_PARAMETER_4, "Unexpected status %08lx.\n", status); ++ ++ size2 = size + 0x1000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); ++ ++ size2 = size - 0x1000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); ++ ++ p1 = (char *)addr1 + 0x1000; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); ++ p1 = addr1; ++ ++ size2 = 0; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_INVALID_PARAMETER_3, "Unexpected status %08lx.\n", status); ++ ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE); ++ ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); ++ ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ check_region_size(p1, size); ++ ++ 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(p1, size / 4); ++ check_region_size(p2, size - size / 4); ++ ++ size2 = size - size / 4; ++ status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), (void **)&p2, &size2, MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, ++ PAGE_READWRITE, NULL, 0); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); ++ ++ size2 = size - size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); ++ ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); ++ ++ size2 = size / 4; ++ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ++ ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ++ + /* Split in two regions, specifying second half. */ + addr1 = NULL; + size = 0x10000; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index d07e980f5a7..ced198c0463 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -4341,6 +4341,30 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si + *size_ptr = size; + } + } ++ else if (type & MEM_COALESCE_PLACEHOLDERS) ++ { ++ struct file_view *next_view = RB_ENTRY_VALUE( rb_next( &view->entry ), struct file_view, entry ); ++ ++ if (type != (MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS)) status = STATUS_INVALID_PARAMETER_4; ++ else if (!size) status = STATUS_INVALID_PARAMETER_3; ++ else if (!next_view || (char *)view->base + view->size != next_view->base ++ || base != view->base || size != view->size + next_view->size ++ || !(view->protect & VPROT_PLACEHOLDER) || !(next_view->protect & VPROT_PLACEHOLDER)) ++ { ++ status = STATUS_CONFLICTING_ADDRESSES; ++ } ++ else ++ { ++ unregister_view( view ); ++ unregister_view( next_view ); ++ ++ view->size += next_view->size; ++ free_view( next_view ); ++ ++ register_view( view ); ++ VIRTUAL_DEBUG_DUMP_VIEW( view ); ++ } ++ } + else + { + WARN("called with wrong free type flags (%08x) !\n", type); +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0016-ntdll-Factor-out-unmap_view_of_section-function.patch b/patches/ntdll-Placeholders/0016-ntdll-Factor-out-unmap_view_of_section-function.patch new file mode 100644 index 00000000..5d1debb2 --- /dev/null +++ b/patches/ntdll-Placeholders/0016-ntdll-Factor-out-unmap_view_of_section-function.patch @@ -0,0 +1,54 @@ +From d8506b129723188bee9daf779a19f3334459205d Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 12:41:31 -0600 +Subject: [PATCH] ntdll: Factor out unmap_view_of_section() function. + +--- + dlls/ntdll/unix/virtual.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index ced198c0463..219dfe8bb94 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -5012,11 +5012,7 @@ NTSTATUS WINAPI NtMapViewOfSectionEx( HANDLE handle, HANDLE process, PVOID *addr + return NtMapViewOfSection( handle, process, addr_ptr, 0, 0, offset_ptr, size_ptr, ViewShare, alloc_type, protect ); + } + +-/*********************************************************************** +- * NtUnmapViewOfSection (NTDLL.@) +- * ZwUnmapViewOfSection (NTDLL.@) +- */ +-NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) ++NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr ) + { + struct file_view *view; + NTSTATUS status = STATUS_NOT_MAPPED_VIEW; +@@ -5073,6 +5069,15 @@ NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) + return status; + } + ++/*********************************************************************** ++ * NtUnmapViewOfSection (NTDLL.@) ++ * ZwUnmapViewOfSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) ++{ ++ return unmap_view_of_section( process, addr ); ++} ++ + /*********************************************************************** + * NtUnmapViewOfSectionEx (NTDLL.@) + * ZwUnmapViewOfSectionEx (NTDLL.@) +@@ -5080,7 +5085,7 @@ NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) + NTSTATUS WINAPI NtUnmapViewOfSectionEx( HANDLE process, PVOID addr, ULONG flags ) + { + if (flags) FIXME("Ignoring flags %#x.\n", flags); +- return NtUnmapViewOfSection( process, addr ); ++ return unmap_view_of_section( process, addr ); + } + + /****************************************************************************** +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/0017-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtUnmapVie.patch b/patches/ntdll-Placeholders/0017-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtUnmapVie.patch new file mode 100644 index 00000000..39c730c0 --- /dev/null +++ b/patches/ntdll-Placeholders/0017-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtUnmapVie.patch @@ -0,0 +1,217 @@ +From 34f2b170598cff223c936c8150ff551955e2b888 Mon Sep 17 00:00:00 2001 +From: Paul Gofman +Date: Fri, 11 Nov 2022 12:54:19 -0600 +Subject: [PATCH] ntdll: Support MEM_PRESERVE_PLACEHOLDER in + NtUnmapViewOfSectionEx(). + +--- + dlls/kernelbase/tests/process.c | 53 +++++++++++++++++++++++++++++++-- + dlls/ntdll/unix/server.c | 2 +- + dlls/ntdll/unix/virtual.c | 25 +++++++++++++--- + server/protocol.def | 1 + + 4 files changed, 73 insertions(+), 8 deletions(-) + +diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c +index 0d75c6e2dda..5221a102863 100644 +--- a/dlls/kernelbase/tests/process.c ++++ b/dlls/kernelbase/tests/process.c +@@ -41,6 +41,7 @@ static PVOID (WINAPI *pVirtualAllocFromApp)(PVOID, SIZE_T, DWORD, DWORD); + static HANDLE (WINAPI *pOpenFileMappingFromApp)( ULONG, BOOL, LPCWSTR); + static HANDLE (WINAPI *pCreateFileMappingFromApp)(HANDLE, PSECURITY_ATTRIBUTES, ULONG, ULONG64, PCWSTR); + static LPVOID (WINAPI *pMapViewOfFileFromApp)(HANDLE, ULONG, ULONG64, SIZE_T); ++static BOOL (WINAPI *pUnmapViewOfFile2)(HANDLE, void *, ULONG); + + static void test_CompareObjectHandles(void) + { +@@ -166,6 +167,13 @@ static void test_VirtualAlloc2(void) + ret = VirtualFree(addr, 0, MEM_RELEASE); + ok(ret, "Unexpected return value %d, error %lu.\n", ret, GetLastError()); + ++ placeholder1 = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE, PAGE_NOACCESS, NULL, 0); ++ ok(!!placeholder1, "Failed to create a placeholder range.\n"); ++ ret = VirtualFree(placeholder1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); ++ ok(!ret && GetLastError() == ERROR_INVALID_ADDRESS, "Got ret %d, error %lu.\n", ret, GetLastError()); ++ ret = VirtualFree(placeholder1, 0, MEM_RELEASE); ++ ok(ret, "Unexpected return value %d, error %lu.\n", ret, GetLastError()); ++ + /* Placeholder splitting functionality */ + placeholder1 = pVirtualAlloc2(NULL, NULL, 2 * size, MEM_RESERVE_PLACEHOLDER | MEM_RESERVE, PAGE_NOACCESS, NULL, 0); + ok(!!placeholder1, "Failed to create a placeholder range.\n"); +@@ -199,11 +207,20 @@ static void test_VirtualAlloc2(void) + section = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL); + ok(!!section, "Failed to create a section.\n"); + +- view1 = pMapViewOfFile3(section, NULL, placeholder1, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); ++ view1 = pMapViewOfFile3(section, NULL, NULL, 0, size, 0, PAGE_READWRITE, NULL, 0); + ok(!!view1, "Failed to map a section.\n"); ++ ret = VirtualFree( view1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER ); ++ ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got ret %d, error %lu.\n", ret, GetLastError()); ++ ret = pUnmapViewOfFile2(GetCurrentProcess(), view1, MEM_PRESERVE_PLACEHOLDER); ++ ok(!ret && GetLastError() == ERROR_INVALID_ADDRESS, "Got ret %d, error %lu.\n", ret, GetLastError()); ++ ret = pUnmapViewOfFile2(GetCurrentProcess(), view1, 0); ++ ok(ret, "Got error %lu.\n", GetLastError()); ++ ++ view1 = pMapViewOfFile3(section, NULL, placeholder1, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); ++ ok(view1 == placeholder1, "Address does not match.\n"); + + view2 = pMapViewOfFile3(section, NULL, placeholder2, 0, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); +- ok(!!view2, "Failed to map a section.\n"); ++ ok(view2 == placeholder2, "Address does not match.\n"); + + memset(&info, 0, sizeof(info)); + VirtualQuery(placeholder1, &info, sizeof(info)); +@@ -220,7 +237,34 @@ static void test_VirtualAlloc2(void) + ok(info.RegionSize == size, "Unexpected size.\n"); + + CloseHandle(section); +- UnmapViewOfFile(view1); ++ ret = pUnmapViewOfFile2(NULL, view1, MEM_PRESERVE_PLACEHOLDER); ++ ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "Got error %lu.\n", GetLastError()); ++ ++ ret = VirtualFree( placeholder1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER ); ++ ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got ret %d, error %lu.\n", ret, GetLastError()); ++ ++ ret = pUnmapViewOfFile2(GetCurrentProcess(), view1, MEM_PRESERVE_PLACEHOLDER); ++ ok(ret, "Got error %lu.\n", GetLastError()); ++ 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"); ++ ++ ret = pUnmapViewOfFile2(GetCurrentProcess(), view1, MEM_PRESERVE_PLACEHOLDER); ++ ok(!ret && GetLastError() == ERROR_INVALID_ADDRESS, "Got error %lu.\n", GetLastError()); ++ ++ ret = UnmapViewOfFile(view1); ++ ok(!ret && GetLastError() == ERROR_INVALID_ADDRESS, "Got error %lu.\n", GetLastError()); ++ ++ ret = VirtualFree( placeholder1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER ); ++ ok(!ret && GetLastError() == ERROR_INVALID_ADDRESS, "Got ret %d, error %lu.\n", ret, GetLastError()); ++ ret = VirtualFreeEx(GetCurrentProcess(), placeholder1, size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER ); ++ ok(!ret && GetLastError() == ERROR_INVALID_ADDRESS, "Got ret %d, error %lu.\n", ret, GetLastError()); ++ ret = VirtualFree(placeholder1, 0, MEM_RELEASE); ++ ok(ret, "Got error %lu.\n", GetLastError()); ++ + UnmapViewOfFile(view2); + + VirtualFree(placeholder1, 0, MEM_RELEASE); +@@ -250,6 +294,8 @@ static void test_VirtualAlloc2(void) + + p1 = p; + p2 = p + size / 2; ++ ret = VirtualFree(p1, 0, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); ++ ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "Got ret %d, error %lu.\n", ret, GetLastError()); + ret = VirtualFree(p1, size / 2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(ret, "Failed to split a placeholder.\n"); + check_region_size(p1, size / 2); +@@ -447,6 +493,7 @@ static void init_funcs(void) + X(VirtualAlloc2); + X(VirtualAlloc2FromApp); + X(VirtualAllocFromApp); ++ X(UnmapViewOfFile2); + + hmod = GetModuleHandleA("ntdll.dll"); + +diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c +index 630abe74c80..cba874ee6a8 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -572,7 +572,7 @@ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOO + result->type = call->type; + addr = wine_server_get_ptr( call->unmap_view.addr ); + if ((ULONG_PTR)addr == call->unmap_view.addr) +- result->unmap_view.status = NtUnmapViewOfSection( NtCurrentProcess(), addr ); ++ result->unmap_view.status = NtUnmapViewOfSectionEx( NtCurrentProcess(), addr, call->unmap_view.flags ); + else + result->unmap_view.status = STATUS_INVALID_PARAMETER; + break; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 219dfe8bb94..84a4e331ad7 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -5012,7 +5012,7 @@ NTSTATUS WINAPI NtMapViewOfSectionEx( HANDLE handle, HANDLE process, PVOID *addr + return NtMapViewOfSection( handle, process, addr_ptr, 0, 0, offset_ptr, size_ptr, ViewShare, alloc_type, protect ); + } + +-NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr ) ++static NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr, ULONG flags ) + { + struct file_view *view; + NTSTATUS status = STATUS_NOT_MAPPED_VIEW; +@@ -5027,6 +5027,7 @@ NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr ) + + call.unmap_view.type = APC_UNMAP_VIEW; + call.unmap_view.addr = wine_server_client_ptr( addr ); ++ call.unmap_view.flags = flags; + status = server_queue_process_apc( process, &call, &result ); + if (status == STATUS_SUCCESS) status = result.unmap_view.status; + return status; +@@ -5035,6 +5036,11 @@ NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr ) + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); + if ((view = find_view( addr, 0 )) && !is_view_valloc( view )) + { ++ if (flags & MEM_PRESERVE_PLACEHOLDER && !(view->protect & VPROT_FROMPLACEHOLDER)) ++ { ++ status = STATUS_CONFLICTING_ADDRESSES; ++ goto done; ++ } + if (view->protect & VPROT_SYSTEM) + { + struct builtin_module *builtin; +@@ -5061,10 +5067,21 @@ NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr ) + if (!status) + { + if (view->protect & SEC_IMAGE) release_builtin_module( view->base ); +- delete_view( view ); ++ if (flags & MEM_PRESERVE_PLACEHOLDER) ++ { ++ view->protect = VPROT_PLACEHOLDER; ++ set_page_vprot( view->base, view->size, 0 ); ++ if (anon_mmap_fixed(view->base, view->size, 0, 0) != view->base) ++ ERR("anon_mmap_fixed failed, err %s.\n", strerror(errno)); ++ } ++ else ++ { ++ delete_view( view ); ++ } + } + else FIXME( "failed to unmap %p %x\n", view->base, status ); + } ++done: + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + return status; + } +@@ -5075,7 +5092,7 @@ NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr ) + */ + NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) + { +- return unmap_view_of_section( process, addr ); ++ return unmap_view_of_section( process, addr, 0 ); + } + + /*********************************************************************** +@@ -5085,7 +5102,7 @@ NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) + NTSTATUS WINAPI NtUnmapViewOfSectionEx( HANDLE process, PVOID addr, ULONG flags ) + { + if (flags) FIXME("Ignoring flags %#x.\n", flags); +- return unmap_view_of_section( process, addr ); ++ return unmap_view_of_section( process, addr, flags ); + } + + /****************************************************************************** +diff --git a/server/protocol.def b/server/protocol.def +index 9a832cd3a06..35408413249 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -602,6 +602,7 @@ typedef union + enum apc_type type; /* APC_UNMAP_VIEW */ + int __pad; + client_ptr_t addr; /* view address */ ++ unsigned int flags; /* unmap flags */ + } unmap_view; + struct + { +-- +2.38.1 + diff --git a/patches/ntdll-Placeholders/definition b/patches/ntdll-Placeholders/definition new file mode 100644 index 00000000..e69de29b diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 78c92abb..e028f409 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -139,6 +139,7 @@ patch_enable_all () enable_ntdll_Manifest_Range="$1" enable_ntdll_NtQuerySection="$1" enable_ntdll_NtSetLdtEntries="$1" + enable_ntdll_Placeholders="$1" enable_ntdll_ProcessQuotaLimits="$1" enable_ntdll_RtlQueryPackageIdentity="$1" enable_ntdll_Serial_Port_Detection="$1" @@ -430,6 +431,9 @@ patch_enable () ntdll-NtSetLdtEntries) enable_ntdll_NtSetLdtEntries="$2" ;; + ntdll-Placeholders) + enable_ntdll_Placeholders="$2" + ;; ntdll-ProcessQuotaLimits) enable_ntdll_ProcessQuotaLimits="$2" ;; @@ -2191,6 +2195,32 @@ if test "$enable_ntdll_NtSetLdtEntries" -eq 1; then patch_apply ntdll-NtSetLdtEntries/0002-libs-wine-Allow-to-modify-reserved-LDT-entries.patch fi +# Patchset ntdll-Placeholders +# | +# | Modified files: +# | * dlls/kernelbase/memory.c, dlls/kernelbase/tests/process.c, dlls/ntdll/tests/virtual.c, dlls/ntdll/unix/server.c, +# | dlls/ntdll/unix/virtual.c, server/protocol.def +# | +if test "$enable_ntdll_Placeholders" -eq 1; then + patch_apply ntdll-Placeholders/0001-ntdll-tests-Add-tests-for-freeing-a-part-of-view.patch + patch_apply ntdll-Placeholders/0002-kernelbase-Validate-nonzero-size-for-MEM_RELEASE-in-.patch + patch_apply ntdll-Placeholders/0003-ntdll-Fix-size-validation-in-NtFreeVirtualMemory.patch + patch_apply ntdll-Placeholders/0004-ntdll-Fully-support-unaligned-views-in-free-ranges-m.patch + patch_apply ntdll-Placeholders/0005-ntdll-Factor-out-some-view-manipulation-functions.patch + patch_apply ntdll-Placeholders/0006-ntdll-Support-partial-view-release-in-NtFreeVirtualM.patch + patch_apply ntdll-Placeholders/0007-ntdll-Add-logging-for-free-ranges.patch + patch_apply ntdll-Placeholders/0008-ntdll-Handle-NULL-process-handle-in-MapViewOfFile3.patch + patch_apply ntdll-Placeholders/0009-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtFreeVirt.patch + patch_apply ntdll-Placeholders/0010-ntdll-Pass-allocation-type-to-map_view.patch + patch_apply ntdll-Placeholders/0011-ntdll-Support-MEM_RESERVE_PLACEHOLDER-in-NtAllocateV.patch + patch_apply ntdll-Placeholders/0012-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-NtAllocateV.patch + patch_apply ntdll-Placeholders/0013-ntdll-Support-MEM_REPLACE_PLACEHOLDER-in-virtual_map.patch + patch_apply ntdll-Placeholders/0014-ntdll-tests-Add-more-tests-for-placeholders.patch + patch_apply ntdll-Placeholders/0015-ntdll-Support-MEM_COALESCE_PLACEHOLDERS-in-NtFreeVir.patch + patch_apply ntdll-Placeholders/0016-ntdll-Factor-out-unmap_view_of_section-function.patch + patch_apply ntdll-Placeholders/0017-ntdll-Support-MEM_PRESERVE_PLACEHOLDER-in-NtUnmapVie.patch +fi + # Patchset ntdll-ProcessQuotaLimits # | # | This patchset fixes the following Wine bugs: