From: Rose Hellsing <rose@pinkro.se> Add tests for NtCreatePort and NtConnectPort error paths - NULL/zero-length object attributes - NULL/empty port names - Duplicate port names - Invalid max message/connect info sizes - Non-existent port connections Also add NULL parameter validation to NtConnectPort. --- dlls/ntdll/tests/port.c | 418 ++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/unix/sync.c | 5 + 2 files changed, 423 insertions(+) diff --git a/dlls/ntdll/tests/port.c b/dlls/ntdll/tests/port.c index 302e766da4e..1e34ff88b73 100644 --- a/dlls/ntdll/tests/port.c +++ b/dlls/ntdll/tests/port.c @@ -119,6 +119,9 @@ static NTSTATUS (WINAPI *pNtAcceptConnectPort)(PHANDLE,ULONG,PLPC_MESSAGE,ULONG, static NTSTATUS (WINAPI *pNtReplyPort)(HANDLE,PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtReplyWaitReceivePort)(PHANDLE,PULONG,PLPC_MESSAGE, PLPC_MESSAGE); +static NTSTATUS (WINAPI *pNtReplyWaitReceivePortEx)(PHANDLE,PULONG,PLPC_MESSAGE, + PLPC_MESSAGE,LARGE_INTEGER*); +static NTSTATUS (WINAPI *pNtListenPort)(HANDLE,PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtCreatePort)(PHANDLE,POBJECT_ATTRIBUTES,ULONG,ULONG,ULONG); static NTSTATUS (WINAPI *pNtRequestWaitReplyPort)(HANDLE,PLPC_MESSAGE,PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtRequestPort)(HANDLE,PLPC_MESSAGE); @@ -143,6 +146,8 @@ static BOOL init_function_ptrs(void) pNtAcceptConnectPort = (void *)GetProcAddress(hntdll, "NtAcceptConnectPort"); pNtReplyPort = (void *)GetProcAddress(hntdll, "NtReplyPort"); pNtReplyWaitReceivePort = (void *)GetProcAddress(hntdll, "NtReplyWaitReceivePort"); + pNtReplyWaitReceivePortEx = (void *)GetProcAddress(hntdll, "NtReplyWaitReceivePortEx"); + pNtListenPort = (void *)GetProcAddress(hntdll, "NtListenPort"); pNtCreatePort = (void *)GetProcAddress(hntdll, "NtCreatePort"); pNtRequestWaitReplyPort = (void *)GetProcAddress(hntdll, "NtRequestWaitReplyPort"); pNtRequestPort = (void *)GetProcAddress(hntdll, "NtRequestPort"); @@ -364,6 +369,412 @@ static void test_ports_server( HANDLE PortHandle ) HeapFree(GetProcessHeap(), 0, LpcMessage); } +static void test_create_port_errors(void) +{ + OBJECT_ATTRIBUTES obj; + HANDLE port_handle; + NTSTATUS status; + UNICODE_STRING name; + static const WCHAR ERR_PORT1[] = {'\\','E','r','r','P','o','r','t','1',0}; + static const WCHAR ERR_PORT2[] = {'\\','E','r','r','P','o','r','t','2',0}; + static const WCHAR ERR_PORT3[] = {'\\','E','r','r','P','o','r','t','3',0}; + + /* Test NULL object attributes - Windows 64-bit returns SUCCESS, Wow64 returns ACCESS_VIOLATION */ + status = pNtCreatePort(&port_handle, NULL, 0, 0, 0); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER || status == STATUS_ACCESS_VIOLATION, + "Expected STATUS_SUCCESS, STATUS_INVALID_PARAMETER or STATUS_ACCESS_VIOLATION, got %08lx\n", status); + if (status == STATUS_SUCCESS) NtClose(port_handle); + + /* Test zero-length object attributes - Windows returns SUCCESS */ + memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES)); + obj.Length = 0; + status = pNtCreatePort(&port_handle, &obj, 0, 0, 0); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_HANDLE, + "Expected STATUS_SUCCESS, STATUS_INVALID_PARAMETER or STATUS_INVALID_HANDLE, got %08lx\n", status); + if (status == STATUS_SUCCESS) NtClose(port_handle); + + /* Test with valid object attributes but NULL name - Windows returns SUCCESS */ + memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES)); + obj.Length = sizeof(OBJECT_ATTRIBUTES); + obj.ObjectName = NULL; + status = pNtCreatePort(&port_handle, &obj, 0, 0, 0); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER || status == STATUS_OBJECT_NAME_INVALID, + "Expected STATUS_SUCCESS, STATUS_INVALID_PARAMETER or STATUS_OBJECT_NAME_INVALID, got %08lx\n", status); + if (status == STATUS_SUCCESS) NtClose(port_handle); + + /* Test with empty name - Windows returns SUCCESS */ + pRtlInitUnicodeString(&name, L""); + memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES)); + obj.Length = sizeof(OBJECT_ATTRIBUTES); + obj.ObjectName = &name; + status = pNtCreatePort(&port_handle, &obj, 0, 0, 0); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER || status == STATUS_OBJECT_NAME_INVALID, + "Expected STATUS_SUCCESS, STATUS_INVALID_PARAMETER or STATUS_OBJECT_NAME_INVALID, got %08lx\n", status); + if (status == STATUS_SUCCESS) NtClose(port_handle); + + /* Test with max message size too large - use unique name to avoid collision */ + pRtlInitUnicodeString(&name, ERR_PORT1); + memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES)); + obj.Length = sizeof(OBJECT_ATTRIBUTES); + obj.ObjectName = &name; + status = pNtCreatePort(&port_handle, &obj, 0x100001, 0, 0); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER || status == STATUS_SECTION_TOO_BIG || status == STATUS_OBJECT_NAME_COLLISION, + "Expected STATUS_SUCCESS, STATUS_INVALID_PARAMETER, STATUS_SECTION_TOO_BIG or STATUS_OBJECT_NAME_COLLISION, got %08lx\n", status); + if (status == STATUS_SUCCESS) NtClose(port_handle); + + /* Test with max connect info too large - use unique name */ + pRtlInitUnicodeString(&name, ERR_PORT2); + memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES)); + obj.Length = sizeof(OBJECT_ATTRIBUTES); + obj.ObjectName = &name; + status = pNtCreatePort(&port_handle, &obj, 0, 0x100001, 0); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER || status == STATUS_SECTION_TOO_BIG || status == STATUS_OBJECT_NAME_COLLISION, + "Expected STATUS_SUCCESS, STATUS_INVALID_PARAMETER, STATUS_SECTION_TOO_BIG or STATUS_OBJECT_NAME_COLLISION, got %08lx\n", status); + if (status == STATUS_SUCCESS) NtClose(port_handle); + + /* Test creating port with name that already exists */ + pRtlInitUnicodeString(&name, ERR_PORT3); + memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES)); + obj.Length = sizeof(OBJECT_ATTRIBUTES); + obj.ObjectName = &name; + status = pNtCreatePort(&port_handle, &obj, 100, 100, 0); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status == STATUS_SUCCESS) + { + NTSTATUS status2; + HANDLE port_handle2; + status2 = pNtCreatePort(&port_handle2, &obj, 100, 100, 0); + ok(status2 == STATUS_OBJECT_NAME_COLLISION, + "Expected STATUS_OBJECT_NAME_COLLISION, got %08lx\n", status2); + NtClose(port_handle); + } +} + +static void test_connect_port_errors(void) +{ + SECURITY_QUALITY_OF_SERVICE sqos; + HANDLE port_handle; + ULONG len; + NTSTATUS status; + UNICODE_STRING bad_name; + static const WCHAR BAD_NAME[] = {'\\','N','o','n','E','x','i','s','t','e','n','t','P','o','r','t',0}; + + sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + sqos.ImpersonationLevel = SecurityImpersonation; + sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + sqos.EffectiveOnly = TRUE; + + /* Test connecting to non-existent port */ + pRtlInitUnicodeString(&bad_name, BAD_NAME); + status = pNtConnectPort(&port_handle, &bad_name, &sqos, 0, 0, &len, NULL, NULL); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_INVALID_PARAMETER, + "Expected STATUS_OBJECT_NAME_NOT_FOUND or STATUS_INVALID_PARAMETER, got %08lx\n", status); + + /* NULL parameter tests crash on Wow64 due to thunking, skip them */ + if (!is_wow64) + { + /* Test with NULL port name - Windows returns STATUS_OBJECT_NAME_INVALID */ + status = pNtConnectPort(&port_handle, NULL, &sqos, 0, 0, &len, NULL, NULL); + ok(status == STATUS_INVALID_PARAMETER || status == STATUS_ACCESS_VIOLATION || status == STATUS_OBJECT_NAME_INVALID, + "Expected STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION or STATUS_OBJECT_NAME_INVALID, got %08lx\n", status); + + /* Test with NULL handle pointer - Windows returns STATUS_OBJECT_NAME_INVALID */ + status = pNtConnectPort(NULL, &port, &sqos, 0, 0, &len, NULL, NULL); + ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER || status == STATUS_OBJECT_NAME_INVALID, + "Expected STATUS_ACCESS_VIOLATION, STATUS_INVALID_PARAMETER or STATUS_OBJECT_NAME_INVALID, got %08lx\n", status); + } +} + +static DWORD WINAPI timeout_client(LPVOID arg) +{ + SECURITY_QUALITY_OF_SERVICE sqos; + HANDLE PortHandle; + ULONG len; + NTSTATUS status; + + sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + sqos.ImpersonationLevel = SecurityImpersonation; + sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + sqos.EffectiveOnly = TRUE; + + status = pNtConnectPort(&PortHandle, &port, &sqos, 0, 0, &len, NULL, NULL); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status != STATUS_SUCCESS) return 1; + + /* Just exit - server should get timeout, not hang forever */ + return 0; +} + +static void test_reply_wait_receive_timeout(HANDLE PortHandle) +{ + union lpc_message *LpcMessage; + NTSTATUS status; + LARGE_INTEGER timeout; + ULONG size; + + size = FIELD_OFFSET(LPC_MESSAGE, Data) + MAX_MESSAGE_LEN; + LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + + /* Wait with a 100ms timeout - should timeout since client just exits */ + timeout.QuadPart = -1000000LL; /* 100ms in 100ns units */ + + /* First, accept the connection */ + status = pNtReplyWaitReceivePort(PortHandle, NULL, NULL, &LpcMessage->msg); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + + if (status == STATUS_SUCCESS && + (is_wow64 ? LpcMessage->msg64.MessageType : LpcMessage->msg.MessageType) == LPC_CONNECTION_REQUEST) + { + HANDLE accept_handle; + status = pNtAcceptConnectPort(&accept_handle, 0, &LpcMessage->msg, 1, NULL, NULL); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status == STATUS_SUCCESS) + { + status = pNtCompleteConnectPort(accept_handle); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + NtClose(accept_handle); + } + } + + /* Now try to receive with timeout - client has exited, so should timeout */ + status = pNtReplyWaitReceivePortEx(PortHandle, NULL, NULL, &LpcMessage->msg, &timeout); + ok(status == STATUS_TIMEOUT || status == STATUS_SUCCESS || status == STATUS_PENDING, + "Expected STATUS_TIMEOUT, STATUS_SUCCESS or STATUS_PENDING, got %08lx\n", status); + + HeapFree(GetProcessHeap(), 0, LpcMessage); +} + +static DWORD WINAPI multi_client_thread(LPVOID arg) +{ + SECURITY_QUALITY_OF_SERVICE sqos; + HANDLE PortHandle; + ULONG len; + NTSTATUS status; + int client_id = (int)(ULONG_PTR)arg; + union lpc_message *LpcMessage, *out; + ULONG size; + char request_data[32]; + char reply_data[32]; + + sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + sqos.ImpersonationLevel = SecurityImpersonation; + sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + sqos.EffectiveOnly = TRUE; + + status = pNtConnectPort(&PortHandle, &port, &sqos, 0, 0, &len, NULL, NULL); + ok(status == STATUS_SUCCESS, "Client %d: Expected STATUS_SUCCESS, got %08lx\n", client_id, status); + if (status != STATUS_SUCCESS) return client_id; + + if (is_wow64) + { + size = FIELD_OFFSET(LPC_MESSAGE64, Data[32]); + LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + out = HeapAlloc(GetProcessHeap(), 0, size); + + snprintf(request_data, sizeof(request_data), "Client%d", client_id); + LpcMessage->msg64.DataSize = strlen(request_data) + 1; + LpcMessage->msg64.MessageSize = FIELD_OFFSET(LPC_MESSAGE64, Data[LpcMessage->msg64.DataSize]); + strcpy((LPSTR)LpcMessage->msg64.Data, request_data); + + status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg); + ok(status == STATUS_SUCCESS, "Client %d: Expected STATUS_SUCCESS, got %08lx\n", client_id, status); + if (status == STATUS_SUCCESS) + { + snprintf(reply_data, sizeof(reply_data), "Reply%d", client_id); + ok(!strcmp((LPSTR)out->msg64.Data, reply_data), + "Client %d: Expected %s, got %s\n", client_id, reply_data, out->msg64.Data); + } + + HeapFree(GetProcessHeap(), 0, out); + HeapFree(GetProcessHeap(), 0, LpcMessage); + } + else + { + size = FIELD_OFFSET(LPC_MESSAGE, Data[32]); + LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + out = HeapAlloc(GetProcessHeap(), 0, size); + + snprintf(request_data, sizeof(request_data), "Client%d", client_id); + LpcMessage->msg.DataSize = strlen(request_data) + 1; + LpcMessage->msg.MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data[LpcMessage->msg.DataSize]); + strcpy((LPSTR)LpcMessage->msg.Data, request_data); + + status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg); + ok(status == STATUS_SUCCESS, "Client %d: Expected STATUS_SUCCESS, got %08lx\n", client_id, status); + if (status == STATUS_SUCCESS) + { + snprintf(reply_data, sizeof(reply_data), "Reply%d", client_id); + ok(!strcmp((LPSTR)out->msg.Data, reply_data), + "Client %d: Expected %s, got %s\n", client_id, reply_data, out->msg.Data); + } + + HeapFree(GetProcessHeap(), 0, out); + HeapFree(GetProcessHeap(), 0, LpcMessage); + } + + NtClose(PortHandle); + return 0; +} + +static void test_multiple_clients_server(HANDLE PortHandle) +{ + HANDLE AcceptPortHandle; + union lpc_message *LpcMessage; + ULONG size; + NTSTATUS status; + int client_count = 0; + int reply_count = 0; + + size = FIELD_OFFSET(LPC_MESSAGE, Data) + MAX_MESSAGE_LEN; + LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + + while (client_count < 3 || reply_count < 3) + { + status = pNtReplyWaitReceivePort(PortHandle, NULL, NULL, &LpcMessage->msg); + if (status != STATUS_SUCCESS) break; + + if (is_wow64) + { + switch (LpcMessage->msg64.MessageType) + { + case LPC_CONNECTION_REQUEST: + status = pNtAcceptConnectPort(&AcceptPortHandle, 0, &LpcMessage->msg, 1, NULL, NULL); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status == STATUS_SUCCESS) + { + status = pNtCompleteConnectPort(AcceptPortHandle); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + } + client_count++; + break; + + case LPC_REQUEST: + { + char reply_data[32]; + int client_id; + if (sscanf((LPSTR)LpcMessage->msg64.Data, "Client%d", &client_id) == 1) + { + snprintf(reply_data, sizeof(reply_data), "Reply%d", client_id); + strcpy((LPSTR)LpcMessage->msg64.Data, reply_data); + status = pNtReplyPort(PortHandle, &LpcMessage->msg); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + reply_count++; + } + break; + } + + case LPC_CLIENT_DIED: + break; + } + } + else + { + switch (LpcMessage->msg.MessageType) + { + case LPC_CONNECTION_REQUEST: + status = pNtAcceptConnectPort(&AcceptPortHandle, 0, &LpcMessage->msg, 1, NULL, NULL); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status == STATUS_SUCCESS) + { + status = pNtCompleteConnectPort(AcceptPortHandle); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + } + client_count++; + break; + + case LPC_REQUEST: + { + char reply_data[32]; + int client_id; + if (sscanf((LPSTR)LpcMessage->msg.Data, "Client%d", &client_id) == 1) + { + snprintf(reply_data, sizeof(reply_data), "Reply%d", client_id); + strcpy((LPSTR)LpcMessage->msg.Data, reply_data); + status = pNtReplyPort(PortHandle, &LpcMessage->msg); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + reply_count++; + } + break; + } + + case LPC_CLIENT_DIED: + break; + } + } + } + + HeapFree(GetProcessHeap(), 0, LpcMessage); +} + +static DWORD WINAPI zero_length_client(LPVOID arg) +{ + SECURITY_QUALITY_OF_SERVICE sqos; + HANDLE PortHandle; + ULONG len; + NTSTATUS status; + union lpc_message *LpcMessage, *out; + ULONG size; + + sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + sqos.ImpersonationLevel = SecurityImpersonation; + sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + sqos.EffectiveOnly = TRUE; + + status = pNtConnectPort(&PortHandle, &port, &sqos, 0, 0, &len, NULL, NULL); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status != STATUS_SUCCESS) return 1; + + if (is_wow64) + { + size = FIELD_OFFSET(LPC_MESSAGE64, Data[MAX_MESSAGE_LEN]); + LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + out = HeapAlloc(GetProcessHeap(), 0, size); + + /* Send zero-length datagram */ + LpcMessage->msg64.DataSize = 0; + LpcMessage->msg64.MessageSize = FIELD_OFFSET(LPC_MESSAGE64, Data[0]); + status = pNtRequestPort(PortHandle, &LpcMessage->msg); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, + "Expected STATUS_SUCCESS or STATUS_INVALID_PARAMETER, got %08lx\n", status); + + /* Send zero-length request and wait for reply */ + LpcMessage->msg64.DataSize = 0; + LpcMessage->msg64.MessageSize = FIELD_OFFSET(LPC_MESSAGE64, Data[0]); + status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, + "Expected STATUS_SUCCESS or STATUS_INVALID_PARAMETER, got %08lx\n", status); + + HeapFree(GetProcessHeap(), 0, out); + HeapFree(GetProcessHeap(), 0, LpcMessage); + } + else + { + size = FIELD_OFFSET(LPC_MESSAGE, Data[MAX_MESSAGE_LEN]); + LpcMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + out = HeapAlloc(GetProcessHeap(), 0, size); + + /* Send zero-length datagram */ + LpcMessage->msg.DataSize = 0; + LpcMessage->msg.MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data[0]); + status = pNtRequestPort(PortHandle, &LpcMessage->msg); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, + "Expected STATUS_SUCCESS or STATUS_INVALID_PARAMETER, got %08lx\n", status); + + /* Send zero-length request and wait for reply */ + LpcMessage->msg.DataSize = 0; + LpcMessage->msg.MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data[0]); + status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg); + ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, + "Expected STATUS_SUCCESS or STATUS_INVALID_PARAMETER, got %08lx\n", status); + + HeapFree(GetProcessHeap(), 0, out); + HeapFree(GetProcessHeap(), 0, LpcMessage); + } + + NtClose(PortHandle); + return 0; +} + START_TEST(port) { OBJECT_ATTRIBUTES obj; @@ -393,5 +804,12 @@ START_TEST(port) ok( WaitForSingleObject( thread, 10000 ) == 0, "thread didn't exit\n" ); CloseHandle(thread); } + + test_create_port_errors(); + test_connect_port_errors(); + + if (status == STATUS_SUCCESS) + NtClose(port_handle); + FreeLibrary(hntdll); } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 60e611bb25d..1f2de575f3d 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -3146,6 +3146,11 @@ NTSTATUS WINAPI NtConnectPort( HANDLE *handle, UNICODE_STRING *name, SECURITY_QU TRACE( "(%p,%s,%p,%p,%p,%p,%p,%p)\n", handle, debugstr_us(name), qos, write, read, max_len, info, info_len ); + if (!handle) + return STATUS_ACCESS_VIOLATION; + if (!name) + return STATUS_OBJECT_NAME_INVALID; + if (write) FIXME( "LPC_SECTION_WRITE not supported\n" ); if (read) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611