From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/unix/server.c | 29 +++++++- dlls/ntdll/unix/virtual.c | 138 +++++++++++++++++++++++++++++--------- include/winnt.h | 7 ++ server/protocol.def | 17 +++++ server/thread.c | 1 + server/trace.c | 6 ++ 6 files changed, 166 insertions(+), 32 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 ffdefc8acd0..7b2b019c345 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 )) { @@ -3716,13 +3716,13 @@ void virtual_set_large_address_space(void) user_space_limit = working_set_limit = address_space_limit; }
- /*********************************************************************** - * NtAllocateVirtualMemory (NTDLL.@) - * ZwAllocateVirtualMemory (NTDLL.@) + * NtAllocateVirtualMemoryEx (NTDLL.@) + * ZwAllocateVirtualMemoryEx (NTDLL.@) */ -NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR zero_bits, - SIZE_T *size_ptr, ULONG type, ULONG protect ) +NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *size_ptr, ULONG type, + ULONG protect, MEM_EXTENDED_PARAMETER *parameters, + ULONG count ) { void *base; unsigned int vprot; @@ -3731,15 +3731,53 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z sigset_t sigset; SIZE_T size = *size_ptr; NTSTATUS status = STATUS_SUCCESS; + ULONG_PTR limit = 0; + + TRACE("%p %p %08lx %x %08x %p %u\n", process, *ret, size, type, protect, parameters, count ); + + if (count && !parameters) return STATUS_INVALID_PARAMETER; + + 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 );
- TRACE("%p %p %08lx %x %08x\n", process, *ret, size, type, protect ); + 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) 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()) { @@ -3748,21 +3786,21 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z
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; + 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.status == STATUS_SUCCESS) + if (result.virtual_alloc_ex.status == STATUS_SUCCESS) { - *ret = wine_server_get_ptr( result.virtual_alloc.addr ); - *size_ptr = result.virtual_alloc.size; + *ret = wine_server_get_ptr( result.virtual_alloc_ex.addr ); + *size_ptr = result.virtual_alloc_ex.size; } - return result.virtual_alloc.status; + return result.virtual_alloc_ex.status; }
/* Round parameters to a page boundary */ @@ -3816,7 +3854,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; } @@ -3856,18 +3894,56 @@ NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR z }
/*********************************************************************** - * NtAllocateVirtualMemoryEx (NTDLL.@) - * ZwAllocateVirtualMemoryEx (NTDLL.@) + * NtAllocateVirtualMemory (NTDLL.@) + * ZwAllocateVirtualMemory (NTDLL.@) */ -NTSTATUS WINAPI NtAllocateVirtualMemoryEx( HANDLE process, PVOID *ret, SIZE_T *size_ptr, ULONG type, - ULONG protect, MEM_EXTENDED_PARAMETER *parameters, - ULONG count ) +NTSTATUS WINAPI NtAllocateVirtualMemory( HANDLE process, PVOID *ret, ULONG_PTR zero_bits, + SIZE_T *size_ptr, ULONG type, ULONG protect ) { - if (count && !parameters) return STATUS_INVALID_PARAMETER; + MEM_ADDRESS_REQUIREMENTS r = { NULL }; + MEM_EXTENDED_PARAMETER ext = + { + .Type = MemExtendedParameterAddressRequirements, + .Pointer = &r + }; + NTSTATUS status; + + + 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; + }
- if (count) FIXME( "Ignoring %d extended parameters %p\n", count, parameters ); + if (!*ret) + r.HighestEndingAddress = min( (char *)get_zero_bits_mask( zero_bits ), (char *)user_space_limit - 1 );
- return NtAllocateVirtualMemory( process, ret, 0, size_ptr, type, protect ); + return NtAllocateVirtualMemoryEx( process, ret, size_ptr, type, protect, &ext, 1 ); }
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 d828d41d1f7..bc8e328e3b1 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 f49fbf40b78..ab783cc3a7d 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1711,6 +1711,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 c6a324bb905..b79a0434eb1 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 );