[PATCH v2 0/3] MR11235: ntdll: Implement NtAlpcCreatePort().
ALPC port attributes have to be put in a VARARG. Otherwise, it exceeds the max_req_size in the make_requests script. As a result, get_req_object_attributes_data() had to be introduced because there are two VARARGs in the alpc_create_port request. -- v2: ntdll: Implement NtAlpcCreatePort(). https://gitlab.winehq.org/wine/wine/-/merge_requests/11235
From: Zhiyi Zhang <zzhang@codeweavers.com> --- dlls/ntdll/tests/om.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index 7f16584d911..f55847ceb27 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -2199,6 +2199,10 @@ static void test_token(void) pNtClose( handle ); } +#define ALPC_PORT_GENERIC_EXECUTE 0 +#define ALPC_PORT_GENERIC_READ (STANDARD_RIGHTS_READ|0x1) +#define ALPC_PORT_GENERIC_WRITE (DELETE|0x1) +#define ALPC_PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1) #define DEBUG_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) #define DEBUG_GENERIC_READ (STANDARD_RIGHTS_READ|DEBUG_READ_EVENT) #define DEBUG_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|DEBUG_PROCESS_ASSIGN) @@ -2330,6 +2334,7 @@ static void test_object_types(void) { #define TYPE(name,gen,extra,broken) { name, { gen ## _GENERIC_READ, gen ## _GENERIC_WRITE, \ gen ## _GENERIC_EXECUTE, gen ## _ALL_ACCESS }, gen ## _ALL_ACCESS | extra, broken } + TYPE( L"ALPC Port", ALPC_PORT, 0, 0 ), TYPE( L"DebugObject", DEBUG, 0, 0 ), TYPE( L"Desktop", DESKTOP, 0, 0 ), TYPE( L"Device", FILE, 0, 0 ), @@ -2412,6 +2417,7 @@ static void test_object_types(void) break; } + todo_wine_if(!lstrcmpW( tests[i].name, L"ALPC Port" )) ok( j < ARRAY_SIZE(all_types), "type %s not found\n", debugstr_w(tests[i].name) ); } for (j = 0; j < ARRAY_SIZE(all_types); j++) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11235
From: Zhiyi Zhang <zzhang@codeweavers.com> --- dlls/ntdll/tests/alpc.c | 133 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/dlls/ntdll/tests/alpc.c b/dlls/ntdll/tests/alpc.c index f34da209041..afe00cb3619 100644 --- a/dlls/ntdll/tests/alpc.c +++ b/dlls/ntdll/tests/alpc.c @@ -29,6 +29,7 @@ DECL_FUNCPTR(AlpcGetHeaderSize) DECL_FUNCPTR(AlpcGetMessageAttribute) DECL_FUNCPTR(AlpcInitializeMessageAttribute) +DECL_FUNCPTR(NtAlpcCreatePort) #undef DECL_FUNCPTR @@ -44,10 +45,27 @@ static void init_functions(void) LOAD_FUNCPTR(AlpcGetHeaderSize) LOAD_FUNCPTR(AlpcGetMessageAttribute) LOAD_FUNCPTR(AlpcInitializeMessageAttribute) + LOAD_FUNCPTR(NtAlpcCreatePort) #undef LOAD_FUNCPTR } +static void init_port_attr(ALPC_PORT_ATTRIBUTES *attr, ULONG flags, SIZE_T max_msg_length) +{ + attr->Flags = flags; + attr->SecurityQos.Length = sizeof(attr->SecurityQos); + attr->SecurityQos.ImpersonationLevel = SecurityIdentification; + attr->SecurityQos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + attr->SecurityQos.EffectiveOnly = FALSE; + attr->MaxMessageLength = max_msg_length; + attr->MemoryBandwidth = 512; + attr->MaxPoolUsage = 0xffffffff; + attr->MaxSectionSize = 0xffffffff; + attr->MaxViewSize = 0xffffffff; + attr->MaxTotalSectionSize = 0xffffffff; + attr->DupObjectTypes = (flags & ALPC_PORTFLG_ALLOW_DUP_OBJECT) ? 0xffffffff : 0; +} + static void test_AlpcGetHeaderSize(void) { unsigned int i, j; @@ -342,6 +360,120 @@ static void test_AlpcInitializeMessageAttribute(void) } } +static void test_NtAlpcCreatePort(void) +{ + UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\test_NtAlpcCreatePort_port"); + OBJECT_NAME_INFORMATION *name_info; + OBJECT_TYPE_INFORMATION *type_info; + ALPC_PORT_ATTRIBUTES port_attr; + OBJECT_BASIC_INFORMATION info; + unsigned char buffer[1024]; + HANDLE handle, handle2; + OBJECT_ATTRIBUTES attr; + DWORD size, flags; + NTSTATUS status; + ULONG flag; + BOOL ret; + + if (!pNtAlpcCreatePort) + { + todo_wine + win_skip("NtAlpcCreatePort is unavailable.\n"); + return; + } + + InitializeObjectAttributes(&attr, &name, 0, NULL, NULL); + init_port_attr(&port_attr, 0, 1024); + + /* Check parameters */ + status = pNtAlpcCreatePort(NULL, &attr, &port_attr); + ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status); + + status = pNtAlpcCreatePort(&handle, NULL, &port_attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ret = GetHandleInformation(handle, &flags); + ok(ret, "GetHandleInformation failed, error %ld.\n", GetLastError()); + CloseHandle(handle); + + status = pNtAlpcCreatePort(&handle, &attr, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ret = GetHandleInformation(handle, &flags); + ok(ret, "GetHandleInformation failed, error %ld.\n", GetLastError()); + CloseHandle(handle); + + status = pNtAlpcCreatePort(&handle, NULL, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ret = GetHandleInformation(handle, &flags); + ok(ret, "GetHandleInformation failed, error %ld.\n", GetLastError()); + CloseHandle(handle); + + /* Normal calls */ + status = pNtAlpcCreatePort(&handle, &attr, &port_attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + /* Check handle */ + ret = GetHandleInformation(handle, &flags); + ok(ret, "GetHandleInformation failed, error %ld.\n", GetLastError()); + + /* Check object attributes and granted access */ + status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(info.Attributes == 0, "Got attributes %#lx\n", info.Attributes); + ok(info.GrantedAccess == (STANDARD_RIGHTS_ALL | 0x1), "Got access %#lx\n", info.GrantedAccess); + + /* Check object name */ + status = NtQueryObject(handle, ObjectNameInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + name_info = (OBJECT_NAME_INFORMATION *)buffer; + ok(!wcsicmp(name_info->Name.Buffer, name.Buffer), "Got unexpected name %s.\n", + debugstr_w(name_info->Name.Buffer)); + + /* Check object type */ + status = NtQueryObject(handle, ObjectTypeInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + type_info = (OBJECT_TYPE_INFORMATION *)buffer; + ok(!wcscmp(type_info->TypeName.Buffer, L"ALPC Port"), "Got unexpected type %s.\n", + debugstr_w(type_info->TypeName.Buffer)); + + /* Duplicate name */ + status = pNtAlpcCreatePort(&handle2, &attr, &port_attr); + ok(status == STATUS_OBJECT_NAME_COLLISION, "Got unexpected status %#lx.\n", status); + + CloseHandle(handle); + + /* Test handle validity after deletion */ + ret = GetHandleInformation(handle, &flags); + ok(!ret, "GetHandleInformation succeeded.\n"); + + /* Test Flags */ + for (flag = 0x1; flag != 0; flag = flag << 1) + { + winetest_push_context("%#lx", flag); + + port_attr.Flags = flag; + status = pNtAlpcCreatePort(&handle, &attr, &port_attr); + if (flag == 0x100000) + { + ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status); + winetest_pop_context(); + continue; + } + else + { + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + } + ret = GetHandleInformation(handle, &flags); + ok(ret, "GetHandleInformation failed, error %ld.\n", GetLastError()); + status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(info.Attributes == 0, "Got attributes %#lx\n", info.Attributes); + ok(info.GrantedAccess == (STANDARD_RIGHTS_ALL | 0x1), "Got access %#lx\n", info.GrantedAccess); + + CloseHandle(handle); + winetest_pop_context(); + } +} + START_TEST(alpc) { init_functions(); @@ -349,4 +481,5 @@ START_TEST(alpc) test_AlpcGetHeaderSize(); test_AlpcGetMessageAttribute(); test_AlpcInitializeMessageAttribute(); + test_NtAlpcCreatePort(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11235
From: Zhiyi Zhang <zzhang@codeweavers.com> --- dlls/ntdll/tests/alpc.c | 1 - dlls/ntdll/tests/om.c | 2 - dlls/ntdll/unix/alpc.c | 58 +++++++++++++- include/winnt.h | 3 + server/Makefile.in | 1 + server/alpc.c | 164 ++++++++++++++++++++++++++++++++++++++++ server/directory.c | 1 + server/object.h | 1 + server/protocol.def | 10 +++ 9 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 server/alpc.c diff --git a/dlls/ntdll/tests/alpc.c b/dlls/ntdll/tests/alpc.c index afe00cb3619..39b8b02526f 100644 --- a/dlls/ntdll/tests/alpc.c +++ b/dlls/ntdll/tests/alpc.c @@ -377,7 +377,6 @@ static void test_NtAlpcCreatePort(void) if (!pNtAlpcCreatePort) { - todo_wine win_skip("NtAlpcCreatePort is unavailable.\n"); return; } diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index f55847ceb27..61919d9c365 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -2202,7 +2202,6 @@ static void test_token(void) #define ALPC_PORT_GENERIC_EXECUTE 0 #define ALPC_PORT_GENERIC_READ (STANDARD_RIGHTS_READ|0x1) #define ALPC_PORT_GENERIC_WRITE (DELETE|0x1) -#define ALPC_PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1) #define DEBUG_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) #define DEBUG_GENERIC_READ (STANDARD_RIGHTS_READ|DEBUG_READ_EVENT) #define DEBUG_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|DEBUG_PROCESS_ASSIGN) @@ -2417,7 +2416,6 @@ static void test_object_types(void) break; } - todo_wine_if(!lstrcmpW( tests[i].name, L"ALPC Port" )) ok( j < ARRAY_SIZE(all_types), "type %s not found\n", debugstr_w(tests[i].name) ); } for (j = 0; j < ARRAY_SIZE(all_types); j++) diff --git a/dlls/ntdll/unix/alpc.c b/dlls/ntdll/unix/alpc.c index c708b588011..513a4b296fb 100644 --- a/dlls/ntdll/unix/alpc.c +++ b/dlls/ntdll/unix/alpc.c @@ -24,6 +24,7 @@ #include "ntstatus.h" #include "wine/debug.h" +#include "wine/server.h" #include "unix_private.h" WINE_DEFAULT_DEBUG_CHANNEL(alpc); @@ -52,10 +53,61 @@ NTSTATUS WINAPI NtAlpcConnectPort( HANDLE *port_handle, UNICODE_STRING *port_nam return STATUS_NOT_IMPLEMENTED; } -NTSTATUS WINAPI NtAlpcCreatePort( HANDLE *port_handle, OBJECT_ATTRIBUTES *obj_attr, ALPC_PORT_ATTRIBUTES *port_attr ) +NTSTATUS WINAPI NtAlpcCreatePort( HANDLE *port_handle, OBJECT_ATTRIBUTES *attr, ALPC_PORT_ATTRIBUTES *port_attr ) { - FIXME( "%p, %p, %p stub!\n", port_handle, obj_attr, port_attr ); - return STATUS_NOT_IMPLEMENTED; + struct object_attributes *objattr = NULL; + unsigned int status; + data_size_t len = 0; + + TRACE( "%p, %p, %p.\n", port_handle, attr, port_attr ); + + if (!port_handle) return STATUS_ACCESS_VIOLATION; + + *port_handle = NULL; + + if (port_attr) + TRACE( "port attributes: flags %#x qos (length %#x impersonation_level %d tracking_mode %d " + "effective only %d) max_msg_length %#lx memory_bandwidth %#lx max_pool_usage %#lx " + "max_section_size %#lx max_view_size %#lx max_total_section_size %#lx.\n", + port_attr->Flags, port_attr->SecurityQos.Length, port_attr->SecurityQos.ImpersonationLevel, + port_attr->SecurityQos.ContextTrackingMode, port_attr->SecurityQos.EffectiveOnly, + port_attr->MaxMessageLength, port_attr->MemoryBandwidth, port_attr->MaxPoolUsage, + port_attr->MaxSectionSize, port_attr->MaxViewSize, port_attr->MaxTotalSectionSize ); + + if (port_attr && port_attr->Flags & 0x100000) return STATUS_INVALID_PARAMETER; + + if (attr) + { + if (attr->ObjectName) TRACE( "name %s.\n", debugstr_us( attr->ObjectName ) ); + if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status; + } + + SERVER_START_REQ( alpc_create_port ) + { + if (port_attr) + { + req->flags = port_attr->Flags; + req->max_msg_len = port_attr->MaxMessageLength; + } + else + { + req->flags = 0; + req->max_msg_len = 65535; + } + wine_server_add_data( req, objattr, len ); + if (!(status = wine_server_call( req ))) + { + *port_handle = wine_server_ptr_handle( reply->handle ); + TRACE( "created %p.\n", *port_handle ); + } + else + { + WARN( "status %#x.\n", status ); + } + } + SERVER_END_REQ; + free( objattr ); + return status; } NTSTATUS WINAPI NtAlpcDisconnectPort( HANDLE port_handle, ULONG flags ) diff --git a/include/winnt.h b/include/winnt.h index 2dc1aadddde..c8a43b49d94 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5457,6 +5457,9 @@ typedef enum tagSID_NAME_USE { #define MAXIMUM_ALLOWED 0x02000000 #define ACCESS_SYSTEM_SECURITY 0x01000000 +#define ALPC_PORT_QUERY_STATE 0x0001 +#define ALPC_PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|ALPC_PORT_QUERY_STATE) + #define EVENT_QUERY_STATE 0x0001 #define EVENT_MODIFY_STATE 0x0002 #define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) diff --git a/server/Makefile.in b/server/Makefile.in index 84a6bd74d9d..0dd53951c4f 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -1,6 +1,7 @@ PROGRAMS = wineserver SOURCES = \ + alpc.c \ async.c \ atom.c \ change.c \ diff --git a/server/alpc.c b/server/alpc.c new file mode 100644 index 00000000000..c102254ab01 --- /dev/null +++ b/server/alpc.c @@ -0,0 +1,164 @@ +/* + * Server-side advanced local procedure call management + * + * Copyright 2026 Zhiyi Zhang for CodeWeavers + * + * 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 <sys/types.h> + +#include "windef.h" +#include "winternl.h" +#include "ntstatus.h" + +#include "handle.h" +#include "thread.h" +#include "security.h" +#include "request.h" + +static const WCHAR alpc_port_name[] = {'A','L','P','C',' ','P','o','r','t'}; + +struct type_descr alpc_port_type = +{ + { alpc_port_name, sizeof(alpc_port_name) }, /* name */ + ALPC_PORT_ALL_ACCESS, /* valid_access */ + { /* mapping */ + STANDARD_RIGHTS_READ | ALPC_PORT_QUERY_STATE, + DELETE | ALPC_PORT_QUERY_STATE, + 0, + ALPC_PORT_ALL_ACCESS + }, +}; + +enum alpc_port_enum_type +{ + CONNECTION_PORT, + COMMUNICATION_PORT +}; + +enum alpc_port_status +{ + UNINITIALIZED, + CONNECTED, + REFUSED, + DISCONNECTED +}; + +struct alpc_port +{ + struct object obj; /* object header */ + enum alpc_port_enum_type type; /* communication port or connection port */ + enum alpc_port_status status; /* port status */ + struct thread *thread; /* thread owning the port */ + unsigned int flags; /* flags in port attributes */ + mem_size_t max_msg_len; /* max message length in port attributes */ +}; + +static void alpc_port_dump( struct object *obj, int verbose ); +static void alpc_port_destroy( struct object *obj ); + +static const struct object_ops alpc_port_ops = +{ + sizeof(struct alpc_port), /* size */ + &alpc_port_type, /* type */ + alpc_port_dump, /* dump */ + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + NULL, /* signaled */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + default_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 */ + alpc_port_destroy /* destroy */ +}; + +static void alpc_port_dump( struct object *obj, int verbose ) +{ + struct alpc_port *port = (struct alpc_port *)obj; + assert( obj->ops == &alpc_port_ops ); + fprintf( stderr, "ALPC Port type=%d status=%d\n", port->type, port->status ); +} + +static void alpc_port_destroy( struct object *obj ) +{ + struct alpc_port *port = (struct alpc_port *)obj; + assert( obj->ops == &alpc_port_ops ); + release_object( port->thread ); +} + +static struct alpc_port *alpc_create_port( struct object *root, const struct unicode_str *name, + unsigned int attr, const struct security_descriptor *sd, + enum alpc_port_enum_type type, unsigned int flags, + mem_size_t max_msg_len ) +{ + struct alpc_port *port; + + port = create_named_object( root, &alpc_port_ops, name, attr, sd ); + if (port) + { + if (get_error() != STATUS_OBJECT_NAME_EXISTS) + { + port->type = type; + port->status = UNINITIALIZED; + port->thread = (struct thread *)grab_object( current ); + port->flags = flags; + port->max_msg_len = max_msg_len; + } + else + { + release_object( port ); + return NULL; + } + } + + return port; +} + +/* Create an ALPC port */ +DECL_HANDLER(alpc_create_port) +{ + struct alpc_port *alpc_port; + const struct security_descriptor *sd; + struct unicode_str name; + struct object *root; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); + + if ((alpc_port = alpc_create_port( root, &name, objattr->attributes, sd, CONNECTION_PORT, + req->flags, req->max_msg_len ))) + { + reply->handle = alloc_handle( current->process, alpc_port, ALPC_PORT_ALL_ACCESS, objattr->attributes ); + release_object( alpc_port ); + } + + if (root) release_object( root ); +} diff --git a/server/directory.c b/server/directory.c index 091ceb84c71..b4df1da154c 100644 --- a/server/directory.c +++ b/server/directory.c @@ -164,6 +164,7 @@ static struct type_descr *types[] = &key_type, &apc_reserve_type, &completion_reserve_type, + &alpc_port_type, }; static void object_type_dump( struct object *obj, int verbose ) diff --git a/server/object.h b/server/object.h index 171ca2f8904..e085d47cb8b 100644 --- a/server/object.h +++ b/server/object.h @@ -371,6 +371,7 @@ extern struct type_descr mapping_type; extern struct type_descr key_type; extern struct type_descr apc_reserve_type; extern struct type_descr completion_reserve_type; +extern struct type_descr alpc_port_type; #define KEYEDEVENT_WAIT 0x0001 #define KEYEDEVENT_WAKE 0x0002 diff --git a/server/protocol.def b/server/protocol.def index 6c2ffc85aab..f8143336fcc 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4313,3 +4313,13 @@ enum inproc_sync_type data_size_t runtime_size; /* size of client runtime data */ VARARG(runtime,bytes); /* client runtime data */ @END + + +/* Create a new ALPC object */ +@REQ(alpc_create_port) + unsigned int flags; /* flags in port attributes */ + mem_size_t max_msg_len; /* max message length in port attributes */ + VARARG(obj_attr,object_attributes); /* object attributes */ +@REPLY + obj_handle_t handle; /* handle to the ALPC port */ +@END -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11235
v2: Remove currently used port attributes from the alpc_create_port request handler. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11235#note_144375
participants (2)
-
Zhiyi Zhang -
Zhiyi Zhang (@zhiyi)