From 7e8bb68f95c81819a4576287130068d6abdd0888 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 5 Oct 2022 16:25:05 -0500 Subject: [PATCH] ntdll: Support HighestEndingAddress in NtAllocateVirtualMemoryEx(). --- dlls/ntdll/unix/server.c | 29 +++++++++++++- dlls/ntdll/unix/virtual.c | 73 ++++++++++++++++++++++++++++++++-- include/wine/server_protocol.h | 19 ++++++++- include/winnt.h | 7 ++++ server/protocol.def | 17 ++++++++ server/thread.c | 1 + server/trace.c | 6 +++ 7 files changed, 147 insertions(+), 5 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 e5816dc1fc6..394e146eb05 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -1921,7 +1921,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 )) { @@ -3894,11 +3894,78 @@ 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) + { + MEM_ADDRESS_REQUIREMENTS *r = NULL; + unsigned int i; - return NtAllocateVirtualMemory( process, ret, 0, size_ptr, type, protect ); + 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 (r) + { + WARN( "Duplicate parameter.\n" ); + return STATUS_INVALID_PARAMETER; + } + 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 allocate_virtual_memory( ret, size_ptr, type, protect, limit ); } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index f77f57ff04e..58dd870a6ce 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -477,6 +477,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, @@ -519,6 +520,15 @@ typedef union unsigned int prot; } virtual_alloc; struct + { + enum apc_type type; + unsigned int op_type; + client_ptr_t addr; + mem_size_t size; + mem_size_t limit; + unsigned int prot; + } virtual_alloc_ex; + struct { enum apc_type type; unsigned int op_type; @@ -614,6 +624,13 @@ typedef union mem_size_t size; } virtual_alloc; struct + { + enum apc_type type; + unsigned int status; + client_ptr_t addr; + mem_size_t size; + } virtual_alloc_ex; + struct { enum apc_type type; unsigned int status; @@ -6338,7 +6355,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 756 +#define SERVER_PROTOCOL_VERSION 757 /* ### protocol_version end ### */ diff --git a/include/winnt.h b/include/winnt.h index 3bbeeb96ec2..14cfe64af74 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 );