Various notes:
- LowestStartingAddress is still unsupported; - Patch 5 ("wow64: Set HighestEndingAddress in wow64_NtAllocateVirtualMemoryEx() if it is absent") is fixing existing WOW issue: NtAllocateVirtualMemoryEx() called from wow64 currently does not constraing the allocation to 32 bit address space (wow64_NtAllocateVirtualMemory() passes zero_bits for that). - I initially thought of using a single inter process APC but added a different one due to zero_bits handling which is easier to convert in the target process.
-- v5: wow64: Set HighestEndingAddress in wow64_NtAllocateVirtualMemoryEx() if it is absent. wow64: Support MEM_ADDRESS_REQUIREMENTS in wow64_NtAllocateVirtualMemoryEx(). ntdll/tests: Add tests for memory address requiements. ntdll: Support HighestEndingAddress in NtAllocateVirtualMemoryEx(). ntdll: Factor out allocate_virtual_memory(). ntdll: Pass limit instead of zero_bits to map_view().
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/virtual.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 422d717b00d..42e73d8ebaa 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1147,7 +1147,7 @@ static inline UINT_PTR get_zero_bits_mask( ULONG_PTR zero_bits ) { unsigned int shift;
- if (zero_bits == 0) return ~(UINT_PTR)0; + if (zero_bits == 0) return 0;
if (zero_bits < 32) shift = 32 + zero_bits; else @@ -1895,7 +1895,7 @@ 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 zero_bits ) + int top_down, unsigned int vprot, ULONG_PTR limit ) { void *ptr; NTSTATUS status; @@ -1915,7 +1915,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
alloc.size = size; alloc.top_down = top_down; - alloc.limit = (void*)(get_zero_bits_mask( zero_bits ) & (UINT_PTR)user_space_limit); + alloc.limit = limit ? (void*)(limit & (UINT_PTR)user_space_limit) : user_space_limit;
if (mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down )) { @@ -1926,7 +1926,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, goto done; }
- if (zero_bits) + if (limit) { if (!(ptr = map_free_area( address_space_start, alloc.limit, size, top_down, get_unix_prot(vprot) ))) @@ -2451,9 +2451,9 @@ static NTSTATUS virtual_map_image( HANDLE mapping, ACCESS_MASK access, void **ad if ((ULONG_PTR)base != image_info->base) base = NULL;
if ((char *)base >= (char *)address_space_start) /* make sure the DOS area remains free */ - status = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits ); + status = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ) );
- if (status) status = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, zero_bits ); + if (status) status = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits )); if (status) goto done;
status = map_image_into_view( view, filename, unix_fd, base, image_info->header_size, @@ -2576,7 +2576,7 @@ 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, zero_bits ); + res = map_view( &view, base, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ) ); if (res) goto done;
TRACE( "handle=%p size=%lx offset=%x%08x\n", handle, size, offset.u.HighPart, offset.u.LowPart ); @@ -3114,7 +3114,8 @@ 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, - VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, zero_bits )) != STATUS_SUCCESS) + VPROT_READ | VPROT_WRITE | VPROT_COMMITTED, get_zero_bits_mask( zero_bits ) )) + != STATUS_SUCCESS) goto done;
#ifdef VALGRIND_STACK_REGISTER @@ -3815,7 +3816,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z
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, zero_bits ); + else status = map_view( &view, base, size, type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ) );
if (status == STATUS_SUCCESS) base = view->base; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/virtual.c | 97 ++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 38 deletions(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 42e73d8ebaa..18e2e2c6d9e 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -3718,11 +3718,12 @@ void virtual_set_large_address_space(void)
/*********************************************************************** - * NtAllocateVirtualMemory (NTDLL.@) - * ZwAllocateVirtualMemory (NTDLL.@) + * allocate_virtual_memory + * + * NtAllocateVirtualMempory[Ex] implementation. */ -NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR zero_bits, - SIZE_T *size_ptr, ULONG type, ULONG protect ) +static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG type, ULONG protect, + ULONG_PTR limit ) { void *base; unsigned int vprot; @@ -3732,39 +3733,6 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z SIZE_T size = *size_ptr; NTSTATUS status = STATUS_SUCCESS;
- TRACE("%p %p %08lx %x %08x\n", process, *ret, size, type, protect ); - - if (!size) return STATUS_INVALID_PARAMETER; - if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; - if (zero_bits > 32 && zero_bits < granularity_mask) return STATUS_INVALID_PARAMETER_3; -#ifndef _WIN64 - if (!is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; -#endif - - if (process != NtCurrentProcess()) - { - apc_call_t call; - apc_result_t result; - - memset( &call, 0, sizeof(call) ); - - call.virtual_alloc.type = APC_VIRTUAL_ALLOC; - call.virtual_alloc.addr = wine_server_client_ptr( *ret ); - call.virtual_alloc.size = *size_ptr; - call.virtual_alloc.zero_bits = zero_bits; - call.virtual_alloc.op_type = type; - call.virtual_alloc.prot = protect; - status = server_queue_process_apc( process, &call, &result ); - if (status != STATUS_SUCCESS) return status; - - if (result.virtual_alloc.status == STATUS_SUCCESS) - { - *ret = wine_server_get_ptr( result.virtual_alloc.addr ); - *size_ptr = result.virtual_alloc.size; - } - return result.virtual_alloc.status; - } - /* Round parameters to a page boundary */
if (is_beyond_limit( 0, size, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE; @@ -3816,7 +3784,7 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z
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, get_zero_bits_mask( zero_bits ) ); + else status = map_view( &view, base, size, type & MEM_TOP_DOWN, vprot, limit );
if (status == STATUS_SUCCESS) base = view->base; } @@ -3855,6 +3823,59 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z return status; }
+ +/*********************************************************************** + * NtAllocateVirtualMemory (NTDLL.@) + * ZwAllocateVirtualMemory (NTDLL.@) + */ +NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR zero_bits, + SIZE_T *size_ptr, ULONG type, ULONG protect ) +{ + ULONG_PTR limit; + + TRACE("%p %p %08lx %x %08x\n", process, *ret, *size_ptr, type, protect ); + + if (!*size_ptr) return STATUS_INVALID_PARAMETER; + if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3; + if (zero_bits > 32 && zero_bits < granularity_mask) return STATUS_INVALID_PARAMETER_3; +#ifndef _WIN64 + if (!is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3; +#endif + + if (process != NtCurrentProcess()) + { + apc_call_t call; + apc_result_t result; + NTSTATUS status; + + memset( &call, 0, sizeof(call) ); + + call.virtual_alloc.type = APC_VIRTUAL_ALLOC; + call.virtual_alloc.addr = wine_server_client_ptr( *ret ); + call.virtual_alloc.size = *size_ptr; + call.virtual_alloc.zero_bits = zero_bits; + call.virtual_alloc.op_type = type; + call.virtual_alloc.prot = protect; + status = server_queue_process_apc( process, &call, &result ); + if (status != STATUS_SUCCESS) return status; + + if (result.virtual_alloc.status == STATUS_SUCCESS) + { + *ret = wine_server_get_ptr( result.virtual_alloc.addr ); + *size_ptr = result.virtual_alloc.size; + } + return result.virtual_alloc.status; + } + + if (!*ret) + limit = get_zero_bits_mask( zero_bits ); + else + limit = 0; + + return allocate_virtual_memory( ret, size_ptr, type, protect, limit ); +} + + /*********************************************************************** * NtAllocateVirtualMemoryEx (NTDLL.@) * ZwAllocateVirtualMemoryEx (NTDLL.@)
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/server.c | 29 ++++++++++++++- dlls/ntdll/unix/virtual.c | 75 +++++++++++++++++++++++++++++++++++++-- include/winnt.h | 7 ++++ server/protocol.def | 17 +++++++++ server/thread.c | 1 + server/trace.c | 6 ++++ 6 files changed, 131 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 77e8d5c7566..b7d8733f2bc 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -358,7 +358,7 @@ static NTSTATUS invoke_user_apc( CONTEXT *context, const user_apc_t *apc, NTSTAT */ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOOL self ) { - SIZE_T size, bits; + SIZE_T size, bits, limit; void *addr;
memset( result, 0, sizeof(*result) ); @@ -401,6 +401,33 @@ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOO } else result->virtual_alloc.status = STATUS_WORKING_SET_LIMIT_RANGE; break; + case APC_VIRTUAL_ALLOC_EX: + { + MEM_ADDRESS_REQUIREMENTS r = { NULL }; + MEM_EXTENDED_PARAMETER ext = + { + .Type = MemExtendedParameterAddressRequirements, + .Pointer = &r + }; + SYSTEM_BASIC_INFORMATION sbi; + + virtual_get_system_info( &sbi, !!NtCurrentTeb()->WowTebOffset ); + result->type = call->type; + addr = wine_server_get_ptr( call->virtual_alloc_ex.addr ); + size = call->virtual_alloc_ex.size; + limit = min( (ULONG_PTR)sbi.HighestUserAddress, call->virtual_alloc_ex.limit ); + if ((ULONG_PTR)addr == call->virtual_alloc_ex.addr && size == call->virtual_alloc_ex.size) + { + r.HighestEndingAddress = (void *)limit; + result->virtual_alloc_ex.status = NtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr, &size, + call->virtual_alloc_ex.op_type, + call->virtual_alloc_ex.prot, &ext, 1 ); + result->virtual_alloc_ex.addr = wine_server_client_ptr( addr ); + result->virtual_alloc_ex.size = size; + } + else result->virtual_alloc_ex.status = STATUS_WORKING_SET_LIMIT_RANGE; + break; + } case APC_VIRTUAL_FREE: result->type = call->type; addr = wine_server_get_ptr( call->virtual_free.addr ); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 18e2e2c6d9e..d1456a4bb09 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1915,7 +1915,7 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size,
alloc.size = size; alloc.top_down = top_down; - alloc.limit = limit ? (void*)(limit & (UINT_PTR)user_space_limit) : user_space_limit; + alloc.limit = limit ? min( (void *)(limit + 1), user_space_limit ) : user_space_limit;
if (mmap_enum_reserved_areas( alloc_reserved_area_callback, &alloc, top_down )) { @@ -3884,11 +3884,80 @@ NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *s ULONG protect, MEM_EXTENDED_PARAMETER *parameters, ULONG count ) { + ULONG_PTR limit = 0; + + TRACE("%p %p %08lx %x %08x %p %u\n", process, *ret, *size_ptr, type, protect, parameters, count ); + if (count && !parameters) return STATUS_INVALID_PARAMETER;
- if (count) FIXME( "Ignoring %d extended parameters %p\n", count, parameters ); + if (count) + { + int mem_requirements_found = 0; + MEM_ADDRESS_REQUIREMENTS *r; + unsigned int i; + + for (i = 0; i < count; ++i) + { + if (parameters[i].Type == MemExtendedParameterInvalidType || parameters[i].Type >= MemExtendedParameterMax) + { + WARN( "Invalid parameter type %d.\n", parameters[i].Type ); + return STATUS_INVALID_PARAMETER; + } + if (parameters[i].Type != MemExtendedParameterAddressRequirements) + { + FIXME( "Parameter type %d is not supported.\n", parameters[i].Type ); + continue; + } + if (mem_requirements_found) + { + WARN( "Duplicate parameter.\n" ); + return STATUS_INVALID_PARAMETER; + } + mem_requirements_found = 1; + r = (MEM_ADDRESS_REQUIREMENTS *)parameters[i].Pointer; + + if (r->LowestStartingAddress || r->Alignment) + FIXME( "Not supported requirements LowestStartingAddress %p, Alignment %p.\n", + r->LowestStartingAddress, (void *)r->Alignment ); + + limit = (ULONG_PTR)r->HighestEndingAddress; + if (limit && (*ret || limit > (ULONG_PTR)user_space_limit || ((limit + 1) & (page_mask - 1)))) + { + WARN( "Invalid limit %p.\n", r->HighestEndingAddress); + return STATUS_INVALID_PARAMETER; + } + TRACE( "limit %p.\n", (void *)limit ); + } + } + + if (!*size_ptr) return STATUS_INVALID_PARAMETER; + + if (process != NtCurrentProcess()) + { + apc_call_t call; + apc_result_t result; + NTSTATUS status; + + memset( &call, 0, sizeof(call) ); + + call.virtual_alloc_ex.type = APC_VIRTUAL_ALLOC_EX; + call.virtual_alloc_ex.addr = wine_server_client_ptr( *ret ); + call.virtual_alloc_ex.size = *size_ptr; + call.virtual_alloc_ex.limit = limit; + call.virtual_alloc_ex.op_type = type; + call.virtual_alloc_ex.prot = protect; + status = server_queue_process_apc( process, &call, &result ); + if (status != STATUS_SUCCESS) return status; + + if (result.virtual_alloc_ex.status == STATUS_SUCCESS) + { + *ret = wine_server_get_ptr( result.virtual_alloc_ex.addr ); + *size_ptr = result.virtual_alloc_ex.size; + } + return result.virtual_alloc_ex.status; + }
- return NtAllocateVirtualMemory( process, ret, 0, size_ptr, type, protect ); + return allocate_virtual_memory( ret, size_ptr, type, protect, limit ); }
diff --git a/include/winnt.h b/include/winnt.h index 836bd7123e5..f5b01ca39ca 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -759,6 +759,13 @@ typedef struct _MEMORY_BASIC_INFORMATION DWORD Type; } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
+typedef struct _MEM_ADDRESS_REQUIREMENTS +{ + void *LowestStartingAddress; + void *HighestEndingAddress; + SIZE_T Alignment; +} MEM_ADDRESS_REQUIREMENTS, *PMEM_ADDRESS_REQUIREMENTS; + #define MEM_EXTENDED_PARAMETER_TYPE_BITS 8
typedef enum MEM_EXTENDED_PARAMETER_TYPE { diff --git a/server/protocol.def b/server/protocol.def index 86fb10951f0..06977c29054 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -493,6 +493,7 @@ enum apc_type APC_USER, APC_ASYNC_IO, APC_VIRTUAL_ALLOC, + APC_VIRTUAL_ALLOC_EX, APC_VIRTUAL_FREE, APC_VIRTUAL_QUERY, APC_VIRTUAL_PROTECT, @@ -535,6 +536,15 @@ typedef union unsigned int prot; /* memory protection flags */ } virtual_alloc; struct + { + enum apc_type type; /* APC_VIRTUAL_ALLOC */ + unsigned int op_type; /* type of operation */ + client_ptr_t addr; /* requested address */ + mem_size_t size; /* allocation size */ + mem_size_t limit; /* allocation address limit */ + unsigned int prot; /* memory protection flags */ + } virtual_alloc_ex; + struct { enum apc_type type; /* APC_VIRTUAL_FREE */ unsigned int op_type; /* type of operation */ @@ -630,6 +640,13 @@ typedef union mem_size_t size; /* resulting size */ } virtual_alloc; struct + { + enum apc_type type; /* APC_VIRTUAL_ALLOC */ + unsigned int status; /* status returned by call */ + client_ptr_t addr; /* resulting address */ + mem_size_t size; /* resulting size */ + } virtual_alloc_ex; + struct { enum apc_type type; /* APC_VIRTUAL_FREE */ unsigned int status; /* status returned by call */ diff --git a/server/thread.c b/server/thread.c index c0fb0eef637..5f8493dd309 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1713,6 +1713,7 @@ DECL_HANDLER(queue_apc) thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ); break; case APC_VIRTUAL_ALLOC: + case APC_VIRTUAL_ALLOC_EX: case APC_VIRTUAL_FREE: case APC_VIRTUAL_PROTECT: case APC_VIRTUAL_FLUSH: diff --git a/server/trace.c b/server/trace.c index cb58ab5162a..560a1b617ea 100644 --- a/server/trace.c +++ b/server/trace.c @@ -189,6 +189,12 @@ static void dump_apc_call( const char *prefix, const apc_call_t *call ) dump_uint64( ",zero_bits=", &call->virtual_alloc.zero_bits ); fprintf( stderr, ",op_type=%x,prot=%x", call->virtual_alloc.op_type, call->virtual_alloc.prot ); break; + case APC_VIRTUAL_ALLOC_EX: + dump_uint64( "APC_VIRTUAL_ALLOC,addr==", &call->virtual_alloc_ex.addr ); + dump_uint64( ",size=", &call->virtual_alloc_ex.size ); + dump_uint64( ",limit=", &call->virtual_alloc_ex.limit ); + fprintf( stderr, ",op_type=%x,prot=%x", call->virtual_alloc_ex.op_type, call->virtual_alloc_ex.prot ); + break; case APC_VIRTUAL_FREE: dump_uint64( "APC_VIRTUAL_FREE,addr=", &call->virtual_free.addr ); dump_uint64( ",size=", &call->virtual_free.size );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/virtual.c | 164 +++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+)
diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 72d15c84f32..2c5e1d6ecc3 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -423,6 +423,169 @@ static void test_NtAllocateVirtualMemoryEx(void) } }
+static void test_NtAllocateVirtualMemoryEx_address_requirements(void) +{ + MEM_EXTENDED_PARAMETER ext[2]; + MEM_ADDRESS_REQUIREMENTS a; + NTSTATUS status; + SYSTEM_INFO si; + SIZE_T size; + void *addr; + + if (!pNtAllocateVirtualMemoryEx) + { + win_skip("NtAllocateVirtualMemoryEx() is missing\n"); + return; + } + + GetSystemInfo(&si); + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = 0; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = MemExtendedParameterMax; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset(&a, 0, sizeof(a)); + ext[0].Type = MemExtendedParameterAddressRequirements; + ext[0].Pointer = &a; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + ext[1] = ext[0]; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 2); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = NULL; + a.Alignment = 0; + + a.HighestEndingAddress = (void *)(0x20001000 + 1); + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 2); + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20000800 - 1); + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (char *)si.lpMaximumApplicationAddress + 0x1000; + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (char *)si.lpMaximumApplicationAddress; + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 1); + size = 0x40000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!((ULONG_PTR)addr & 0xffff), "Unexpected addr %p.\n", addr); + ok((ULONG_PTR)addr + size <= 0x20001000, "Unexpected addr %p.\n", addr); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + + size = 0x40000; + a.HighestEndingAddress = (void *)(0x20001000 - 1); + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 24, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + ok(status == STATUS_INVALID_PARAMETER_3 || status == STATUS_INVALID_PARAMETER, + "Unexpected status %08lx.\n", status); + + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0xffffffff, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (is_win64 || is_wow64) + ok(!status || status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + else + ok(status == STATUS_INVALID_PARAMETER_3 || status == STATUS_INVALID_PARAMETER, + "Unexpected status %08lx.\n", status); + + if (!status) + { + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + } + + a.HighestEndingAddress = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status || status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + if (!status) + { + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + } + + + a.HighestEndingAddress = (void *)(0x20001000 - 1); + a.Alignment = 0x10000; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!((ULONG_PTR)addr & 0xffff), "Unexpected addr %p.\n", addr); + ok((ULONG_PTR)addr + size < 0x20001000, "Unexpected addr %p.\n", addr); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); +} + struct test_stack_size_thread_args { DWORD expect_committed; @@ -1708,6 +1871,7 @@ START_TEST(virtual)
test_NtAllocateVirtualMemory(); test_NtAllocateVirtualMemoryEx(); + test_NtAllocateVirtualMemoryEx_address_requirements(); test_NtFreeVirtualMemory(); test_RtlCreateUserStack(); test_NtMapViewOfSection();
From: Paul Gofman pgofman@codeweavers.com
--- dlls/wow64/struct32.h | 7 +++++++ dlls/wow64/virtual.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index 0a0324a22f3..9511f80523f 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -685,4 +685,11 @@ struct __server_request_info32 struct __server_iovec32 data[__SERVER_MAX_DATA]; };
+typedef struct +{ + ULONG LowestStartingAddress; + ULONG HighestEndingAddress; + ULONG Alignment; +} MEM_ADDRESS_REQUIREMENTS32; + #endif /* __WOW64_STRUCT32_H */ diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c index 4d3bb90386a..0d211113a0f 100644 --- a/dlls/wow64/virtual.c +++ b/dlls/wow64/virtual.c @@ -91,10 +91,42 @@ NTSTATUS WINAPI wow64_NtAllocateVirtualMemoryEx( UINT *args ) void *addr; SIZE_T size; NTSTATUS status; + SIZE_T alloc_size = count * sizeof(*params); + MEM_EXTENDED_PARAMETER *params64; + MEM_ADDRESS_REQUIREMENTS *buf; + unsigned int i; + + if (count && !params) return STATUS_INVALID_PARAMETER; + + for (i = 0; i < count; ++i) + { + if (params[i].Type == MemExtendedParameterAddressRequirements) + alloc_size += sizeof(MEM_ADDRESS_REQUIREMENTS); + else if (params[i].Type && params[i].Type < MemExtendedParameterMax) + FIXME( "Unsupported parameter type %d.\n", params[i].Type); + } + + params64 = Wow64AllocateTemp( alloc_size ); + memcpy( params64, params, count * sizeof(*params64) ); + buf = (MEM_ADDRESS_REQUIREMENTS *)((char *)params64 + count * sizeof(*params64)); + for (i = 0; i < count; ++i) + { + if (params64[i].Type == MemExtendedParameterAddressRequirements) + { + MEM_ADDRESS_REQUIREMENTS32 *p = (MEM_ADDRESS_REQUIREMENTS32 *)params[i].Pointer; + + if (p->HighestEndingAddress > highest_user_address) return STATUS_INVALID_PARAMETER; + + buf->LowestStartingAddress = (void *)(ULONG_PTR)p->LowestStartingAddress; + buf->HighestEndingAddress = (void *)(ULONG_PTR)p->HighestEndingAddress; + buf->Alignment = p->Alignment; + params64[i].Pointer = buf; + ++buf; + } + }
- if (count) FIXME( "%ld extended parameters %p\n", count, params ); status = NtAllocateVirtualMemoryEx( process, addr_32to64( &addr, addr32 ), size_32to64( &size, size32 ), - type, protect, params, count ); + type, protect, params64, count ); if (!status) { put_addr( addr32, addr );
From: Paul Gofman pgofman@codeweavers.com
To prevent 64 bit NtAllocateVirtualMemoryEx() allocating from above 32 bit user space limit. --- dlls/wow64/virtual.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-)
diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c index 0d211113a0f..aab31f499ae 100644 --- a/dlls/wow64/virtual.c +++ b/dlls/wow64/virtual.c @@ -93,6 +93,8 @@ NTSTATUS WINAPI wow64_NtAllocateVirtualMemoryEx( UINT *args ) NTSTATUS status; SIZE_T alloc_size = count * sizeof(*params); MEM_EXTENDED_PARAMETER *params64; + BOOL set_highest_address = (!*addr32 && process == GetCurrentProcess()); + BOOL add_address_requirements = set_highest_address; MEM_ADDRESS_REQUIREMENTS *buf; unsigned int i;
@@ -101,24 +103,49 @@ NTSTATUS WINAPI wow64_NtAllocateVirtualMemoryEx( UINT *args ) for (i = 0; i < count; ++i) { if (params[i].Type == MemExtendedParameterAddressRequirements) + { alloc_size += sizeof(MEM_ADDRESS_REQUIREMENTS); + add_address_requirements = FALSE; + } else if (params[i].Type && params[i].Type < MemExtendedParameterMax) + { FIXME( "Unsupported parameter type %d.\n", params[i].Type); + } }
+ if (add_address_requirements) + alloc_size += sizeof(*params) + sizeof(MEM_ADDRESS_REQUIREMENTS); params64 = Wow64AllocateTemp( alloc_size ); memcpy( params64, params, count * sizeof(*params64) ); - buf = (MEM_ADDRESS_REQUIREMENTS *)((char *)params64 + count * sizeof(*params64)); + if (add_address_requirements) + { + buf = (MEM_ADDRESS_REQUIREMENTS *)((char *)params64 + (count + 1) * sizeof(*params64)); + params64[count].Type = MemExtendedParameterAddressRequirements; + params64[count].Pointer = buf; + memset(buf, 0, sizeof(*buf)); + buf->HighestEndingAddress = (void *)(ULONG_PTR)highest_user_address; + ++buf; + } + else + { + buf = (MEM_ADDRESS_REQUIREMENTS *)((char *)params64 + count * sizeof(*params64)); + } for (i = 0; i < count; ++i) { if (params64[i].Type == MemExtendedParameterAddressRequirements) { MEM_ADDRESS_REQUIREMENTS32 *p = (MEM_ADDRESS_REQUIREMENTS32 *)params[i].Pointer;
- if (p->HighestEndingAddress > highest_user_address) return STATUS_INVALID_PARAMETER; - buf->LowestStartingAddress = (void *)(ULONG_PTR)p->LowestStartingAddress; - buf->HighestEndingAddress = (void *)(ULONG_PTR)p->HighestEndingAddress; + if (p->HighestEndingAddress) + { + if (p->HighestEndingAddress > highest_user_address) return STATUS_INVALID_PARAMETER; + buf->HighestEndingAddress = (void *)(ULONG_PTR)p->HighestEndingAddress; + } + else + { + buf->HighestEndingAddress = set_highest_address ? (void *)(ULONG_PTR)highest_user_address : NULL; + } buf->Alignment = p->Alignment; params64[i].Pointer = buf; ++buf; @@ -126,7 +153,7 @@ NTSTATUS WINAPI wow64_NtAllocateVirtualMemoryEx( UINT *args ) }
status = NtAllocateVirtualMemoryEx( process, addr_32to64( &addr, addr32 ), size_32to64( &size, size32 ), - type, protect, params64, count ); + type, protect, params64, count + add_address_requirements ); if (!status) { put_addr( addr32, addr );
v5: - do not enforce HighestEndingAddress in wow64_NtAllocateVirtualMemoryEx() for other process (that will be set in invoke_system_apc()); - strip off the last two patches from the MR (alignment implementation).