Module: wine Branch: master Commit: 03096546c9b11983170469bb2eda651cef5bba7a URL: https://gitlab.winehq.org/wine/wine/-/commit/03096546c9b11983170469bb2eda651...
Author: Paul Gofman pgofman@codeweavers.com Date: Thu Nov 10 19:02:50 2022 -0600
ntdll: Support MEM_COALESCE_PLACEHOLDERS in NtFreeVirtualMemory().
---
dlls/ntdll/tests/virtual.c | 72 ++++++++++++++++++++++++++++++++++++++++++++-- dlls/ntdll/unix/virtual.c | 56 +++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 45ba58868df..f5f1964e516 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -295,9 +295,9 @@ static void test_NtAllocateVirtualMemoryEx(void) { MEMORY_BASIC_INFORMATION mbi; MEM_EXTENDED_PARAMETER ext[2]; + char *p, *p1, *p2, *p3; void *addresses[16]; SIZE_T size, size2; - char *p, *p1, *p2; ULONG granularity; NTSTATUS status; ULONG_PTR count; @@ -510,6 +510,7 @@ static void test_NtAllocateVirtualMemoryEx(void)
p1 = addr1; p2 = p1 + size / 4; + p3 = p2 + 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); @@ -517,12 +518,79 @@ static void test_NtAllocateVirtualMemoryEx(void) ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1);
+ status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + check_region_size(p1, p2 - p1); + check_region_size(p2, p3 - p2); + check_region_size(p3, size - (p3 - p1)); + + 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); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + 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); + 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); - status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + + 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); + ok(size2 == 0xc000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 4, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 4); + + 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); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + + size2 = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p3, &size2, MEM_RELEASE); + ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status);
/* Split in two regions, specifying second half. */ addr1 = NULL; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 2d0dda64249..6fe025d2a7b 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2294,6 +2294,53 @@ static NTSTATUS free_pages( struct file_view *view, char *base, size_t size ) }
+/*********************************************************************** + * coalesce_placeholders + * + * Coalesce placeholder views. + * virtual_mutex must be held by caller. + */ +static NTSTATUS coalesce_placeholders( struct file_view *view, char *base, size_t size ) +{ + struct rb_entry *next; + struct file_view *curr_view, *next_view; + unsigned int i, view_count = 0; + size_t views_size = 0; + + if (!size) return STATUS_INVALID_PARAMETER_3; + if (base != view->base) return STATUS_CONFLICTING_ADDRESSES; + + curr_view = view; + while (curr_view->protect & VPROT_FREE_PLACEHOLDER) + { + ++view_count; + views_size += curr_view->size; + if (views_size >= size) break; + if (!(next = rb_next( &curr_view->entry ))) break; + next_view = RB_ENTRY_VALUE( next, struct file_view, entry ); + if ((char *)curr_view->base + curr_view->size != next_view->base) break; + curr_view = next_view; + } + + if (view_count < 2 || size != views_size) return STATUS_CONFLICTING_ADDRESSES; + + for (i = 1; i < view_count; ++i) + { + curr_view = RB_ENTRY_VALUE( rb_next( &view->entry ), struct file_view, entry ); + unregister_view( curr_view ); + free_view( curr_view ); + } + + unregister_view( view ); + view->size = views_size; + register_view( view ); + + VIRTUAL_DEBUG_DUMP_VIEW( view ); + + return STATUS_SUCCESS; +} + + /*********************************************************************** * allocate_dos_memory * @@ -4445,7 +4492,8 @@ 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 (!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 ((char *)view->base + view->size - base < size && !(type & MEM_COALESCE_PLACEHOLDERS)) + status = STATUS_UNABLE_TO_FREE_VM; else switch (type) { case MEM_DECOMMIT: @@ -4458,6 +4506,12 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T *si case MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER: status = free_pages_preserve_placeholder( view, base, size ); break; + case MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS: + status = coalesce_placeholders( view, base, size ); + break; + case MEM_COALESCE_PLACEHOLDERS: + status = STATUS_INVALID_PARAMETER_4; + break; default: status = STATUS_INVALID_PARAMETER; break;