From: Rose Hellsing <rose@pinkro.se> Add the basic LPC port object type with support for creating named ports. This implements the server-side create_lpc_port request which will be used by NtCreatePort and NtCreateWaitablePort. The LPC port object supports: - Named ports in the object namespace - Waitable ports (PORT_FLAG_WAITABLE) that can be used with WaitForSingleObject - Maximum message length and connection info size limits --- dlls/ntdll/signal_arm64ec.c | 1 + server/Makefile.in | 1 + server/lpc_port.c | 213 ++++++++++++++++++++++++++++++++++++ server/protocol.def | 12 ++ 4 files changed, 227 insertions(+) create mode 100644 server/lpc_port.c 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/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..3c4ef23a4fb --- /dev/null +++ b/server/lpc_port.c @@ -0,0 +1,213 @@ +/* + * 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 <string.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 types */ +#define PORT_TYPE_SERVER 0x01 /* Named port that server listens on */ + +/* 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 port_type; /* PORT_TYPE_* */ + unsigned int flags; /* PORT_FLAG_* */ + struct list msg_queue; /* list of pending 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) */ +}; + +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->port_type = PORT_TYPE_SERVER; + port->flags = flags; + list_init( &port->msg_queue ); + 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 ); + + if (!port->queue_event) + { + release_object( port ); + return NULL; + } + + 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 type=%s flags=%04x max_msg=%u max_connect=%u\n", + port->port_type == PORT_TYPE_SERVER ? "SERVER" : "?", + 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; + + 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; + + assert( obj->ops == &lpc_port_ops ); + + if (port->queue_event) release_object( port->queue_event ); + 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..af8011611b3 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 */ + unsigned int flags; /* port flags (waitable) */ + unsigned int max_msg_len; /* maximum message length */ + unsigned int max_connect_info; /* maximum connection info length */ + VARARG(objattr,object_attributes);/* object attributes */ +@REPLY + obj_handle_t handle; /* port handle */ +@END -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611