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