[PATCH v10 0/5] MR10611: LPC: Implement proper LPC system
Until now, LPC was just a stub, any application that relied on LPC for communication would fail. This change implements parts of LPC, to allow for IPC between programs. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30069 - fully fixed, sqlservr is able to run as expected Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59494 - partially fixed, starmoney doesn't flat out crash anymore, but it doesn't start fully, possibly because it uses the ALPC system which was introduced in Windows Vista, the version Starmoney 10 was advertised for -- v10: LPC: Properly handle port closes https://gitlab.winehq.org/wine/wine/-/merge_requests/10611
From: Rose Hellsing <rose@pinkro.se> Implement the server-side LPC port object with basic infrastructure including the object operations table, port types, and the create_lpc_port request handler. Add ntdll implementations for NtCreatePort and NtCreateWaitablePort that create LPC ports via the server. This is the foundation for full LPC support needed by applications like StarMoney and MSDE 2000. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30069 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59494 --- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/unix/sync.c | 60 ++++++++++- dlls/wow64/sync.c | 20 ++++ include/winternl.h | 1 + server/Makefile.in | 1 + server/lpc_port.c | 194 ++++++++++++++++++++++++++++++++++++ server/protocol.def | 12 +++ 8 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 server/lpc_port.c diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index f396c334e2d..5aa620bd60b 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -206,7 +206,7 @@ @ stdcall -syscall NtCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) @ stdcall -syscall NtCreateTransaction(ptr long ptr ptr long long long long ptr ptr) @ stdcall -syscall NtCreateUserProcess(ptr ptr long long ptr ptr long long ptr ptr ptr) -# @ stub NtCreateWaitablePort +@ stdcall -syscall NtCreateWaitablePort(ptr ptr long long long) @ stdcall -arch=i386 NtCurrentTeb() @ stdcall -syscall NtDebugActiveProcess(long long) @ stdcall -syscall NtDebugContinue(long ptr long) @@ -1280,7 +1280,7 @@ @ stdcall -private ZwCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) NtCreateToken @ stdcall -private ZwCreateTransaction(ptr long ptr ptr long long long long ptr ptr) NtCreateTransaction @ stdcall -private ZwCreateUserProcess(ptr ptr long long ptr ptr long long ptr ptr ptr) NtCreateUserProcess -# @ stub ZwCreateWaitablePort +@ stdcall -private ZwCreateWaitablePort(ptr ptr long long long) NtCreateWaitablePort @ stdcall -private ZwDebugActiveProcess(long long) NtDebugActiveProcess @ stdcall -private ZwDebugContinue(long ptr long) NtDebugContinue @ stdcall -private ZwDelayExecution(long ptr) NtDelayExecution diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 954ace7824e..517f7690bf1 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -400,6 +400,7 @@ DEFINE_SYSCALL(NtCreateTimer, (HANDLE *handle, ACCESS_MASK access, const OBJECT_ DEFINE_SYSCALL(NtCreateToken, (HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, TOKEN_TYPE type, LUID *token_id, LARGE_INTEGER *expire, TOKEN_USER *user, TOKEN_GROUPS *groups, TOKEN_PRIVILEGES *privs, TOKEN_OWNER *owner, TOKEN_PRIMARY_GROUP *group, TOKEN_DEFAULT_DACL *dacl, TOKEN_SOURCE *source)) DEFINE_SYSCALL(NtCreateTransaction, (HANDLE *handle, ACCESS_MASK mask, OBJECT_ATTRIBUTES *obj_attr, GUID *guid, HANDLE tm, ULONG options, ULONG isol_level, ULONG isol_flags, PLARGE_INTEGER timeout, UNICODE_STRING *description)) DEFINE_SYSCALL(NtCreateUserProcess, (HANDLE *process_handle_ptr, HANDLE *thread_handle_ptr, ACCESS_MASK process_access, ACCESS_MASK thread_access, OBJECT_ATTRIBUTES *process_attr, OBJECT_ATTRIBUTES *thread_attr, ULONG process_flags, ULONG thread_flags, RTL_USER_PROCESS_PARAMETERS *params, PS_CREATE_INFO *info, PS_ATTRIBUTE_LIST *ps_attr)) +DEFINE_SYSCALL(NtCreateWaitablePort, (HANDLE *handle, OBJECT_ATTRIBUTES *attr, ULONG info_len, ULONG data_len, ULONG reserved)) DEFINE_SYSCALL(NtDebugActiveProcess, (HANDLE process, HANDLE debug)) DEFINE_SYSCALL(NtDebugContinue, (HANDLE handle, CLIENT_ID *client, NTSTATUS status)) DEFINE_SYSCALL(NtDelayExecution, (BOOLEAN alertable, const LARGE_INTEGER *timeout)) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index bc9bbf2c30d..c478a19db52 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -74,6 +74,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(sync); +/* LPC port access rights */ +#define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x0001) + HANDLE keyed_event = 0; int inproc_device_fd = -1; @@ -3068,8 +3071,61 @@ NTSTATUS WINAPI NtOpenSection( HANDLE *handle, ACCESS_MASK access, const OBJECT_ NTSTATUS WINAPI NtCreatePort( HANDLE *handle, OBJECT_ATTRIBUTES *attr, ULONG info_len, ULONG data_len, ULONG *reserved ) { - FIXME( "(%p,%p,%u,%u,%p),stub!\n", handle, attr, info_len, data_len, reserved ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + data_size_t len; + struct object_attributes *objattr; + + TRACE( "(%p,%p,%u,%u,%p)\n", handle, attr, info_len, data_len, reserved ); + + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) + return ret; + + SERVER_START_REQ( create_lpc_port ) + { + req->access = PORT_ALL_ACCESS; + req->flags = 0; + req->max_msg_len = data_len; + req->max_connect_info = info_len; + wine_server_add_data( req, objattr, len ); + if (!(ret = wine_server_call( req ))) + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + free( objattr ); + return ret; +} + + +/*********************************************************************** + * NtCreateWaitablePort (NTDLL.@) + */ +NTSTATUS WINAPI NtCreateWaitablePort( HANDLE *handle, OBJECT_ATTRIBUTES *attr, ULONG info_len, + ULONG data_len, ULONG reserved ) +{ + unsigned int ret; + data_size_t len; + struct object_attributes *objattr; + + TRACE( "(%p,%p,%u,%u,%u)\n", handle, attr, info_len, data_len, reserved ); + + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) + return ret; + + SERVER_START_REQ( create_lpc_port ) + { + req->access = PORT_ALL_ACCESS; + req->flags = 0x0001; /* PORT_FLAG_WAITABLE */ + req->max_msg_len = data_len; + req->max_connect_info = info_len; + wine_server_add_data( req, objattr, len ); + if (!(ret = wine_server_call( req ))) + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + free( objattr ); + return ret; } diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index d5b52a5c815..dd2ff72e9ed 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -402,6 +402,26 @@ NTSTATUS WINAPI wow64_NtCreateSection( UINT *args ) return status; } +/********************************************************************** + * wow64_NtCreateWaitablePort + */ +NTSTATUS WINAPI wow64_NtCreateWaitablePort( UINT *args ) +{ + ULONG *handle_ptr = get_ptr( &args ); + OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args ); + ULONG info_len = get_ulong( &args ); + ULONG data_len = get_ulong( &args ); + ULONG reserved = get_ulong( &args ); + + struct object_attr64 attr; + HANDLE handle = 0; + NTSTATUS status; + + *handle_ptr = 0; + status = NtCreateWaitablePort( &handle, objattr_32to64( &attr, attr32 ), info_len, data_len, reserved ); + put_handle( handle_ptr, handle ); + return status; +} /********************************************************************** * wow64_NtCreateSemaphore diff --git a/include/winternl.h b/include/winternl.h index a20aca55a0e..3abf69f06b7 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4784,6 +4784,7 @@ NTSYSAPI NTSTATUS WINAPI NtCreateTimer(HANDLE*, ACCESS_MASK, const OBJECT_ATTRI NTSYSAPI NTSTATUS WINAPI NtCreateToken(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,TOKEN_TYPE,PLUID,PLARGE_INTEGER,PTOKEN_USER,PTOKEN_GROUPS,PTOKEN_PRIVILEGES,PTOKEN_OWNER,PTOKEN_PRIMARY_GROUP,PTOKEN_DEFAULT_DACL,PTOKEN_SOURCE); NTSYSAPI NTSTATUS WINAPI NtCreateTransaction(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,LPGUID,HANDLE,ULONG,ULONG,ULONG,PLARGE_INTEGER,PUNICODE_STRING); NTSYSAPI NTSTATUS WINAPI NtCreateUserProcess(HANDLE*,HANDLE*,ACCESS_MASK,ACCESS_MASK,OBJECT_ATTRIBUTES*,OBJECT_ATTRIBUTES*,ULONG,ULONG,RTL_USER_PROCESS_PARAMETERS*,PS_CREATE_INFO*,PS_ATTRIBUTE_LIST*); +NTSYSAPI NTSTATUS WINAPI NtCreateWaitablePort(PHANDLE,POBJECT_ATTRIBUTES,ULONG,ULONG,ULONG); NTSYSAPI NTSTATUS WINAPI NtDebugActiveProcess(HANDLE,HANDLE); NTSYSAPI NTSTATUS WINAPI NtDebugContinue(HANDLE,CLIENT_ID*,NTSTATUS); NTSYSAPI NTSTATUS WINAPI NtDelayExecution(BOOLEAN,const LARGE_INTEGER*); diff --git a/server/Makefile.in b/server/Makefile.in index 84a6bd74d9d..b6dc080896c 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -18,6 +18,7 @@ SOURCES = \ handle.c \ hook.c \ inproc_sync.c \ + lpc_port.c \ mach.c \ mailslot.c \ main.c \ diff --git a/server/lpc_port.c b/server/lpc_port.c new file mode 100644 index 00000000000..5905facb6ba --- /dev/null +++ b/server/lpc_port.c @@ -0,0 +1,194 @@ +/* + * Server-side LPC port management + * + * Copyright 2026 Wine project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" + +#include "handle.h" +#include "thread.h" +#include "process.h" +#include "request.h" +#include "security.h" +#include "object.h" + +/* Port access rights */ +#define PORT_CONNECT 0x0001 +#define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | PORT_CONNECT) + +/* Port flags */ +#define PORT_FLAG_WAITABLE 0x0001 + +/* Maximum message size */ +#define MAX_LPC_MESSAGE_SIZE 0x40000 + +static const WCHAR lpc_port_name[] = {'L','P','C',' ','P','o','r','t'}; + +struct type_descr lpc_port_type = +{ + { lpc_port_name, sizeof(lpc_port_name) }, /* name */ + PORT_ALL_ACCESS, /* valid_access */ + { /* mapping */ + STANDARD_RIGHTS_READ | PORT_CONNECT, + STANDARD_RIGHTS_WRITE, + STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, + PORT_ALL_ACCESS + }, +}; + +/* LPC port object */ +struct lpc_port +{ + struct object obj; /* object header */ + unsigned int flags; /* PORT_FLAG_* */ + unsigned int max_msg_len; /* maximum message length */ + unsigned int max_connect_info;/* maximum connection info length */ + struct process *server_process; /* server process (for named ports) */ + struct object *wait_event; /* event for WaitForSingleObject (waitable ports) */ +}; + +static void lpc_port_dump( struct object *obj, int verbose ); +static struct object *lpc_port_get_sync( struct object *obj ); +static void lpc_port_destroy( struct object *obj ); + +static const struct object_ops lpc_port_ops = +{ + sizeof(struct lpc_port), /* size */ + &lpc_port_type, /* type */ + lpc_port_dump, /* dump */ + NULL, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + lpc_port_get_sync, /* get_sync */ + default_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + default_get_full_name, /* get_full_name */ + no_lookup_name, /* lookup_name */ + directory_link_name, /* link_name */ + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + lpc_port_destroy /* destroy */ +}; + +static struct lpc_port *create_lpc_port( struct object *root, const struct unicode_str *name, + unsigned int attr, unsigned int flags, + unsigned int max_msg_len, unsigned int max_connect_info, + const struct security_descriptor *sd ) +{ + struct lpc_port *port; + + if (max_msg_len > MAX_LPC_MESSAGE_SIZE) + max_msg_len = MAX_LPC_MESSAGE_SIZE; + if (max_connect_info > MAX_LPC_MESSAGE_SIZE) + max_connect_info = MAX_LPC_MESSAGE_SIZE; + + if ((port = create_named_object( root, &lpc_port_ops, name, attr, sd ))) + { + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + port->flags = flags; + port->max_msg_len = max_msg_len ? max_msg_len : MAX_LPC_MESSAGE_SIZE; + port->max_connect_info = max_connect_info ? max_connect_info : 256; + port->server_process = NULL; + port->wait_event = NULL; + + if (name->len) + port->server_process = (struct process *)grab_object( current->process ); + + if (flags & PORT_FLAG_WAITABLE) + { + port->wait_event = create_internal_sync( 1, 0 ); + if (!port->wait_event) + { + release_object( port ); + return NULL; + } + } + } + } + return port; +} + +static void lpc_port_dump( struct object *obj, int verbose ) +{ + struct lpc_port *port = (struct lpc_port *)obj; + assert( obj->ops == &lpc_port_ops ); + fprintf( stderr, "LPC Port flags=%04x max_msg=%u max_connect=%u\n", + port->flags, port->max_msg_len, port->max_connect_info ); +} + +static struct object *lpc_port_get_sync( struct object *obj ) +{ + struct lpc_port *port = (struct lpc_port *)obj; + assert( obj->ops == &lpc_port_ops ); + + if (port->wait_event) + return grab_object( port->wait_event ); + + return NULL; +} + +static void lpc_port_destroy( struct object *obj ) +{ + struct lpc_port *port = (struct lpc_port *)obj; + assert( obj->ops == &lpc_port_ops ); + + if (port->wait_event) release_object( port->wait_event ); + if (port->server_process) release_object( port->server_process ); +} + +/* Create an LPC port */ +DECL_HANDLER(create_lpc_port) +{ + struct lpc_port *port; + struct unicode_str name; + struct object *root; + const struct security_descriptor *sd; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); + + if (!objattr) return; + + if ((port = create_lpc_port( root, &name, objattr->attributes, req->flags, + req->max_msg_len, req->max_connect_info, sd ))) + { + if (get_error() == STATUS_OBJECT_NAME_EXISTS) + reply->handle = alloc_handle( current->process, port, req->access, objattr->attributes ); + else + reply->handle = alloc_handle_no_access_check( current->process, port, + req->access, objattr->attributes ); + release_object( port ); + } + + if (root) release_object( root ); +} diff --git a/server/protocol.def b/server/protocol.def index 5bca381fd91..a26e92f7dd3 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4274,3 +4274,15 @@ enum inproc_sync_type data_size_t runtime_size; /* size of client runtime data */ VARARG(runtime,bytes); /* client runtime data */ @END + + +/* Create an LPC port */ +@REQ(create_lpc_port) + unsigned int access; /* desired access rights */ + unsigned int flags; /* port flags (e.g., waitable) */ + unsigned int max_msg_len; /* maximum message data length */ + unsigned int max_connect_info; /* maximum connection info length */ + VARARG(objattr,object_attributes); /* object attributes */ +@REPLY + obj_handle_t handle; /* handle to the port */ +@END -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611
From: Rose Hellsing <rose@pinkro.se> --- dlls/ntdll/unix/sync.c | 165 ++++++++++++++-- server/lpc_port.c | 419 ++++++++++++++++++++++++++++++++++++++++- server/protocol.def | 52 +++++ 3 files changed, 617 insertions(+), 19 deletions(-) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index c478a19db52..3179698202e 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -3136,10 +3136,89 @@ NTSTATUS WINAPI NtConnectPort( HANDLE *handle, UNICODE_STRING *name, SECURITY_QU LPC_SECTION_WRITE *write, LPC_SECTION_READ *read, ULONG *max_len, void *info, ULONG *info_len ) { - FIXME( "(%p,%s,%p,%p,%p,%p,%p,%p),stub!\n", handle, debugstr_us(name), qos, - write, read, max_len, info, info_len ); - if (info && info_len) TRACE("msg = %s\n", debugstr_an( info, *info_len )); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + data_size_t len; + struct object_attributes *objattr; + OBJECT_ATTRIBUTES attr; + ULONG in_len = (info && info_len) ? *info_len : 0; + HANDLE port_handle; + + TRACE( "(%p,%s,%p,%p,%p,%p,%p,%p)\n", handle, debugstr_us(name), qos, write, read, max_len, info, info_len ); + + if (write) + FIXME( "LPC_SECTION_WRITE not supported\n" ); + if (read) + FIXME( "LPC_SECTION_READ not supported\n" ); + + *handle = 0; + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = name; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = qos; + + if ((ret = alloc_object_attributes( &attr, &objattr, &len ))) + return ret; + + SERVER_START_REQ( connect_lpc_port ) + { + req->access = PORT_ALL_ACCESS; + req->info_size = in_len; + wine_server_add_data( req, objattr, len ); + if (in_len) wine_server_add_data( req, info, in_len ); + if (!(ret = wine_server_call( req ))) + { + port_handle = wine_server_ptr_handle( reply->handle ); + if (info_len) + *info_len = reply->info_size; + } + } + SERVER_END_REQ; + free( objattr ); + + if (ret) return ret; + + /* Wait for the connection to be accepted/rejected by the server */ + for (;;) + { + unsigned int connect_status; + + SERVER_START_REQ( get_lpc_connect_status ) + { + req->handle = wine_server_obj_handle( port_handle ); + ret = wine_server_call( req ); + connect_status = reply->status; + } + SERVER_END_REQ; + + if (ret) + { + NtClose( port_handle ); + return ret; + } + + if (connect_status != STATUS_PENDING) + { + if (connect_status == STATUS_SUCCESS) + { + *handle = port_handle; + return STATUS_SUCCESS; + } + else + { + NtClose( port_handle ); + return connect_status; + } + } + + ret = NtWaitForSingleObject( port_handle, FALSE, NULL ); + if (ret) + { + NtClose( port_handle ); + return ret; + } + } } @@ -3150,9 +3229,11 @@ NTSTATUS WINAPI NtSecureConnectPort( HANDLE *handle, UNICODE_STRING *name, SECUR LPC_SECTION_WRITE *write, PSID sid, LPC_SECTION_READ *read, ULONG *max_len, void *info, ULONG *info_len ) { - FIXME( "(%p,%s,%p,%p,%p,%p,%p,%p,%p),stub!\n", handle, debugstr_us(name), qos, - write, sid, read, max_len, info, info_len ); - return STATUS_NOT_IMPLEMENTED; + TRACE( "(%p,%s,%p,%p,%p,%p,%p,%p,%p)\n", handle, debugstr_us(name), qos, write, sid, read, max_len, info, info_len ); + + if (sid) + FIXME( "SID verification not implemented\n" ); + return NtConnectPort( handle, name, qos, write, read, max_len, info, info_len ); } @@ -3161,8 +3242,37 @@ NTSTATUS WINAPI NtSecureConnectPort( HANDLE *handle, UNICODE_STRING *name, SECUR */ NTSTATUS WINAPI NtListenPort( HANDLE handle, LPC_MESSAGE *msg ) { - FIXME("(%p,%p),stub!\n", handle, msg ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p,%p)\n", handle, msg ); + + for (;;) + { + SERVER_START_REQ( listen_lpc_port ) + { + req->handle = wine_server_obj_handle( handle ); + wine_server_set_reply( req, msg ? msg->Data : NULL, msg ? 0x1000 : 0 ); + ret = wine_server_call( req ); + if (!ret && msg) + { + msg->DataSize = reply->msg_size; + msg->MessageSize = sizeof(*msg) + reply->msg_size; + msg->MessageType = 10; /* LPC_CONNECTION_REQUEST */ + msg->VirtualRangesOffset = 0; + msg->ClientId.UniqueProcess = ULongToHandle( reply->client_pid ); + msg->ClientId.UniqueThread = ULongToHandle( reply->client_tid ); + msg->MessageId = reply->msg_id; + msg->SectionSize = 0; + } + } + SERVER_END_REQ; + + if (ret != STATUS_PENDING) break; + + ret = NtWaitForSingleObject( handle, FALSE, NULL ); + if (ret) break; + } + return ret; } @@ -3172,8 +3282,28 @@ NTSTATUS WINAPI NtListenPort( HANDLE handle, LPC_MESSAGE *msg ) NTSTATUS WINAPI NtAcceptConnectPort( HANDLE *handle, ULONG id, LPC_MESSAGE *msg, BOOLEAN accept, LPC_SECTION_WRITE *write, LPC_SECTION_READ *read ) { - FIXME("(%p,%u,%p,%d,%p,%p),stub!\n", handle, id, msg, accept, write, read ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p,%u,%p,%d,%p,%p)\n", handle, id, msg, accept, write, read ); + + if (write) + FIXME( "LPC_SECTION_WRITE not supported\n" ); + if (read) + FIXME( "LPC_SECTION_READ not supported\n" ); + + *handle = 0; + + SERVER_START_REQ( accept_lpc_connect ) + { + req->handle = 0; + req->accept = accept; + req->msg_id = msg ? msg->MessageId : 0; + req->context = id; + if (!(ret = wine_server_call( req )) && accept) + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + return ret; } @@ -3182,8 +3312,17 @@ NTSTATUS WINAPI NtAcceptConnectPort( HANDLE *handle, ULONG id, LPC_MESSAGE *msg, */ NTSTATUS WINAPI NtCompleteConnectPort( HANDLE handle ) { - FIXME( "(%p),stub!\n", handle ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p)\n", handle ); + + SERVER_START_REQ( complete_lpc_connect ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + return ret; } diff --git a/server/lpc_port.c b/server/lpc_port.c index 5905facb6ba..cc13de09da7 100644 --- a/server/lpc_port.c +++ b/server/lpc_port.c @@ -24,6 +24,7 @@ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <string.h> #include "ntstatus.h" #define WIN32_NO_STATUS @@ -41,11 +42,26 @@ #define PORT_CONNECT 0x0001 #define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | PORT_CONNECT) +/* Port types */ +#define PORT_TYPE_SERVER 0x01 /* Named port that server listens on */ +#define PORT_TYPE_CLIENT 0x02 /* Client's end of connection */ +#define PORT_TYPE_CHANNEL 0x03 /* Server's per-client communication channel */ + /* Port flags */ #define PORT_FLAG_WAITABLE 0x0001 +/* LPC message types */ +#define LPC_CONNECTION_REQUEST 10 + /* Maximum message size */ #define MAX_LPC_MESSAGE_SIZE 0x40000 +#define MAX_LPC_DATA_SIZE 0x100000 + +/* Global counter for generating unique message IDs */ +static unsigned int global_msg_id_counter = 0; + +/* Global list of pending connection requests */ +static struct list global_pending_connects = LIST_INIT(global_pending_connects); static const WCHAR lpc_port_name[] = {'L','P','C',' ','P','o','r','t'}; @@ -61,15 +77,39 @@ struct type_descr lpc_port_type = }, }; +/* Internal message structure */ +struct lpc_message +{ + struct list entry; /* queue entry */ + struct list global_entry; /* entry in global_pending_connects */ + struct lpc_port *sender_port; /* port that sent this message */ + struct lpc_port *server_port; /* server port (for connection requests) */ + struct thread *sender_thread; /* thread that sent this message */ + unsigned int msg_id; /* unique message ID */ + unsigned int msg_type; /* LPC message type */ + process_id_t client_pid; /* sender's process ID */ + thread_id_t client_tid; /* sender's thread ID */ + data_size_t data_size; /* size of message data */ + char data[1]; /* variable-length message data */ +}; + /* LPC port object */ struct lpc_port { struct object obj; /* object header */ + unsigned int port_type; /* PORT_TYPE_* */ unsigned int flags; /* PORT_FLAG_* */ + struct lpc_port *connection_port; /* reference to connection port */ + struct lpc_port *connected_port; /* paired port: client <-> channel */ + struct list pending_connects;/* list of pending connection messages */ + struct object *queue_event; /* event signaled when message arrives */ unsigned int max_msg_len; /* maximum message length */ unsigned int max_connect_info;/* maximum connection info length */ - struct process *server_process; /* server process (for named ports) */ struct object *wait_event; /* event for WaitForSingleObject (waitable ports) */ + struct process *server_process; /* server process (for named ports) */ + struct thread *client_thread; /* client thread (for NtCompleteConnectPort) */ + struct object *connect_event; /* event signaled when connection completes */ + unsigned int connect_status; /* STATUS_SUCCESS or error code from accept */ }; static void lpc_port_dump( struct object *obj, int verbose ); @@ -101,6 +141,70 @@ static const struct object_ops lpc_port_ops = lpc_port_destroy /* destroy */ }; +/* Allocate a new message with the given data size */ +static struct lpc_message *alloc_lpc_message( data_size_t data_size ) +{ + struct lpc_message *msg; + data_size_t alloc_size; + + if (data_size > MAX_LPC_DATA_SIZE) + { + set_error( STATUS_INVALID_PARAMETER ); + return NULL; + } + + alloc_size = max( sizeof(struct lpc_message), offsetof(struct lpc_message, data) + data_size ); + msg = mem_alloc( alloc_size ); + if (msg) + { + memset( msg, 0, alloc_size ); + list_init( &msg->global_entry ); + msg->data_size = data_size; + } + return msg; +} + +/* Free a message */ +static void free_lpc_message( struct lpc_message *msg ) +{ + if (msg) + { + if (!list_empty( &msg->global_entry )) + list_remove( &msg->global_entry ); + if (msg->sender_port) release_object( msg->sender_port ); + if (msg->server_port) release_object( msg->server_port ); + if (msg->sender_thread) release_object( msg->sender_thread ); + free( msg ); + } +} + +/* Get the next globally unique message ID */ +static unsigned int get_next_msg_id( void ) +{ + return ++global_msg_id_counter; +} + +/* Signal the port's queue event to wake waiting threads */ +static void signal_port_queue( struct lpc_port *port ) +{ + if (port->queue_event) + signal_sync( port->queue_event ); + if (port->wait_event) + signal_sync( port->wait_event ); +} + +/* Reset the port's queue event after receiving a message */ +static void reset_port_queue( struct lpc_port *port ) +{ + if (list_empty( &port->pending_connects )) + { + if (port->queue_event) + reset_sync( port->queue_event ); + if (port->wait_event) + reset_sync( port->wait_event ); + } +} + static struct lpc_port *create_lpc_port( struct object *root, const struct unicode_str *name, unsigned int attr, unsigned int flags, unsigned int max_msg_len, unsigned int max_connect_info, @@ -117,14 +221,25 @@ static struct lpc_port *create_lpc_port( struct object *root, const struct unico { if (get_error() != STATUS_OBJECT_NAME_EXISTS) { + port->port_type = PORT_TYPE_SERVER; port->flags = flags; + port->connection_port = (struct lpc_port *)grab_object( port ); + port->connected_port = NULL; + list_init( &port->pending_connects ); + port->queue_event = create_internal_sync( 0, 0 ); port->max_msg_len = max_msg_len ? max_msg_len : MAX_LPC_MESSAGE_SIZE; port->max_connect_info = max_connect_info ? max_connect_info : 256; - port->server_process = NULL; port->wait_event = NULL; + port->server_process = (struct process *)grab_object( current->process ); + port->client_thread = NULL; + port->connect_event = NULL; + port->connect_status = STATUS_PENDING; - if (name->len) - port->server_process = (struct process *)grab_object( current->process ); + if (!port->queue_event) + { + release_object( port ); + return NULL; + } if (flags & PORT_FLAG_WAITABLE) { @@ -140,12 +255,69 @@ static struct lpc_port *create_lpc_port( struct object *root, const struct unico return port; } +/* Create a client or communication port (internal, no name) */ +static struct lpc_port *create_port_internal( unsigned int port_type, struct lpc_port *connection_port ) +{ + struct lpc_port *port; + + port = alloc_object( &lpc_port_ops ); + if (!port) return NULL; + + port->port_type = port_type; + port->flags = 0; + port->connection_port = (struct lpc_port *)grab_object( connection_port ); + port->connected_port = NULL; + list_init( &port->pending_connects ); + port->queue_event = create_internal_sync( 0, 0 ); + port->max_msg_len = connection_port->max_msg_len; + port->max_connect_info = connection_port->max_connect_info; + port->wait_event = NULL; + port->server_process = NULL; + port->client_thread = NULL; + port->connect_event = NULL; + port->connect_status = STATUS_PENDING; + + if (port_type == PORT_TYPE_CLIENT) + { + port->connect_event = (struct object *)create_server_internal_sync( 1, 0 ); + if (!port->connect_event) + { + release_object( port ); + return NULL; + } + } + + if (!port->queue_event) + { + release_object( port ); + return NULL; + } + + return port; +} + +/* Find a pending connection message by message ID */ +static struct lpc_message *find_pending_connect_global( unsigned int msg_id ) +{ + struct lpc_message *msg; + + LIST_FOR_EACH_ENTRY( msg, &global_pending_connects, struct lpc_message, global_entry ) + { + if (msg->msg_id == msg_id) + return msg; + } + return NULL; +} + static void lpc_port_dump( struct object *obj, int verbose ) { struct lpc_port *port = (struct lpc_port *)obj; + static const char *type_names[] = { "???", "SERVER", "CLIENT", "CHANNEL" }; + const char *type_name = port->port_type < 4 ? type_names[port->port_type] : "???"; + assert( obj->ops == &lpc_port_ops ); - fprintf( stderr, "LPC Port flags=%04x max_msg=%u max_connect=%u\n", - port->flags, port->max_msg_len, port->max_connect_info ); + fprintf( stderr, "LPC Port type=%s flags=%04x max_msg=%u max_connect=%u\n", + type_name, port->flags, port->max_msg_len, port->max_connect_info ); } static struct object *lpc_port_get_sync( struct object *obj ) @@ -153,19 +325,40 @@ static struct object *lpc_port_get_sync( struct object *obj ) struct lpc_port *port = (struct lpc_port *)obj; assert( obj->ops == &lpc_port_ops ); + if (port->port_type == PORT_TYPE_CLIENT && port->connect_event) + return grab_object( port->connect_event ); + if (port->wait_event) return grab_object( port->wait_event ); + if (port->queue_event) + return grab_object( port->queue_event ); + return NULL; } static void lpc_port_destroy( struct object *obj ) { struct lpc_port *port = (struct lpc_port *)obj; + struct lpc_message *msg, *next_msg; + assert( obj->ops == &lpc_port_ops ); + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->pending_connects, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + + if (port->queue_event) release_object( port->queue_event ); if (port->wait_event) release_object( port->wait_event ); + if (port->connect_event) release_object( port->connect_event ); + if (port->connection_port && port->connection_port != port) + release_object( port->connection_port ); + if (port->connected_port && port->connected_port != port) + release_object( port->connected_port ); if (port->server_process) release_object( port->server_process ); + if (port->client_thread) release_object( port->client_thread ); } /* Create an LPC port */ @@ -192,3 +385,217 @@ DECL_HANDLER(create_lpc_port) if (root) release_object( root ); } + +/* Connect to an LPC port */ +DECL_HANDLER(connect_lpc_port) +{ + struct lpc_port *connection_port; + struct lpc_port *client_port; + struct lpc_message *msg; + struct unicode_str name; + struct object *root; + const struct security_descriptor *sd; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); + data_size_t info_size = get_req_data_size(); + + if (!objattr) return; + + connection_port = (struct lpc_port *)open_named_object( root, &lpc_port_ops, &name, objattr->attributes ); + if (root) release_object( root ); + + if (!connection_port) + { + set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + return; + } + + if (connection_port->port_type != PORT_TYPE_SERVER) + { + release_object( connection_port ); + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + return; + } + + client_port = create_port_internal( PORT_TYPE_CLIENT, connection_port ); + if (!client_port) + { + release_object( connection_port ); + return; + } + + msg = alloc_lpc_message( info_size ); + if (!msg) + { + release_object( client_port ); + release_object( connection_port ); + return; + } + + msg->sender_port = (struct lpc_port *)grab_object( client_port ); + msg->server_port = (struct lpc_port *)grab_object( connection_port ); + msg->sender_thread = (struct thread *)grab_object( current ); + msg->msg_id = get_next_msg_id(); + msg->msg_type = LPC_CONNECTION_REQUEST; + msg->client_pid = current->process->id; + msg->client_tid = current->id; + if (info_size) + memcpy( msg->data, get_req_data(), info_size ); + + list_add_tail( &connection_port->pending_connects, &msg->entry ); + list_add_tail( &global_pending_connects, &msg->global_entry ); + signal_port_queue( connection_port ); + + reply->handle = alloc_handle_no_access_check( current->process, client_port, + req->access, objattr->attributes ); + reply->info_size = 0; + + release_object( client_port ); + release_object( connection_port ); +} + +/* Listen for connection requests on a port */ +DECL_HANDLER(listen_lpc_port) +{ + struct lpc_port *port; + struct lpc_message *msg; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + if (port->port_type != PORT_TYPE_SERVER) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + if (list_empty( &port->pending_connects )) + { + set_error( STATUS_PENDING ); + release_object( port ); + return; + } + + msg = LIST_ENTRY( list_head( &port->pending_connects ), struct lpc_message, entry ); + + reply->message = 0; + reply->msg_size = msg->data_size; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->msg_id = msg->msg_id; + + if (msg->data_size) + set_reply_data( msg->data, min( msg->data_size, get_reply_max_size() ) ); + + reset_port_queue( port ); + release_object( port ); +} + +/* Accept or reject a connection */ +DECL_HANDLER(accept_lpc_connect) +{ + struct lpc_port *connection_port; + struct lpc_port *comm_port = NULL; + struct lpc_port *client_port; + struct lpc_message *msg; + + msg = find_pending_connect_global( req->msg_id ); + if (!msg) + { + set_error( STATUS_INVALID_CID ); + return; + } + + connection_port = msg->server_port; + if (!connection_port || connection_port->port_type != PORT_TYPE_SERVER) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + return; + } + + client_port = msg->sender_port; + + /* Remove from port's pending list */ + list_remove( &msg->entry ); + + if (req->accept) + { + comm_port = create_port_internal( PORT_TYPE_CHANNEL, connection_port ); + if (!comm_port) + { + free_lpc_message( msg ); + return; + } + + comm_port->connected_port = (struct lpc_port *)grab_object( client_port ); + client_port->connected_port = (struct lpc_port *)grab_object( comm_port ); + client_port->connect_status = STATUS_SUCCESS; + + if (msg->sender_thread) + comm_port->client_thread = (struct thread *)grab_object( msg->sender_thread ); + + reply->handle = alloc_handle_no_access_check( current->process, comm_port, + PORT_ALL_ACCESS, 0 ); + release_object( comm_port ); + } + else + { + client_port->connect_status = STATUS_PORT_CONNECTION_REFUSED; + if (client_port->connect_event) + signal_sync( client_port->connect_event ); + reply->handle = 0; + } + + free_lpc_message( msg ); +} + +/* Complete the connection (wake the client) */ +DECL_HANDLER(complete_lpc_connect) +{ + struct lpc_port *port; + struct lpc_port *client_port; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + if (port->port_type != PORT_TYPE_CHANNEL) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + client_port = port->connected_port; + if (client_port && client_port->connect_event) + signal_sync( client_port->connect_event ); + + if (port->client_thread) + { + release_object( port->client_thread ); + port->client_thread = NULL; + } + + release_object( port ); +} + +/* Get connection status for client port */ +DECL_HANDLER(get_lpc_connect_status) +{ + struct lpc_port *port; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + if (port->port_type != PORT_TYPE_CLIENT) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + reply->status = port->connect_status; + release_object( port ); +} diff --git a/server/protocol.def b/server/protocol.def index a26e92f7dd3..8116f197fe2 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4286,3 +4286,55 @@ enum inproc_sync_type @REPLY obj_handle_t handle; /* handle to the port */ @END + + +/* Connect to an LPC port */ +@REQ(connect_lpc_port) + unsigned int access; /* desired access rights */ + data_size_t info_size; /* size of connection info */ + VARARG(objattr,object_attributes); /* object attributes */ + VARARG(info,bytes); /* connection info data */ +@REPLY + obj_handle_t handle; /* handle to client port */ + data_size_t info_size; /* size of returned info */ + VARARG(info,bytes); /* returned connection info */ +@END + + +/* Listen for connection requests */ +@REQ(listen_lpc_port) + obj_handle_t handle; /* handle to server port */ +@REPLY + client_ptr_t message; /* message pointer (unused) */ + data_size_t msg_size; /* size of connection info */ + process_id_t client_pid; /* client process ID */ + thread_id_t client_tid; /* client thread ID */ + unsigned int msg_id; /* message ID for accept */ + VARARG(info,bytes); /* connection info data */ +@END + + +/* Accept or reject a connection */ +@REQ(accept_lpc_connect) + obj_handle_t handle; /* handle (unused, connection found by msg_id) */ + int accept; /* accept or reject */ + unsigned int msg_id; /* message ID from listen */ + client_ptr_t context; /* port context */ + VARARG(info,bytes); /* connection info to return */ +@REPLY + obj_handle_t handle; /* handle to communication port */ +@END + + +/* Complete the connection */ +@REQ(complete_lpc_connect) + obj_handle_t handle; /* handle to communication port */ +@END + + +/* Get connection status for client port */ +@REQ(get_lpc_connect_status) + obj_handle_t handle; /* handle to client port */ +@REPLY + unsigned int status; /* connection status */ +@END -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611
From: Rose Hellsing <rose@pinkro.se> --- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/unix/sync.c | 194 +++++++++++++++++++-- dlls/wow64/sync.c | 12 ++ server/lpc_port.c | 335 +++++++++++++++++++++++++++++++++++- server/protocol.def | 35 ++++ server/thread.c | 5 + server/thread.h | 1 + 8 files changed, 564 insertions(+), 23 deletions(-) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 5aa620bd60b..0ee1b0303a0 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -370,7 +370,7 @@ @ stdcall -syscall=0x000b NtReplyWaitReceivePort(ptr ptr ptr ptr) @ stdcall -syscall=0x002b NtReplyWaitReceivePortEx(long ptr ptr ptr ptr) # @ stub NtReplyWaitReplyPort -# @ stub NtRequestPort +@ stdcall -syscall NtRequestPort(ptr ptr) @ stdcall -syscall=0x0022 NtRequestWaitReplyPort(ptr ptr ptr) @ stdcall -syscall NtResetEvent(long ptr) @ stdcall -syscall NtResetWriteWatch(long ptr long) @@ -1442,7 +1442,7 @@ @ stdcall -private ZwReplyWaitReceivePort(ptr ptr ptr ptr) NtReplyWaitReceivePort @ stdcall -private ZwReplyWaitReceivePortEx(long ptr ptr ptr ptr) NtReplyWaitReceivePortEx # @ stub ZwReplyWaitReplyPort -# @ stub ZwRequestPort +@ stdcall -private ZwRequestPort(ptr ptr) NtRequestPort @ stdcall -private ZwRequestWaitReplyPort(ptr ptr ptr) NtRequestWaitReplyPort @ stdcall -private ZwResetEvent(long ptr) NtResetEvent @ stdcall -private ZwResetWriteWatch(long ptr long) NtResetWriteWatch diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 517f7690bf1..dd139721966 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -532,6 +532,7 @@ DEFINE_SYSCALL(NtReplaceKey, (OBJECT_ATTRIBUTES *attr, HANDLE key, OBJECT_ATTRIB DEFINE_SYSCALL(NtReplyPort, (HANDLE handle, LPC_MESSAGE *reply)) DEFINE_SYSCALL(NtReplyWaitReceivePort, (HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg)) DEFINE_SYSCALL(NtReplyWaitReceivePortEx, (HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg, LARGE_INTEGER *timeout)) +DEFINE_SYSCALL(NtRequestPort, (HANDLE handle, LPC_MESSAGE *msg)) DEFINE_SYSCALL(NtRequestWaitReplyPort, (HANDLE handle, LPC_MESSAGE *msg_in, LPC_MESSAGE *msg_out)) DEFINE_SYSCALL(NtResetEvent, (HANDLE handle, LONG *prev_state)) DEFINE_SYSCALL(NtResetWriteWatch, (HANDLE process, PVOID base, SIZE_T size)) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 3179698202e..48a564545d9 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -3352,8 +3352,45 @@ NTSTATUS WINAPI NtReadRequestData( HANDLE handle, LPC_MESSAGE *request, ULONG id */ NTSTATUS WINAPI NtRegisterThreadTerminatePort( HANDLE handle ) { - FIXME( "(%p),stub!\n", handle ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p)\n", handle ); + + SERVER_START_REQ( register_lpc_terminate_port ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + return ret; +} + + +/*********************************************************************** + * NtRequestPort (NTDLL.@) + */ +NTSTATUS WINAPI NtRequestPort( HANDLE handle, LPC_MESSAGE *msg ) +{ + unsigned int ret; + USHORT data_size; + + TRACE( "(%p,%p)\n", handle, msg ); + + if (!msg) + return STATUS_INVALID_PARAMETER; + + data_size = msg->DataSize; + + SERVER_START_REQ( request_lpc_reply ) + { + req->handle = wine_server_obj_handle( handle ); + req->data_size = data_size; + req->msg_type = 3; /* datagram */ + wine_server_add_data( req, msg->Data, data_size ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + return ret; } @@ -3362,44 +3399,165 @@ NTSTATUS WINAPI NtRegisterThreadTerminatePort( HANDLE handle ) */ NTSTATUS WINAPI NtRequestWaitReplyPort( HANDLE handle, LPC_MESSAGE *msg_in, LPC_MESSAGE *msg_out ) { - FIXME( "(%p,%p,%p),stub!\n", handle, msg_in, msg_out ); - if (msg_in) - TRACE("datasize %u msgsize %u type %u ranges %u client %p/%p msgid %lu size %lu data %s\n", - msg_in->DataSize, msg_in->MessageSize, msg_in->MessageType, msg_in->VirtualRangesOffset, - msg_in->ClientId.UniqueProcess, msg_in->ClientId.UniqueThread, msg_in->MessageId, - msg_in->SectionSize, debugstr_an( (const char *)msg_in->Data, msg_in->DataSize )); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + USHORT data_size; + + TRACE( "(%p,%p,%p)\n", handle, msg_in, msg_out ); + + if (!msg_in || !msg_out) return STATUS_INVALID_PARAMETER; + + data_size = msg_in->DataSize; + + /* Send the request message */ + SERVER_START_REQ( request_lpc_reply ) + { + req->handle = wine_server_obj_handle( handle ); + req->data_size = data_size; + req->msg_type = 1; /* request */ + wine_server_add_data( req, msg_in->Data, data_size ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + if (ret) return ret; + + /* Wait for and receive the reply */ + for (;;) + { + SERVER_START_REQ( reply_wait_receive_lpc ) + { + req->handle = wine_server_obj_handle( handle ); + req->reply_msg_id = 0; + req->reply_size = 0; + req->timeout = TIMEOUT_INFINITE; + /* Use a reasonable max size for LPC message data. + * sizeof(msg_out->Data) is just 1 due to ANYSIZE_ARRAY. */ + wine_server_set_reply( req, msg_out->Data, 0x1000 ); + ret = wine_server_call( req ); + if (!ret) + { + msg_out->DataSize = reply->data_size; + msg_out->MessageSize = sizeof(*msg_out) + reply->data_size; + msg_out->MessageType = reply->msg_type; + msg_out->VirtualRangesOffset = 0; + msg_out->ClientId.UniqueProcess = ULongToHandle( reply->client_pid ); + msg_out->ClientId.UniqueThread = ULongToHandle( reply->client_tid ); + msg_out->MessageId = reply->msg_id; + msg_out->SectionSize = 0; + } + } + SERVER_END_REQ; + + if (ret != STATUS_PENDING) break; + + /* Wait on port for message availability */ + ret = NtWaitForSingleObject( handle, FALSE, NULL ); + if (ret) break; + } + return ret; } /*********************************************************************** * NtReplyPort (NTDLL.@) */ -NTSTATUS WINAPI NtReplyPort( HANDLE handle, LPC_MESSAGE *reply ) +NTSTATUS WINAPI NtReplyPort( HANDLE handle, LPC_MESSAGE *reply_msg ) { - FIXME("(%p,%p),stub!\n", handle, reply ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p,%p)\n", handle, reply_msg ); + + if (!reply_msg) + return STATUS_INVALID_PARAMETER; + + SERVER_START_REQ( reply_wait_receive_lpc ) + { + req->handle = wine_server_obj_handle( handle ); + req->reply_msg_id = reply_msg->MessageId; + req->reply_size = reply_msg->DataSize; + req->timeout = 0; /* Don't wait for a new message */ + wine_server_add_data( req, reply_msg->Data, reply_msg->DataSize ); + ret = wine_server_call( req ); + /* STATUS_PENDING just means no new message, which is fine for NtReplyPort */ + if (ret == STATUS_PENDING) + ret = STATUS_SUCCESS; + } + SERVER_END_REQ; + return ret; } /*********************************************************************** * NtReplyWaitReceivePort (NTDLL.@) */ -NTSTATUS WINAPI NtReplyWaitReceivePort( HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg ) +NTSTATUS WINAPI NtReplyWaitReceivePort( HANDLE handle, ULONG *id, LPC_MESSAGE *reply_msg, LPC_MESSAGE *msg ) { - FIXME("(%p,%p,%p,%p),stub!\n", handle, id, reply, msg ); - return STATUS_NOT_IMPLEMENTED; + return NtReplyWaitReceivePortEx( handle, id, reply_msg, msg, NULL ); } /*********************************************************************** * NtReplyWaitReceivePortEx (NTDLL.@) */ -NTSTATUS WINAPI NtReplyWaitReceivePortEx( HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg, +NTSTATUS WINAPI NtReplyWaitReceivePortEx( HANDLE handle, ULONG *id, LPC_MESSAGE *reply_msg, LPC_MESSAGE *msg, LARGE_INTEGER *timeout ) { - FIXME("(%p,%p,%p,%p,%p),stub!\n", handle, id, reply, msg, timeout ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; + unsigned int reply_msg_id = 0; + USHORT reply_size = 0; + + TRACE( "(%p,%p,%p,%p,%p)\n", handle, id, reply_msg, msg, timeout ); + + if (reply_msg) + { + reply_msg_id = reply_msg->MessageId; + reply_size = reply_msg->DataSize; + } + + for (;;) + { + SERVER_START_REQ( reply_wait_receive_lpc ) + { + req->handle = wine_server_obj_handle( handle ); + req->reply_msg_id = reply_msg_id; + req->reply_size = reply_size; + req->timeout = abs_timeout; + if (reply_msg && reply_size) + wine_server_add_data( req, reply_msg->Data, reply_size ); + if (msg) + { + /* Use a reasonable max size for LPC message data. + * sizeof(msg->Data) is just 1 due to ANYSIZE_ARRAY. */ + wine_server_set_reply( req, msg->Data, 0x1000 ); + } + ret = wine_server_call( req ); + if (!ret && msg) + { + msg->DataSize = reply->data_size; + msg->MessageSize = sizeof(*msg) + reply->data_size; + msg->MessageType = reply->msg_type; + msg->VirtualRangesOffset = 0; + msg->ClientId.UniqueProcess = ULongToHandle( reply->client_pid ); + msg->ClientId.UniqueThread = ULongToHandle( reply->client_tid ); + msg->MessageId = reply->msg_id; + msg->SectionSize = 0; + if (id) *id = (ULONG)(ULONG_PTR)reply->context; + } + } + SERVER_END_REQ; + + /* After first iteration, don't send reply again */ + reply_msg_id = 0; + reply_size = 0; + + if (ret != STATUS_PENDING) break; + + /* Wait on port for message availability */ + ret = NtWaitForSingleObject( handle, FALSE, timeout ); + if (ret) break; + } + return ret; } diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index dd2ff72e9ed..b083d63253e 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1352,6 +1352,18 @@ NTSTATUS WINAPI wow64_NtReplyWaitReceivePortEx( UINT *args ) } +/********************************************************************** + * wow64_NtRequestPort + */ +NTSTATUS WINAPI wow64_NtRequestPort( UINT *args ) +{ + HANDLE handle = get_handle( &args ); + LPC_MESSAGE *msg = get_ptr( &args ); + + return NtRequestPort( handle, msg ); +} + + /********************************************************************** * wow64_NtRequestWaitReplyPort */ diff --git a/server/lpc_port.c b/server/lpc_port.c index cc13de09da7..ffb39e21cbf 100644 --- a/server/lpc_port.c +++ b/server/lpc_port.c @@ -51,6 +51,10 @@ #define PORT_FLAG_WAITABLE 0x0001 /* LPC message types */ +#define LPC_REQUEST 1 +#define LPC_REPLY 2 +#define LPC_DATAGRAM 3 +#define LPC_CLIENT_DIED 6 #define LPC_CONNECTION_REQUEST 10 /* Maximum message size */ @@ -63,6 +67,24 @@ static unsigned int global_msg_id_counter = 0; /* Global list of pending connection requests */ static struct list global_pending_connects = LIST_INIT(global_pending_connects); +/* Global list of pending requests awaiting replies */ +static struct list global_pending_requests = LIST_INIT(global_pending_requests); + +/* Pending request entry */ +struct pending_request +{ + struct list entry; /* entry in global_pending_requests */ + unsigned int msg_id; /* message ID waiting for reply */ + struct lpc_port *client_port; /* client port to deliver reply to */ +}; + +/* Entry in thread's list of ports to notify on termination */ +struct lpc_terminate_port_entry +{ + struct list entry; /* entry in thread's lpc_terminate_ports list */ + struct lpc_port *port; /* the LPC port (client port) */ +}; + static const WCHAR lpc_port_name[] = {'L','P','C',' ','P','o','r','t'}; struct type_descr lpc_port_type = @@ -89,6 +111,7 @@ struct lpc_message unsigned int msg_type; /* LPC message type */ process_id_t client_pid; /* sender's process ID */ thread_id_t client_tid; /* sender's thread ID */ + client_ptr_t port_context; /* port context for this message */ data_size_t data_size; /* size of message data */ char data[1]; /* variable-length message data */ }; @@ -101,12 +124,14 @@ struct lpc_port unsigned int flags; /* PORT_FLAG_* */ struct lpc_port *connection_port; /* reference to connection port */ struct lpc_port *connected_port; /* paired port: client <-> channel */ + struct list msg_queue; /* list of pending messages */ struct list pending_connects;/* list of pending connection messages */ struct object *queue_event; /* event signaled when message arrives */ unsigned int max_msg_len; /* maximum message length */ unsigned int max_connect_info;/* maximum connection info length */ struct object *wait_event; /* event for WaitForSingleObject (waitable ports) */ struct process *server_process; /* server process (for named ports) */ + client_ptr_t port_context; /* user-defined port context */ struct thread *client_thread; /* client thread (for NtCompleteConnectPort) */ struct object *connect_event; /* event signaled when connection completes */ unsigned int connect_status; /* STATUS_SUCCESS or error code from accept */ @@ -196,7 +221,7 @@ static void signal_port_queue( struct lpc_port *port ) /* Reset the port's queue event after receiving a message */ static void reset_port_queue( struct lpc_port *port ) { - if (list_empty( &port->pending_connects )) + if (list_empty( &port->msg_queue ) && list_empty( &port->pending_connects )) { if (port->queue_event) reset_sync( port->queue_event ); @@ -205,6 +230,36 @@ static void reset_port_queue( struct lpc_port *port ) } } +/* Track a pending request awaiting reply */ +static void track_pending_request( unsigned int msg_id, struct lpc_port *client_port ) +{ + struct pending_request *pr = mem_alloc( sizeof(*pr) ); + if (pr) + { + pr->msg_id = msg_id; + pr->client_port = (struct lpc_port *)grab_object( client_port ); + list_add_tail( &global_pending_requests, &pr->entry ); + } +} + +/* Find and remove a pending request by message ID */ +static struct lpc_port *find_pending_request_client( unsigned int msg_id ) +{ + struct pending_request *pr; + + LIST_FOR_EACH_ENTRY( pr, &global_pending_requests, struct pending_request, entry ) + { + if (pr->msg_id == msg_id) + { + struct lpc_port *client = pr->client_port; + list_remove( &pr->entry ); + free( pr ); + return client; + } + } + return NULL; +} + static struct lpc_port *create_lpc_port( struct object *root, const struct unicode_str *name, unsigned int attr, unsigned int flags, unsigned int max_msg_len, unsigned int max_connect_info, @@ -225,12 +280,14 @@ static struct lpc_port *create_lpc_port( struct object *root, const struct unico port->flags = flags; port->connection_port = (struct lpc_port *)grab_object( port ); port->connected_port = NULL; + list_init( &port->msg_queue ); list_init( &port->pending_connects ); port->queue_event = create_internal_sync( 0, 0 ); port->max_msg_len = max_msg_len ? max_msg_len : MAX_LPC_MESSAGE_SIZE; port->max_connect_info = max_connect_info ? max_connect_info : 256; port->wait_event = NULL; port->server_process = (struct process *)grab_object( current->process ); + port->port_context = 0; port->client_thread = NULL; port->connect_event = NULL; port->connect_status = STATUS_PENDING; @@ -267,12 +324,14 @@ static struct lpc_port *create_port_internal( unsigned int port_type, struct lpc port->flags = 0; port->connection_port = (struct lpc_port *)grab_object( connection_port ); port->connected_port = NULL; + list_init( &port->msg_queue ); list_init( &port->pending_connects ); port->queue_event = create_internal_sync( 0, 0 ); port->max_msg_len = connection_port->max_msg_len; port->max_connect_info = connection_port->max_connect_info; port->wait_event = NULL; port->server_process = NULL; + port->port_context = 0; port->client_thread = NULL; port->connect_event = NULL; port->connect_status = STATUS_PENDING; @@ -325,6 +384,10 @@ static struct object *lpc_port_get_sync( struct object *obj ) struct lpc_port *port = (struct lpc_port *)obj; assert( obj->ops == &lpc_port_ops ); + /* For clients with a connect_event, always return it so wait entries + * added to connect_event's queue are properly checked when signaled. + * This is needed because accept_lpc_connect sets connect_status before + * complete_lpc_connect signals the event. */ if (port->port_type == PORT_TYPE_CLIENT && port->connect_event) return grab_object( port->connect_event ); @@ -344,6 +407,12 @@ static void lpc_port_destroy( struct object *obj ) assert( obj->ops == &lpc_port_ops ); + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->msg_queue, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->pending_connects, struct lpc_message, entry ) { list_remove( &msg->entry ); @@ -531,6 +600,7 @@ DECL_HANDLER(accept_lpc_connect) comm_port->connected_port = (struct lpc_port *)grab_object( client_port ); client_port->connected_port = (struct lpc_port *)grab_object( comm_port ); client_port->connect_status = STATUS_SUCCESS; + comm_port->port_context = req->context; if (msg->sender_thread) comm_port->client_thread = (struct thread *)grab_object( msg->sender_thread ); @@ -568,8 +638,18 @@ DECL_HANDLER(complete_lpc_connect) } client_port = port->connected_port; - if (client_port && client_port->connect_event) - signal_sync( client_port->connect_event ); + if (client_port) + { + /* Signal the connect_event to wake the client. The client is waiting + * on this event during NtConnectPort. After signaling, release the + * event so future waits will use queue_event instead. */ + if (client_port->connect_event) + { + signal_sync( client_port->connect_event ); + release_object( client_port->connect_event ); + client_port->connect_event = NULL; + } + } if (port->client_thread) { @@ -599,3 +679,252 @@ DECL_HANDLER(get_lpc_connect_status) reply->status = port->connect_status; release_object( port ); } + +/* Send a request message */ +DECL_HANDLER(request_lpc_reply) +{ + struct lpc_port *port; + struct lpc_port *target_port; + struct lpc_message *msg; + data_size_t data_size = get_req_data_size(); + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + if (port->port_type == PORT_TYPE_CLIENT) + { + if (!port->connected_port || !port->connected_port->connection_port) + { + set_error( STATUS_PORT_DISCONNECTED ); + release_object( port ); + return; + } + target_port = port->connected_port->connection_port; + } + else if (port->port_type == PORT_TYPE_CHANNEL) + { + if (!port->connected_port) + { + set_error( STATUS_PORT_DISCONNECTED ); + release_object( port ); + return; + } + target_port = port->connected_port; + } + else + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + msg = alloc_lpc_message( data_size ); + if (!msg) + { + release_object( port ); + return; + } + + msg->sender_port = (struct lpc_port *)grab_object( port ); + msg->sender_thread = (struct thread *)grab_object( current ); + msg->msg_id = get_next_msg_id(); + msg->msg_type = req->msg_type ? req->msg_type : LPC_REQUEST; + msg->client_pid = current->process->id; + msg->client_tid = current->id; + msg->port_context = port->port_context; + if (data_size) + memcpy( msg->data, get_req_data(), data_size ); + + if (msg->msg_type == LPC_REQUEST && port->port_type == PORT_TYPE_CLIENT) + track_pending_request( msg->msg_id, port ); + + list_add_tail( &target_port->msg_queue, &msg->entry ); + signal_port_queue( target_port ); + + reply->msg_id = msg->msg_id; + + release_object( port ); +} + +/* Reply to a message and wait for next one */ +DECL_HANDLER(reply_wait_receive_lpc) +{ + struct lpc_port *port; + struct lpc_port *receive_port; + struct lpc_message *msg; + data_size_t reply_size = get_req_data_size(); + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + /* Handle reply to previous message */ + if (req->reply_msg_id) + { + struct lpc_port *client_port; + struct lpc_message *reply_msg; + + client_port = find_pending_request_client( req->reply_msg_id ); + if (client_port) + { + reply_msg = alloc_lpc_message( reply_size ); + if (reply_msg) + { + reply_msg->msg_type = LPC_REPLY; + reply_msg->msg_id = req->reply_msg_id; + reply_msg->client_pid = current->process->id; + reply_msg->client_tid = current->id; + reply_msg->data_size = reply_size; + if (reply_size) + memcpy( reply_msg->data, get_req_data(), reply_size ); + + list_add_tail( &client_port->msg_queue, &reply_msg->entry ); + signal_port_queue( client_port ); + } + release_object( client_port ); + } + } + + if (port->port_type == PORT_TYPE_CHANNEL || port->port_type == PORT_TYPE_CLIENT) + receive_port = port; + else if (port->port_type == PORT_TYPE_SERVER) + receive_port = port; + else + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + if (list_empty( &receive_port->msg_queue )) + { + if (port->port_type == PORT_TYPE_SERVER && !list_empty( &port->pending_connects )) + { + msg = LIST_ENTRY( list_head( &port->pending_connects ), struct lpc_message, entry ); + list_remove( &msg->entry ); + + reply->msg_id = msg->msg_id; + reply->msg_type = msg->msg_type; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->context = msg->port_context; + reply->data_size = msg->data_size; + + if (msg->data_size) + set_reply_data( msg->data, min( msg->data_size, get_reply_max_size() ) ); + + reset_port_queue( receive_port ); + release_object( port ); + return; + } + else + { + set_error( STATUS_PENDING ); + release_object( port ); + return; + } + } + else + { + msg = LIST_ENTRY( list_head( &receive_port->msg_queue ), struct lpc_message, entry ); + list_remove( &msg->entry ); + } + + reply->msg_id = msg->msg_id; + reply->msg_type = msg->msg_type; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->context = msg->port_context; + reply->data_size = msg->data_size; + + if (msg->data_size) + set_reply_data( msg->data, min( msg->data_size, get_reply_max_size() ) ); + + free_lpc_message( msg ); + reset_port_queue( receive_port ); + release_object( port ); +} + +/* Register a port for thread termination notification */ +DECL_HANDLER(register_lpc_terminate_port) +{ + struct lpc_port *port; + struct lpc_terminate_port_entry *entry; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + 0, &lpc_port_ops ); + if (!port) return; + + /* Only client ports should be registered for termination */ + if (port->port_type != PORT_TYPE_CLIENT) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + /* Check if already registered */ + LIST_FOR_EACH_ENTRY( entry, ¤t->lpc_terminate_ports, struct lpc_terminate_port_entry, entry ) + { + if (entry->port == port) + { + release_object( port ); + return; + } + } + + /* Add to thread's terminate port list */ + entry = mem_alloc( sizeof(*entry) ); + if (entry) + { + entry->port = port; /* transfer the reference */ + list_add_tail( ¤t->lpc_terminate_ports, &entry->entry ); + } + else + { + release_object( port ); + } +} + +/* Send LPC_CLIENT_DIED messages to all registered ports for a thread. + * Called from cleanup_thread in thread.c */ +void lpc_send_client_died( struct thread *thread ) +{ + struct lpc_terminate_port_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE( entry, next, &thread->lpc_terminate_ports, + struct lpc_terminate_port_entry, entry ) + { + struct lpc_port *client_port = entry->port; + + /* Send LPC_CLIENT_DIED to the server port via the communication channel */ + if (client_port && client_port->connected_port) + { + struct lpc_port *comm_port = client_port->connected_port; + struct lpc_port *server_port = comm_port->connection_port; + + if (server_port && server_port->port_type == PORT_TYPE_SERVER) + { + struct lpc_message *died_msg = alloc_lpc_message( 0 ); + if (died_msg) + { + died_msg->msg_id = get_next_msg_id(); + died_msg->msg_type = LPC_CLIENT_DIED; + died_msg->client_pid = thread->process->id; + died_msg->client_tid = thread->id; + died_msg->port_context = comm_port->port_context; + + /* Queue on server port */ + list_add_tail( &server_port->msg_queue, &died_msg->entry ); + signal_port_queue( server_port ); + } + } + } + + /* Clean up the entry */ + list_remove( &entry->entry ); + release_object( client_port ); + free( entry ); + } +} diff --git a/server/protocol.def b/server/protocol.def index 8116f197fe2..f8069d26e72 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4338,3 +4338,38 @@ enum inproc_sync_type @REPLY unsigned int status; /* connection status */ @END + + +/* Send request and wait for reply */ +@REQ(request_lpc_reply) + obj_handle_t handle; + data_size_t data_size; + unsigned int msg_type; + VARARG(data,bytes); +@REPLY + unsigned int msg_id; +@END + + +/* Wait for message and reply to previous */ +@REQ(reply_wait_receive_lpc) + obj_handle_t handle; + unsigned int reply_msg_id; + data_size_t reply_size; + timeout_t timeout; + VARARG(reply,bytes); +@REPLY + unsigned int msg_id; + unsigned int msg_type; + process_id_t client_pid; + thread_id_t client_tid; + client_ptr_t context; + data_size_t data_size; + VARARG(data,bytes); +@END + + +/* Register port for thread termination notification */ +@REQ(register_lpc_terminate_port) + obj_handle_t handle; +@END diff --git a/server/thread.c b/server/thread.c index 7207f918400..e5280ac4315 100644 --- a/server/thread.c +++ b/server/thread.c @@ -60,6 +60,9 @@ #include "user.h" #include "security.h" +/* LPC support */ +extern void lpc_send_client_died( struct thread *thread ); + /* thread queues */ @@ -437,6 +440,7 @@ static inline void init_thread_structure( struct thread *thread ) list_init( &thread->system_apc ); list_init( &thread->user_apc ); list_init( &thread->kernel_object ); + list_init( &thread->lpc_terminate_ports ); for (i = 0; i < MAX_INFLIGHT_FDS; i++) thread->inflight[i].server = thread->inflight[i].client = -1; @@ -609,6 +613,7 @@ static void cleanup_thread( struct thread *thread ) { int i; + lpc_send_client_died( thread ); cleanup_thread_completion( thread ); if (thread->context) { diff --git a/server/thread.h b/server/thread.h index 77ea355483d..ca23822f572 100644 --- a/server/thread.h +++ b/server/thread.h @@ -99,6 +99,7 @@ struct thread data_size_t desc_len; /* thread description length in bytes */ WCHAR *desc; /* thread description string */ struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */ + struct list lpc_terminate_ports; /* list of LPC ports to notify on thread termination */ }; extern struct thread *current; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611
From: Rose Hellsing <rose@pinkro.se> On native Windows WoW64, 32-bit applications seemingly use the 64-bit LPC_MESSAGE structure layout directly, so the wrappers just pass through the pointers. Also fix NtReplyWaitReceivePortEx to use msg->DataSize as the receive buffer size instead of a hardcoded value. --- dlls/ntdll/tests/port.c | 7 ++----- dlls/ntdll/unix/sync.c | 2 +- dlls/wow64/sync.c | 34 +++++++++++++++++++--------------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/dlls/ntdll/tests/port.c b/dlls/ntdll/tests/port.c index 80c60d09683..302e766da4e 100644 --- a/dlls/ntdll/tests/port.c +++ b/dlls/ntdll/tests/port.c @@ -239,7 +239,7 @@ static DWORD WINAPI test_ports_client(LPVOID arg) sqos.EffectiveOnly = TRUE; status = pNtConnectPort(&PortHandle, &port, &sqos, 0, 0, &len, NULL, NULL); - todo_wine ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); if (status != STATUS_SUCCESS) return 1; status = pNtRegisterThreadTerminatePort(PortHandle); @@ -322,10 +322,7 @@ static void test_ports_server( HANDLE PortHandle ) while (TRUE) { status = pNtReplyWaitReceivePort(PortHandle, NULL, NULL, &LpcMessage->msg); - todo_wine - { - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld(%lx)\n", status, status); - } + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld(%lx)\n", status, status); /* STATUS_INVALID_HANDLE: win2k without admin rights will perform an * endless loop here */ diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 48a564545d9..48d7bc6041a 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -3529,7 +3529,7 @@ NTSTATUS WINAPI NtReplyWaitReceivePortEx( HANDLE handle, ULONG *id, LPC_MESSAGE { /* Use a reasonable max size for LPC message data. * sizeof(msg->Data) is just 1 due to ANYSIZE_ARRAY. */ - wine_server_set_reply( req, msg->Data, 0x1000 ); + wine_server_set_reply( req, msg->Data, msg->DataSize ); } ret = wine_server_call( req ); if (!ret && msg) diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index b083d63253e..a0633cfccae 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -140,8 +140,12 @@ NTSTATUS WINAPI wow64_NtAcceptConnectPort( UINT *args ) LPC_SECTION_WRITE *write = get_ptr( &args ); LPC_SECTION_READ *read = get_ptr( &args ); - FIXME( "%p %lu %p %u %p %p: stub\n", handle_ptr, id, msg, accept, write, read ); - return STATUS_NOT_IMPLEMENTED; + HANDLE handle = 0; + NTSTATUS status; + + status = NtAcceptConnectPort( &handle, id, msg, accept, write, read ); + put_handle( handle_ptr, handle ); + return status; } @@ -205,9 +209,14 @@ NTSTATUS WINAPI wow64_NtConnectPort( UINT *args ) void *info = get_ptr( &args ); ULONG *info_len = get_ptr( &args ); - FIXME( "%p %p %p %p %p %p %p %p: stub\n", - handle_ptr, name32, qos, write, read, max_len, info, info_len ); - return STATUS_NOT_IMPLEMENTED; + UNICODE_STRING name; + HANDLE handle = 0; + NTSTATUS status; + + status = NtConnectPort( &handle, unicode_str_32to64( &name, name32 ), + qos, write, read, max_len, info, info_len ); + put_handle( handle_ptr, handle ); + return status; } @@ -561,8 +570,7 @@ NTSTATUS WINAPI wow64_NtListenPort( UINT *args ) HANDLE handle = get_handle( &args ); LPC_MESSAGE *msg = get_ptr( &args ); - FIXME( "%p %p: stub\n", handle, msg ); - return STATUS_NOT_IMPLEMENTED; + return NtListenPort( handle, msg ); } @@ -1316,8 +1324,7 @@ NTSTATUS WINAPI wow64_NtReplyPort( UINT *args ) HANDLE handle = get_handle( &args ); LPC_MESSAGE *reply = get_ptr( &args ); - FIXME( "%p %p: stub\n", handle, reply ); - return STATUS_NOT_IMPLEMENTED; + return NtReplyPort( handle, reply ); } @@ -1331,8 +1338,7 @@ NTSTATUS WINAPI wow64_NtReplyWaitReceivePort( UINT *args ) LPC_MESSAGE *reply = get_ptr( &args ); LPC_MESSAGE *msg = get_ptr( &args ); - FIXME( "%p %p %p %p: stub\n", handle, id, reply, msg ); - return STATUS_NOT_IMPLEMENTED; + return NtReplyWaitReceivePort( handle, id, reply, msg ); } @@ -1347,8 +1353,7 @@ NTSTATUS WINAPI wow64_NtReplyWaitReceivePortEx( UINT *args ) LPC_MESSAGE *msg = get_ptr( &args ); LARGE_INTEGER *timeout = get_ptr( &args ); - FIXME( "%p %p %p %p %p: stub\n", handle, id, reply, msg, timeout ); - return STATUS_NOT_IMPLEMENTED; + return NtReplyWaitReceivePortEx( handle, id, reply, msg, timeout ); } @@ -1373,8 +1378,7 @@ NTSTATUS WINAPI wow64_NtRequestWaitReplyPort( UINT *args ) LPC_MESSAGE *msg_in = get_ptr( &args ); LPC_MESSAGE *msg_out = get_ptr( &args ); - FIXME( "%p %p %p: stub\n", handle, msg_in, msg_out ); - return STATUS_NOT_IMPLEMENTED; + return NtRequestWaitReplyPort( handle, msg_in, msg_out ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611
From: Rose Hellsing <rose@pinkro.se> --- server/lpc_port.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/server/lpc_port.c b/server/lpc_port.c index ffb39e21cbf..dbe16b996c5 100644 --- a/server/lpc_port.c +++ b/server/lpc_port.c @@ -139,6 +139,7 @@ struct lpc_port static void lpc_port_dump( struct object *obj, int verbose ); static struct object *lpc_port_get_sync( struct object *obj ); +static int lpc_port_close_handle( struct object *obj, struct process *process, obj_handle_t handle ); static void lpc_port_destroy( struct object *obj ); static const struct object_ops lpc_port_ops = @@ -162,7 +163,7 @@ static const struct object_ops lpc_port_ops = default_unlink_name, /* unlink_name */ no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ - no_close_handle, /* close_handle */ + lpc_port_close_handle, /* close_handle */ lpc_port_destroy /* destroy */ }; @@ -400,6 +401,59 @@ static struct object *lpc_port_get_sync( struct object *obj ) return NULL; } +static int lpc_port_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +{ + struct lpc_port *port = (struct lpc_port *)obj; + struct pending_request *pr, *next_pr; + + if (obj->handle_count == 1) + { + /* Clean up any pending requests that reference this port */ + LIST_FOR_EACH_ENTRY_SAFE( pr, next_pr, &global_pending_requests, struct pending_request, entry ) + { + if (pr->client_port == port) + { + list_remove( &pr->entry ); + release_object( pr->client_port ); + free( pr ); + } + } + + /* Break the bidirectional connected_port reference */ + if (port->connected_port) + { + struct lpc_port *peer = port->connected_port; + + /* Clear peer's reference to us first */ + if (peer->connected_port == port) + { + peer->connected_port = NULL; + release_object( port ); + } + + /* Clear our reference to peer */ + port->connected_port = NULL; + release_object( peer ); + } + + /* For SERVER ports, release the self-reference from connection_port */ + if (port->port_type == PORT_TYPE_SERVER && port->connection_port == port) + { + port->connection_port = NULL; + release_object( port ); + } + /* For client/channel ports, release the connection_port reference + * to allow the named server port to be destroyed */ + else if (port->port_type != PORT_TYPE_SERVER && port->connection_port) + { + release_object( port->connection_port ); + port->connection_port = NULL; + } + } + + return 1; +} + static void lpc_port_destroy( struct object *obj ) { struct lpc_port *port = (struct lpc_port *)obj; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611
Thanks, but I'm afraid the commits are still way too big. Also this will need a lot more tests. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10611#note_136707
participants (3)
-
Alexandre Julliard (@julliard) -
Rose Hellsing -
Rose Hellsing (@axtlos)