ntdll: Support MEM_COALESCE_PLACEHOLDERS in NtFreeVirtualMemory().

This commit is contained in:
Paul Gofman 2022-11-10 19:02:50 -06:00 committed by Alexandre Julliard
parent d5f23441ad
commit 03096546c9
2 changed files with 125 additions and 3 deletions

View file

@ -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;

View file

@ -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;