From: Rose Hellsing <rose@pinkro.se> 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 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59494 --- dlls/ntdll/ntdll.spec | 6 +- dlls/ntdll/ntsyscalls.h | 492 +++++++-------- dlls/ntdll/signal_arm64ec.c | 2 + dlls/ntdll/tests/port.c | 32 +- dlls/ntdll/unix/sync.c | 428 ++++++++++++- dlls/wow64/sync.c | 32 + include/wine/server_protocol.h | 177 +++++- include/winternl.h | 1 + server/Makefile.in | 1 + server/lpc_port.c | 1044 ++++++++++++++++++++++++++++++++ server/object.h | 4 + server/protocol.def | 90 +++ server/request_handlers.h | 72 +++ server/request_trace.h | 141 +++++ server/thread.c | 4 + server/thread.h | 1 + 16 files changed, 2238 insertions(+), 289 deletions(-) create mode 100644 server/lpc_port.c diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index f396c334e2d..7ec32bf83cc 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) @@ -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) @@ -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/ntsyscalls.h b/dlls/ntdll/ntsyscalls.h index ba7cc060c66..2c8c307e5c7 100644 --- a/dlls/ntdll/ntsyscalls.h +++ b/dlls/ntdll/ntsyscalls.h @@ -139,132 +139,134 @@ SYSCALL_ENTRY( 0x0087, NtCreateToken, 52 ) \ SYSCALL_ENTRY( 0x0088, NtCreateTransaction, 40 ) \ SYSCALL_ENTRY( 0x0089, NtCreateUserProcess, 44 ) \ - SYSCALL_ENTRY( 0x008a, NtDebugActiveProcess, 8 ) \ - SYSCALL_ENTRY( 0x008b, NtDebugContinue, 12 ) \ - SYSCALL_ENTRY( 0x008c, NtDeleteAtom, 4 ) \ - SYSCALL_ENTRY( 0x008d, NtDeleteFile, 4 ) \ - SYSCALL_ENTRY( 0x008e, NtDeleteKey, 4 ) \ - SYSCALL_ENTRY( 0x008f, NtDeleteValueKey, 8 ) \ - SYSCALL_ENTRY( 0x0090, NtDisplayString, 4 ) \ - SYSCALL_ENTRY( 0x0091, NtFilterToken, 24 ) \ - SYSCALL_ENTRY( 0x0092, NtFlushBuffersFileEx, 20 ) \ - SYSCALL_ENTRY( 0x0093, NtFlushInstructionCache, 12 ) \ - SYSCALL_ENTRY( 0x0094, NtFlushKey, 4 ) \ - SYSCALL_ENTRY( 0x0095, NtFlushProcessWriteBuffers, 0 ) \ - SYSCALL_ENTRY( 0x0096, NtFlushVirtualMemory, 16 ) \ - SYSCALL_ENTRY( 0x0097, NtGetContextThread, 8 ) \ - SYSCALL_ENTRY( 0x0098, NtGetCurrentProcessorNumber, 0 ) \ - SYSCALL_ENTRY( 0x0099, NtGetNextProcess, 20 ) \ - SYSCALL_ENTRY( 0x009a, NtGetNextThread, 24 ) \ - SYSCALL_ENTRY( 0x009b, NtGetNlsSectionPtr, 20 ) \ - SYSCALL_ENTRY( 0x009c, NtGetWriteWatch, 28 ) \ - SYSCALL_ENTRY( 0x009d, NtImpersonateAnonymousToken, 4 ) \ - SYSCALL_ENTRY( 0x009e, NtInitializeNlsFiles, 12 ) \ - SYSCALL_ENTRY( 0x009f, NtInitiatePowerAction, 16 ) \ - SYSCALL_ENTRY( 0x00a0, NtListenPort, 8 ) \ - SYSCALL_ENTRY( 0x00a1, NtLoadDriver, 4 ) \ - SYSCALL_ENTRY( 0x00a2, NtLoadKey, 8 ) \ - SYSCALL_ENTRY( 0x00a3, NtLoadKey2, 12 ) \ - SYSCALL_ENTRY( 0x00a4, NtLoadKeyEx, 32 ) \ - SYSCALL_ENTRY( 0x00a5, NtLockFile, 40 ) \ + SYSCALL_ENTRY( 0x008a, NtCreateWaitablePort, 20 ) \ + SYSCALL_ENTRY( 0x008b, NtDebugActiveProcess, 8 ) \ + SYSCALL_ENTRY( 0x008c, NtDebugContinue, 12 ) \ + SYSCALL_ENTRY( 0x008d, NtDeleteAtom, 4 ) \ + SYSCALL_ENTRY( 0x008e, NtDeleteFile, 4 ) \ + SYSCALL_ENTRY( 0x008f, NtDeleteKey, 4 ) \ + SYSCALL_ENTRY( 0x0090, NtDeleteValueKey, 8 ) \ + SYSCALL_ENTRY( 0x0091, NtDisplayString, 4 ) \ + SYSCALL_ENTRY( 0x0092, NtFilterToken, 24 ) \ + SYSCALL_ENTRY( 0x0093, NtFlushBuffersFileEx, 20 ) \ + SYSCALL_ENTRY( 0x0094, NtFlushInstructionCache, 12 ) \ + SYSCALL_ENTRY( 0x0095, NtFlushKey, 4 ) \ + SYSCALL_ENTRY( 0x0096, NtFlushProcessWriteBuffers, 0 ) \ + SYSCALL_ENTRY( 0x0097, NtFlushVirtualMemory, 16 ) \ + SYSCALL_ENTRY( 0x0098, NtGetContextThread, 8 ) \ + SYSCALL_ENTRY( 0x0099, NtGetCurrentProcessorNumber, 0 ) \ + SYSCALL_ENTRY( 0x009a, NtGetNextProcess, 20 ) \ + SYSCALL_ENTRY( 0x009b, NtGetNextThread, 24 ) \ + SYSCALL_ENTRY( 0x009c, NtGetNlsSectionPtr, 20 ) \ + SYSCALL_ENTRY( 0x009d, NtGetWriteWatch, 28 ) \ + SYSCALL_ENTRY( 0x009e, NtImpersonateAnonymousToken, 4 ) \ + SYSCALL_ENTRY( 0x009f, NtInitializeNlsFiles, 12 ) \ + SYSCALL_ENTRY( 0x00a0, NtInitiatePowerAction, 16 ) \ + SYSCALL_ENTRY( 0x00a1, NtListenPort, 8 ) \ + SYSCALL_ENTRY( 0x00a2, NtLoadDriver, 4 ) \ + SYSCALL_ENTRY( 0x00a3, NtLoadKey, 8 ) \ + SYSCALL_ENTRY( 0x00a4, NtLoadKey2, 12 ) \ + SYSCALL_ENTRY( 0x00a5, NtLoadKeyEx, 32 ) \ SYSCALL_ENTRY( 0x00a6, NtCreateDebugObject, 16 ) \ - SYSCALL_ENTRY( 0x00a7, NtLockVirtualMemory, 16 ) \ - SYSCALL_ENTRY( 0x00a8, NtMakePermanentObject, 4 ) \ - SYSCALL_ENTRY( 0x00a9, NtMakeTemporaryObject, 4 ) \ - SYSCALL_ENTRY( 0x00aa, NtMapViewOfSectionEx, 36 ) \ - SYSCALL_ENTRY( 0x00ab, NtNotifyChangeDirectoryFile, 36 ) \ - SYSCALL_ENTRY( 0x00ac, NtNotifyChangeKey, 40 ) \ - SYSCALL_ENTRY( 0x00ad, NtNotifyChangeMultipleKeys, 48 ) \ - SYSCALL_ENTRY( 0x00ae, NtOpenIoCompletion, 12 ) \ - SYSCALL_ENTRY( 0x00af, NtOpenJobObject, 12 ) \ - SYSCALL_ENTRY( 0x00b0, NtOpenKeyEx, 16 ) \ - SYSCALL_ENTRY( 0x00b1, NtOpenKeyTransacted, 16 ) \ - SYSCALL_ENTRY( 0x00b2, NtOpenKeyTransactedEx, 20 ) \ - SYSCALL_ENTRY( 0x00b3, NtOpenKeyedEvent, 12 ) \ - SYSCALL_ENTRY( 0x00b4, NtOpenMutant, 12 ) \ - SYSCALL_ENTRY( 0x00b5, NtOpenProcessToken, 12 ) \ - SYSCALL_ENTRY( 0x00b6, NtOpenSemaphore, 12 ) \ - SYSCALL_ENTRY( 0x00b7, NtOpenSymbolicLinkObject, 12 ) \ - SYSCALL_ENTRY( 0x00b8, NtOpenThread, 16 ) \ - SYSCALL_ENTRY( 0x00b9, NtOpenTimer, 12 ) \ - SYSCALL_ENTRY( 0x00ba, NtPrivilegeCheck, 12 ) \ - SYSCALL_ENTRY( 0x00bb, NtPulseEvent, 8 ) \ - SYSCALL_ENTRY( 0x00bc, NtQueryDirectoryObject, 28 ) \ - SYSCALL_ENTRY( 0x00bd, NtQueryEaFile, 36 ) \ - SYSCALL_ENTRY( 0x00be, NtQueryFullAttributesFile, 8 ) \ - SYSCALL_ENTRY( 0x00bf, NtQueryInformationAtom, 20 ) \ - SYSCALL_ENTRY( 0x00c0, NtQueryInformationJobObject, 20 ) \ - SYSCALL_ENTRY( 0x00c1, NtQueryInstallUILanguage, 4 ) \ - SYSCALL_ENTRY( 0x00c2, NtQueryIoCompletion, 20 ) \ - SYSCALL_ENTRY( 0x00c3, NtQueryLicenseValue, 20 ) \ - SYSCALL_ENTRY( 0x00c4, NtQueryMultipleValueKey, 24 ) \ - SYSCALL_ENTRY( 0x00c5, NtQueryMutant, 20 ) \ - SYSCALL_ENTRY( 0x00c6, NtQuerySecurityObject, 20 ) \ - SYSCALL_ENTRY( 0x00c7, NtQuerySemaphore, 20 ) \ - SYSCALL_ENTRY( 0x00c8, NtQuerySymbolicLinkObject, 12 ) \ - SYSCALL_ENTRY( 0x00c9, NtQuerySystemEnvironmentValue, 16 ) \ - SYSCALL_ENTRY( 0x00ca, NtQuerySystemEnvironmentValueEx, 20 ) \ - SYSCALL_ENTRY( 0x00cb, NtQuerySystemInformationEx, 24 ) \ - SYSCALL_ENTRY( 0x00cc, NtQueryTimerResolution, 12 ) \ - SYSCALL_ENTRY( 0x00cd, NtQueueApcThreadEx, 24 ) \ - SYSCALL_ENTRY( 0x00ce, NtQueueApcThreadEx2, 28 ) \ - SYSCALL_ENTRY( 0x00cf, NtRaiseException, 12 ) \ - SYSCALL_ENTRY( 0x00d0, NtRaiseHardError, 24 ) \ - SYSCALL_ENTRY( 0x00d1, NtRegisterThreadTerminatePort, 4 ) \ - SYSCALL_ENTRY( 0x00d2, NtReleaseKeyedEvent, 16 ) \ - SYSCALL_ENTRY( 0x00d3, NtRemoveIoCompletionEx, 24 ) \ - SYSCALL_ENTRY( 0x00d4, NtRemoveProcessDebug, 8 ) \ - SYSCALL_ENTRY( 0x00d5, NtRenameKey, 8 ) \ - SYSCALL_ENTRY( 0x00d6, NtReplaceKey, 12 ) \ - SYSCALL_ENTRY( 0x00d7, NtResetEvent, 8 ) \ - SYSCALL_ENTRY( 0x00d8, NtResetWriteWatch, 12 ) \ - SYSCALL_ENTRY( 0x00d9, NtRestoreKey, 12 ) \ - SYSCALL_ENTRY( 0x00da, NtResumeProcess, 4 ) \ - SYSCALL_ENTRY( 0x00db, NtRollbackTransaction, 8 ) \ - SYSCALL_ENTRY( 0x00dc, NtSaveKey, 8 ) \ - SYSCALL_ENTRY( 0x00dd, NtSecureConnectPort, 36 ) \ - SYSCALL_ENTRY( 0x00de, NtSetContextThread, 8 ) \ - SYSCALL_ENTRY( 0x00df, NtSetDebugFilterState, 12 ) \ - SYSCALL_ENTRY( 0x00e0, NtSetDefaultLocale, 8 ) \ - SYSCALL_ENTRY( 0x00e1, NtSetDefaultUILanguage, 4 ) \ - SYSCALL_ENTRY( 0x00e2, NtSetEaFile, 16 ) \ - SYSCALL_ENTRY( 0x00e3, NtSetInformationDebugObject, 20 ) \ - SYSCALL_ENTRY( 0x00e4, NtSetInformationJobObject, 16 ) \ - SYSCALL_ENTRY( 0x00e5, NtSetInformationKey, 16 ) \ - SYSCALL_ENTRY( 0x00e6, NtSetInformationToken, 16 ) \ - SYSCALL_ENTRY( 0x00e7, NtSetInformationVirtualMemory, 24 ) \ - SYSCALL_ENTRY( 0x00e8, NtSetIntervalProfile, 8 ) \ - SYSCALL_ENTRY( 0x00e9, NtSetIoCompletion, 20 ) \ - SYSCALL_ENTRY( 0x00ea, NtSetIoCompletionEx, 24 ) \ - SYSCALL_ENTRY( 0x00eb, NtSetLdtEntries, 24 ) \ - SYSCALL_ENTRY( 0x00ec, NtSetSecurityObject, 12 ) \ - SYSCALL_ENTRY( 0x00ed, NtSetSystemInformation, 12 ) \ - SYSCALL_ENTRY( 0x00ee, NtSetSystemTime, 8 ) \ - SYSCALL_ENTRY( 0x00ef, NtSetThreadExecutionState, 8 ) \ - SYSCALL_ENTRY( 0x00f0, NtSetTimerResolution, 12 ) \ - SYSCALL_ENTRY( 0x00f1, NtSetVolumeInformationFile, 20 ) \ - SYSCALL_ENTRY( 0x00f2, NtShutdownSystem, 4 ) \ - SYSCALL_ENTRY( 0x00f3, NtSignalAndWaitForSingleObject, 16 ) \ - SYSCALL_ENTRY( 0x00f4, NtSuspendProcess, 4 ) \ - SYSCALL_ENTRY( 0x00f5, NtSuspendThread, 8 ) \ - SYSCALL_ENTRY( 0x00f6, NtSystemDebugControl, 24 ) \ - SYSCALL_ENTRY( 0x00f7, NtTerminateJobObject, 8 ) \ - SYSCALL_ENTRY( 0x00f8, NtTestAlert, 0 ) \ - SYSCALL_ENTRY( 0x00f9, NtTraceControl, 24 ) \ - SYSCALL_ENTRY( 0x00fa, NtUnloadDriver, 4 ) \ - SYSCALL_ENTRY( 0x00fb, NtUnloadKey, 4 ) \ - SYSCALL_ENTRY( 0x00fc, NtUnlockFile, 20 ) \ - SYSCALL_ENTRY( 0x00fd, NtUnlockVirtualMemory, 16 ) \ - SYSCALL_ENTRY( 0x00fe, NtUnmapViewOfSectionEx, 12 ) \ - SYSCALL_ENTRY( 0x00ff, NtWaitForAlertByThreadId, 8 ) \ - SYSCALL_ENTRY( 0x0100, NtWaitForDebugEvent, 16 ) \ - SYSCALL_ENTRY( 0x0101, NtWaitForKeyedEvent, 16 ) \ - SYSCALL_ENTRY( 0x0102, NtWow64AllocateVirtualMemory64, 28 ) \ - SYSCALL_ENTRY( 0x0103, NtWow64GetNativeSystemInformation, 16 ) \ - SYSCALL_ENTRY( 0x0104, NtWow64IsProcessorFeaturePresent, 4 ) \ - SYSCALL_ENTRY( 0x0105, NtWow64QueryInformationProcess64, 20 ) \ - SYSCALL_ENTRY( 0x0106, NtWow64ReadVirtualMemory64, 28 ) \ - SYSCALL_ENTRY( 0x0107, NtWow64WriteVirtualMemory64, 28 ) + SYSCALL_ENTRY( 0x00a7, NtLockFile, 40 ) \ + SYSCALL_ENTRY( 0x00a8, NtLockVirtualMemory, 16 ) \ + SYSCALL_ENTRY( 0x00a9, NtMakePermanentObject, 4 ) \ + SYSCALL_ENTRY( 0x00aa, NtMakeTemporaryObject, 4 ) \ + SYSCALL_ENTRY( 0x00ab, NtMapViewOfSectionEx, 36 ) \ + SYSCALL_ENTRY( 0x00ac, NtNotifyChangeDirectoryFile, 36 ) \ + SYSCALL_ENTRY( 0x00ad, NtNotifyChangeKey, 40 ) \ + SYSCALL_ENTRY( 0x00ae, NtNotifyChangeMultipleKeys, 48 ) \ + SYSCALL_ENTRY( 0x00af, NtOpenIoCompletion, 12 ) \ + SYSCALL_ENTRY( 0x00b0, NtOpenJobObject, 12 ) \ + SYSCALL_ENTRY( 0x00b1, NtOpenKeyEx, 16 ) \ + SYSCALL_ENTRY( 0x00b2, NtOpenKeyTransacted, 16 ) \ + SYSCALL_ENTRY( 0x00b3, NtOpenKeyTransactedEx, 20 ) \ + SYSCALL_ENTRY( 0x00b4, NtOpenKeyedEvent, 12 ) \ + SYSCALL_ENTRY( 0x00b5, NtOpenMutant, 12 ) \ + SYSCALL_ENTRY( 0x00b6, NtOpenProcessToken, 12 ) \ + SYSCALL_ENTRY( 0x00b7, NtOpenSemaphore, 12 ) \ + SYSCALL_ENTRY( 0x00b8, NtOpenSymbolicLinkObject, 12 ) \ + SYSCALL_ENTRY( 0x00b9, NtOpenThread, 16 ) \ + SYSCALL_ENTRY( 0x00ba, NtOpenTimer, 12 ) \ + SYSCALL_ENTRY( 0x00bb, NtPrivilegeCheck, 12 ) \ + SYSCALL_ENTRY( 0x00bc, NtPulseEvent, 8 ) \ + SYSCALL_ENTRY( 0x00bd, NtQueryDirectoryObject, 28 ) \ + SYSCALL_ENTRY( 0x00be, NtQueryEaFile, 36 ) \ + SYSCALL_ENTRY( 0x00bf, NtQueryFullAttributesFile, 8 ) \ + SYSCALL_ENTRY( 0x00c0, NtQueryInformationAtom, 20 ) \ + SYSCALL_ENTRY( 0x00c1, NtQueryInformationJobObject, 20 ) \ + SYSCALL_ENTRY( 0x00c2, NtQueryInstallUILanguage, 4 ) \ + SYSCALL_ENTRY( 0x00c3, NtQueryIoCompletion, 20 ) \ + SYSCALL_ENTRY( 0x00c4, NtQueryLicenseValue, 20 ) \ + SYSCALL_ENTRY( 0x00c5, NtQueryMultipleValueKey, 24 ) \ + SYSCALL_ENTRY( 0x00c6, NtQueryMutant, 20 ) \ + SYSCALL_ENTRY( 0x00c7, NtQuerySecurityObject, 20 ) \ + SYSCALL_ENTRY( 0x00c8, NtQuerySemaphore, 20 ) \ + SYSCALL_ENTRY( 0x00c9, NtQuerySymbolicLinkObject, 12 ) \ + SYSCALL_ENTRY( 0x00ca, NtQuerySystemEnvironmentValue, 16 ) \ + SYSCALL_ENTRY( 0x00cb, NtQuerySystemEnvironmentValueEx, 20 ) \ + SYSCALL_ENTRY( 0x00cc, NtQuerySystemInformationEx, 24 ) \ + SYSCALL_ENTRY( 0x00cd, NtQueryTimerResolution, 12 ) \ + SYSCALL_ENTRY( 0x00ce, NtQueueApcThreadEx, 24 ) \ + SYSCALL_ENTRY( 0x00cf, NtQueueApcThreadEx2, 28 ) \ + SYSCALL_ENTRY( 0x00d0, NtRaiseException, 12 ) \ + SYSCALL_ENTRY( 0x00d1, NtRaiseHardError, 24 ) \ + SYSCALL_ENTRY( 0x00d2, NtRegisterThreadTerminatePort, 4 ) \ + SYSCALL_ENTRY( 0x00d3, NtReleaseKeyedEvent, 16 ) \ + SYSCALL_ENTRY( 0x00d4, NtRemoveIoCompletionEx, 24 ) \ + SYSCALL_ENTRY( 0x00d5, NtRemoveProcessDebug, 8 ) \ + SYSCALL_ENTRY( 0x00d6, NtRenameKey, 8 ) \ + SYSCALL_ENTRY( 0x00d7, NtReplaceKey, 12 ) \ + SYSCALL_ENTRY( 0x00d8, NtRequestPort, 8 ) \ + SYSCALL_ENTRY( 0x00d9, NtResetEvent, 8 ) \ + SYSCALL_ENTRY( 0x00da, NtResetWriteWatch, 12 ) \ + SYSCALL_ENTRY( 0x00db, NtRestoreKey, 12 ) \ + SYSCALL_ENTRY( 0x00dc, NtResumeProcess, 4 ) \ + SYSCALL_ENTRY( 0x00dd, NtRollbackTransaction, 8 ) \ + SYSCALL_ENTRY( 0x00de, NtSaveKey, 8 ) \ + SYSCALL_ENTRY( 0x00df, NtSecureConnectPort, 36 ) \ + SYSCALL_ENTRY( 0x00e0, NtSetContextThread, 8 ) \ + SYSCALL_ENTRY( 0x00e1, NtSetDebugFilterState, 12 ) \ + SYSCALL_ENTRY( 0x00e2, NtSetDefaultLocale, 8 ) \ + SYSCALL_ENTRY( 0x00e3, NtSetDefaultUILanguage, 4 ) \ + SYSCALL_ENTRY( 0x00e4, NtSetEaFile, 16 ) \ + SYSCALL_ENTRY( 0x00e5, NtSetInformationDebugObject, 20 ) \ + SYSCALL_ENTRY( 0x00e6, NtSetInformationJobObject, 16 ) \ + SYSCALL_ENTRY( 0x00e7, NtSetInformationKey, 16 ) \ + SYSCALL_ENTRY( 0x00e8, NtSetInformationToken, 16 ) \ + SYSCALL_ENTRY( 0x00e9, NtSetInformationVirtualMemory, 24 ) \ + SYSCALL_ENTRY( 0x00ea, NtSetIntervalProfile, 8 ) \ + SYSCALL_ENTRY( 0x00eb, NtSetIoCompletion, 20 ) \ + SYSCALL_ENTRY( 0x00ec, NtSetIoCompletionEx, 24 ) \ + SYSCALL_ENTRY( 0x00ed, NtSetLdtEntries, 24 ) \ + SYSCALL_ENTRY( 0x00ee, NtSetSecurityObject, 12 ) \ + SYSCALL_ENTRY( 0x00ef, NtSetSystemInformation, 12 ) \ + SYSCALL_ENTRY( 0x00f0, NtSetSystemTime, 8 ) \ + SYSCALL_ENTRY( 0x00f1, NtSetThreadExecutionState, 8 ) \ + SYSCALL_ENTRY( 0x00f2, NtSetTimerResolution, 12 ) \ + SYSCALL_ENTRY( 0x00f3, NtSetVolumeInformationFile, 20 ) \ + SYSCALL_ENTRY( 0x00f4, NtShutdownSystem, 4 ) \ + SYSCALL_ENTRY( 0x00f5, NtSignalAndWaitForSingleObject, 16 ) \ + SYSCALL_ENTRY( 0x00f6, NtSuspendProcess, 4 ) \ + SYSCALL_ENTRY( 0x00f7, NtSuspendThread, 8 ) \ + SYSCALL_ENTRY( 0x00f8, NtSystemDebugControl, 24 ) \ + SYSCALL_ENTRY( 0x00f9, NtTerminateJobObject, 8 ) \ + SYSCALL_ENTRY( 0x00fa, NtTestAlert, 0 ) \ + SYSCALL_ENTRY( 0x00fb, NtTraceControl, 24 ) \ + SYSCALL_ENTRY( 0x00fc, NtUnloadDriver, 4 ) \ + SYSCALL_ENTRY( 0x00fd, NtUnloadKey, 4 ) \ + SYSCALL_ENTRY( 0x00fe, NtUnlockFile, 20 ) \ + SYSCALL_ENTRY( 0x00ff, NtUnlockVirtualMemory, 16 ) \ + SYSCALL_ENTRY( 0x0100, NtUnmapViewOfSectionEx, 12 ) \ + SYSCALL_ENTRY( 0x0101, NtWaitForAlertByThreadId, 8 ) \ + SYSCALL_ENTRY( 0x0102, NtWaitForDebugEvent, 16 ) \ + SYSCALL_ENTRY( 0x0103, NtWaitForKeyedEvent, 16 ) \ + SYSCALL_ENTRY( 0x0104, NtWow64AllocateVirtualMemory64, 28 ) \ + SYSCALL_ENTRY( 0x0105, NtWow64GetNativeSystemInformation, 16 ) \ + SYSCALL_ENTRY( 0x0106, NtWow64IsProcessorFeaturePresent, 4 ) \ + SYSCALL_ENTRY( 0x0107, NtWow64QueryInformationProcess64, 20 ) \ + SYSCALL_ENTRY( 0x0108, NtWow64ReadVirtualMemory64, 28 ) \ + SYSCALL_ENTRY( 0x0109, NtWow64WriteVirtualMemory64, 28 ) #ifdef _WIN64 #define ALL_SYSCALLS \ SYSCALL_ENTRY( 0x0000, NtAccessCheck, 64 ) \ @@ -405,126 +407,128 @@ SYSCALL_ENTRY( 0x0087, NtCreateToken, 104 ) \ SYSCALL_ENTRY( 0x0088, NtCreateTransaction, 80 ) \ SYSCALL_ENTRY( 0x0089, NtCreateUserProcess, 88 ) \ - SYSCALL_ENTRY( 0x008a, NtDebugActiveProcess, 16 ) \ - SYSCALL_ENTRY( 0x008b, NtDebugContinue, 24 ) \ - SYSCALL_ENTRY( 0x008c, NtDeleteAtom, 8 ) \ - SYSCALL_ENTRY( 0x008d, NtDeleteFile, 8 ) \ - SYSCALL_ENTRY( 0x008e, NtDeleteKey, 8 ) \ - SYSCALL_ENTRY( 0x008f, NtDeleteValueKey, 16 ) \ - SYSCALL_ENTRY( 0x0090, NtDisplayString, 8 ) \ - SYSCALL_ENTRY( 0x0091, NtFilterToken, 48 ) \ - SYSCALL_ENTRY( 0x0092, NtFlushBuffersFileEx, 40 ) \ - SYSCALL_ENTRY( 0x0093, NtFlushInstructionCache, 24 ) \ - SYSCALL_ENTRY( 0x0094, NtFlushKey, 8 ) \ - SYSCALL_ENTRY( 0x0095, NtFlushProcessWriteBuffers, 0 ) \ - SYSCALL_ENTRY( 0x0096, NtFlushVirtualMemory, 32 ) \ - SYSCALL_ENTRY( 0x0097, NtGetContextThread, 16 ) \ - SYSCALL_ENTRY( 0x0098, NtGetCurrentProcessorNumber, 0 ) \ - SYSCALL_ENTRY( 0x0099, NtGetNextProcess, 40 ) \ - SYSCALL_ENTRY( 0x009a, NtGetNextThread, 48 ) \ - SYSCALL_ENTRY( 0x009b, NtGetNlsSectionPtr, 40 ) \ - SYSCALL_ENTRY( 0x009c, NtGetWriteWatch, 56 ) \ - SYSCALL_ENTRY( 0x009d, NtImpersonateAnonymousToken, 8 ) \ - SYSCALL_ENTRY( 0x009e, NtInitializeNlsFiles, 24 ) \ - SYSCALL_ENTRY( 0x009f, NtInitiatePowerAction, 32 ) \ - SYSCALL_ENTRY( 0x00a0, NtListenPort, 16 ) \ - SYSCALL_ENTRY( 0x00a1, NtLoadDriver, 8 ) \ - SYSCALL_ENTRY( 0x00a2, NtLoadKey, 16 ) \ - SYSCALL_ENTRY( 0x00a3, NtLoadKey2, 24 ) \ - SYSCALL_ENTRY( 0x00a4, NtLoadKeyEx, 64 ) \ - SYSCALL_ENTRY( 0x00a5, NtLockFile, 80 ) \ + SYSCALL_ENTRY( 0x008a, NtCreateWaitablePort, 40 ) \ + SYSCALL_ENTRY( 0x008b, NtDebugActiveProcess, 16 ) \ + SYSCALL_ENTRY( 0x008c, NtDebugContinue, 24 ) \ + SYSCALL_ENTRY( 0x008d, NtDeleteAtom, 8 ) \ + SYSCALL_ENTRY( 0x008e, NtDeleteFile, 8 ) \ + SYSCALL_ENTRY( 0x008f, NtDeleteKey, 8 ) \ + SYSCALL_ENTRY( 0x0090, NtDeleteValueKey, 16 ) \ + SYSCALL_ENTRY( 0x0091, NtDisplayString, 8 ) \ + SYSCALL_ENTRY( 0x0092, NtFilterToken, 48 ) \ + SYSCALL_ENTRY( 0x0093, NtFlushBuffersFileEx, 40 ) \ + SYSCALL_ENTRY( 0x0094, NtFlushInstructionCache, 24 ) \ + SYSCALL_ENTRY( 0x0095, NtFlushKey, 8 ) \ + SYSCALL_ENTRY( 0x0096, NtFlushProcessWriteBuffers, 0 ) \ + SYSCALL_ENTRY( 0x0097, NtFlushVirtualMemory, 32 ) \ + SYSCALL_ENTRY( 0x0098, NtGetContextThread, 16 ) \ + SYSCALL_ENTRY( 0x0099, NtGetCurrentProcessorNumber, 0 ) \ + SYSCALL_ENTRY( 0x009a, NtGetNextProcess, 40 ) \ + SYSCALL_ENTRY( 0x009b, NtGetNextThread, 48 ) \ + SYSCALL_ENTRY( 0x009c, NtGetNlsSectionPtr, 40 ) \ + SYSCALL_ENTRY( 0x009d, NtGetWriteWatch, 56 ) \ + SYSCALL_ENTRY( 0x009e, NtImpersonateAnonymousToken, 8 ) \ + SYSCALL_ENTRY( 0x009f, NtInitializeNlsFiles, 24 ) \ + SYSCALL_ENTRY( 0x00a0, NtInitiatePowerAction, 32 ) \ + SYSCALL_ENTRY( 0x00a1, NtListenPort, 16 ) \ + SYSCALL_ENTRY( 0x00a2, NtLoadDriver, 8 ) \ + SYSCALL_ENTRY( 0x00a3, NtLoadKey, 16 ) \ + SYSCALL_ENTRY( 0x00a4, NtLoadKey2, 24 ) \ + SYSCALL_ENTRY( 0x00a5, NtLoadKeyEx, 64 ) \ SYSCALL_ENTRY( 0x00a6, NtCreateDebugObject, 32 ) \ - SYSCALL_ENTRY( 0x00a7, NtLockVirtualMemory, 32 ) \ - SYSCALL_ENTRY( 0x00a8, NtMakePermanentObject, 8 ) \ - SYSCALL_ENTRY( 0x00a9, NtMakeTemporaryObject, 8 ) \ - SYSCALL_ENTRY( 0x00aa, NtMapViewOfSectionEx, 72 ) \ - SYSCALL_ENTRY( 0x00ab, NtNotifyChangeDirectoryFile, 72 ) \ - SYSCALL_ENTRY( 0x00ac, NtNotifyChangeKey, 80 ) \ - SYSCALL_ENTRY( 0x00ad, NtNotifyChangeMultipleKeys, 96 ) \ - SYSCALL_ENTRY( 0x00ae, NtOpenIoCompletion, 24 ) \ - SYSCALL_ENTRY( 0x00af, NtOpenJobObject, 24 ) \ - SYSCALL_ENTRY( 0x00b0, NtOpenKeyEx, 32 ) \ - SYSCALL_ENTRY( 0x00b1, NtOpenKeyTransacted, 32 ) \ - SYSCALL_ENTRY( 0x00b2, NtOpenKeyTransactedEx, 40 ) \ - SYSCALL_ENTRY( 0x00b3, NtOpenKeyedEvent, 24 ) \ - SYSCALL_ENTRY( 0x00b4, NtOpenMutant, 24 ) \ - SYSCALL_ENTRY( 0x00b5, NtOpenProcessToken, 24 ) \ - SYSCALL_ENTRY( 0x00b6, NtOpenSemaphore, 24 ) \ - SYSCALL_ENTRY( 0x00b7, NtOpenSymbolicLinkObject, 24 ) \ - SYSCALL_ENTRY( 0x00b8, NtOpenThread, 32 ) \ - SYSCALL_ENTRY( 0x00b9, NtOpenTimer, 24 ) \ - SYSCALL_ENTRY( 0x00ba, NtPrivilegeCheck, 24 ) \ - SYSCALL_ENTRY( 0x00bb, NtPulseEvent, 16 ) \ - SYSCALL_ENTRY( 0x00bc, NtQueryDirectoryObject, 56 ) \ - SYSCALL_ENTRY( 0x00bd, NtQueryEaFile, 72 ) \ - SYSCALL_ENTRY( 0x00be, NtQueryFullAttributesFile, 16 ) \ - SYSCALL_ENTRY( 0x00bf, NtQueryInformationAtom, 40 ) \ - SYSCALL_ENTRY( 0x00c0, NtQueryInformationJobObject, 40 ) \ - SYSCALL_ENTRY( 0x00c1, NtQueryInstallUILanguage, 8 ) \ - SYSCALL_ENTRY( 0x00c2, NtQueryIoCompletion, 40 ) \ - SYSCALL_ENTRY( 0x00c3, NtQueryLicenseValue, 40 ) \ - SYSCALL_ENTRY( 0x00c4, NtQueryMultipleValueKey, 48 ) \ - SYSCALL_ENTRY( 0x00c5, NtQueryMutant, 40 ) \ - SYSCALL_ENTRY( 0x00c6, NtQuerySecurityObject, 40 ) \ - SYSCALL_ENTRY( 0x00c7, NtQuerySemaphore, 40 ) \ - SYSCALL_ENTRY( 0x00c8, NtQuerySymbolicLinkObject, 24 ) \ - SYSCALL_ENTRY( 0x00c9, NtQuerySystemEnvironmentValue, 32 ) \ - SYSCALL_ENTRY( 0x00ca, NtQuerySystemEnvironmentValueEx, 40 ) \ - SYSCALL_ENTRY( 0x00cb, NtQuerySystemInformationEx, 48 ) \ - SYSCALL_ENTRY( 0x00cc, NtQueryTimerResolution, 24 ) \ - SYSCALL_ENTRY( 0x00cd, NtQueueApcThreadEx, 48 ) \ - SYSCALL_ENTRY( 0x00ce, NtQueueApcThreadEx2, 56 ) \ - SYSCALL_ENTRY( 0x00cf, NtRaiseException, 24 ) \ - SYSCALL_ENTRY( 0x00d0, NtRaiseHardError, 48 ) \ - SYSCALL_ENTRY( 0x00d1, NtRegisterThreadTerminatePort, 8 ) \ - SYSCALL_ENTRY( 0x00d2, NtReleaseKeyedEvent, 32 ) \ - SYSCALL_ENTRY( 0x00d3, NtRemoveIoCompletionEx, 48 ) \ - SYSCALL_ENTRY( 0x00d4, NtRemoveProcessDebug, 16 ) \ - SYSCALL_ENTRY( 0x00d5, NtRenameKey, 16 ) \ - SYSCALL_ENTRY( 0x00d6, NtReplaceKey, 24 ) \ - SYSCALL_ENTRY( 0x00d7, NtResetEvent, 16 ) \ - SYSCALL_ENTRY( 0x00d8, NtResetWriteWatch, 24 ) \ - SYSCALL_ENTRY( 0x00d9, NtRestoreKey, 24 ) \ - SYSCALL_ENTRY( 0x00da, NtResumeProcess, 8 ) \ - SYSCALL_ENTRY( 0x00db, NtRollbackTransaction, 16 ) \ - SYSCALL_ENTRY( 0x00dc, NtSaveKey, 16 ) \ - SYSCALL_ENTRY( 0x00dd, NtSecureConnectPort, 72 ) \ - SYSCALL_ENTRY( 0x00de, NtSetContextThread, 16 ) \ - SYSCALL_ENTRY( 0x00df, NtSetDebugFilterState, 24 ) \ - SYSCALL_ENTRY( 0x00e0, NtSetDefaultLocale, 16 ) \ - SYSCALL_ENTRY( 0x00e1, NtSetDefaultUILanguage, 8 ) \ - SYSCALL_ENTRY( 0x00e2, NtSetEaFile, 32 ) \ - SYSCALL_ENTRY( 0x00e3, NtSetInformationDebugObject, 40 ) \ - SYSCALL_ENTRY( 0x00e4, NtSetInformationJobObject, 32 ) \ - SYSCALL_ENTRY( 0x00e5, NtSetInformationKey, 32 ) \ - SYSCALL_ENTRY( 0x00e6, NtSetInformationToken, 32 ) \ - SYSCALL_ENTRY( 0x00e7, NtSetInformationVirtualMemory, 48 ) \ - SYSCALL_ENTRY( 0x00e8, NtSetIntervalProfile, 16 ) \ - SYSCALL_ENTRY( 0x00e9, NtSetIoCompletion, 40 ) \ - SYSCALL_ENTRY( 0x00ea, NtSetIoCompletionEx, 48 ) \ - SYSCALL_ENTRY( 0x00eb, NtSetLdtEntries, 48 ) \ - SYSCALL_ENTRY( 0x00ec, NtSetSecurityObject, 24 ) \ - SYSCALL_ENTRY( 0x00ed, NtSetSystemInformation, 24 ) \ - SYSCALL_ENTRY( 0x00ee, NtSetSystemTime, 16 ) \ - SYSCALL_ENTRY( 0x00ef, NtSetThreadExecutionState, 16 ) \ - SYSCALL_ENTRY( 0x00f0, NtSetTimerResolution, 24 ) \ - SYSCALL_ENTRY( 0x00f1, NtSetVolumeInformationFile, 40 ) \ - SYSCALL_ENTRY( 0x00f2, NtShutdownSystem, 8 ) \ - SYSCALL_ENTRY( 0x00f3, NtSignalAndWaitForSingleObject, 32 ) \ - SYSCALL_ENTRY( 0x00f4, NtSuspendProcess, 8 ) \ - SYSCALL_ENTRY( 0x00f5, NtSuspendThread, 16 ) \ - SYSCALL_ENTRY( 0x00f6, NtSystemDebugControl, 48 ) \ - SYSCALL_ENTRY( 0x00f7, NtTerminateJobObject, 16 ) \ - SYSCALL_ENTRY( 0x00f8, NtTestAlert, 0 ) \ - SYSCALL_ENTRY( 0x00f9, NtTraceControl, 48 ) \ - SYSCALL_ENTRY( 0x00fa, NtUnloadDriver, 8 ) \ - SYSCALL_ENTRY( 0x00fb, NtUnloadKey, 8 ) \ - SYSCALL_ENTRY( 0x00fc, NtUnlockFile, 40 ) \ - SYSCALL_ENTRY( 0x00fd, NtUnlockVirtualMemory, 32 ) \ - SYSCALL_ENTRY( 0x00fe, NtUnmapViewOfSectionEx, 24 ) \ - SYSCALL_ENTRY( 0x00ff, NtWaitForAlertByThreadId, 16 ) \ - SYSCALL_ENTRY( 0x0100, NtWaitForDebugEvent, 32 ) \ - SYSCALL_ENTRY( 0x0101, NtWaitForKeyedEvent, 32 ) + SYSCALL_ENTRY( 0x00a7, NtLockFile, 80 ) \ + SYSCALL_ENTRY( 0x00a8, NtLockVirtualMemory, 32 ) \ + SYSCALL_ENTRY( 0x00a9, NtMakePermanentObject, 8 ) \ + SYSCALL_ENTRY( 0x00aa, NtMakeTemporaryObject, 8 ) \ + SYSCALL_ENTRY( 0x00ab, NtMapViewOfSectionEx, 72 ) \ + SYSCALL_ENTRY( 0x00ac, NtNotifyChangeDirectoryFile, 72 ) \ + SYSCALL_ENTRY( 0x00ad, NtNotifyChangeKey, 80 ) \ + SYSCALL_ENTRY( 0x00ae, NtNotifyChangeMultipleKeys, 96 ) \ + SYSCALL_ENTRY( 0x00af, NtOpenIoCompletion, 24 ) \ + SYSCALL_ENTRY( 0x00b0, NtOpenJobObject, 24 ) \ + SYSCALL_ENTRY( 0x00b1, NtOpenKeyEx, 32 ) \ + SYSCALL_ENTRY( 0x00b2, NtOpenKeyTransacted, 32 ) \ + SYSCALL_ENTRY( 0x00b3, NtOpenKeyTransactedEx, 40 ) \ + SYSCALL_ENTRY( 0x00b4, NtOpenKeyedEvent, 24 ) \ + SYSCALL_ENTRY( 0x00b5, NtOpenMutant, 24 ) \ + SYSCALL_ENTRY( 0x00b6, NtOpenProcessToken, 24 ) \ + SYSCALL_ENTRY( 0x00b7, NtOpenSemaphore, 24 ) \ + SYSCALL_ENTRY( 0x00b8, NtOpenSymbolicLinkObject, 24 ) \ + SYSCALL_ENTRY( 0x00b9, NtOpenThread, 32 ) \ + SYSCALL_ENTRY( 0x00ba, NtOpenTimer, 24 ) \ + SYSCALL_ENTRY( 0x00bb, NtPrivilegeCheck, 24 ) \ + SYSCALL_ENTRY( 0x00bc, NtPulseEvent, 16 ) \ + SYSCALL_ENTRY( 0x00bd, NtQueryDirectoryObject, 56 ) \ + SYSCALL_ENTRY( 0x00be, NtQueryEaFile, 72 ) \ + SYSCALL_ENTRY( 0x00bf, NtQueryFullAttributesFile, 16 ) \ + SYSCALL_ENTRY( 0x00c0, NtQueryInformationAtom, 40 ) \ + SYSCALL_ENTRY( 0x00c1, NtQueryInformationJobObject, 40 ) \ + SYSCALL_ENTRY( 0x00c2, NtQueryInstallUILanguage, 8 ) \ + SYSCALL_ENTRY( 0x00c3, NtQueryIoCompletion, 40 ) \ + SYSCALL_ENTRY( 0x00c4, NtQueryLicenseValue, 40 ) \ + SYSCALL_ENTRY( 0x00c5, NtQueryMultipleValueKey, 48 ) \ + SYSCALL_ENTRY( 0x00c6, NtQueryMutant, 40 ) \ + SYSCALL_ENTRY( 0x00c7, NtQuerySecurityObject, 40 ) \ + SYSCALL_ENTRY( 0x00c8, NtQuerySemaphore, 40 ) \ + SYSCALL_ENTRY( 0x00c9, NtQuerySymbolicLinkObject, 24 ) \ + SYSCALL_ENTRY( 0x00ca, NtQuerySystemEnvironmentValue, 32 ) \ + SYSCALL_ENTRY( 0x00cb, NtQuerySystemEnvironmentValueEx, 40 ) \ + SYSCALL_ENTRY( 0x00cc, NtQuerySystemInformationEx, 48 ) \ + SYSCALL_ENTRY( 0x00cd, NtQueryTimerResolution, 24 ) \ + SYSCALL_ENTRY( 0x00ce, NtQueueApcThreadEx, 48 ) \ + SYSCALL_ENTRY( 0x00cf, NtQueueApcThreadEx2, 56 ) \ + SYSCALL_ENTRY( 0x00d0, NtRaiseException, 24 ) \ + SYSCALL_ENTRY( 0x00d1, NtRaiseHardError, 48 ) \ + SYSCALL_ENTRY( 0x00d2, NtRegisterThreadTerminatePort, 8 ) \ + SYSCALL_ENTRY( 0x00d3, NtReleaseKeyedEvent, 32 ) \ + SYSCALL_ENTRY( 0x00d4, NtRemoveIoCompletionEx, 48 ) \ + SYSCALL_ENTRY( 0x00d5, NtRemoveProcessDebug, 16 ) \ + SYSCALL_ENTRY( 0x00d6, NtRenameKey, 16 ) \ + SYSCALL_ENTRY( 0x00d7, NtReplaceKey, 24 ) \ + SYSCALL_ENTRY( 0x00d8, NtRequestPort, 16 ) \ + SYSCALL_ENTRY( 0x00d9, NtResetEvent, 16 ) \ + SYSCALL_ENTRY( 0x00da, NtResetWriteWatch, 24 ) \ + SYSCALL_ENTRY( 0x00db, NtRestoreKey, 24 ) \ + SYSCALL_ENTRY( 0x00dc, NtResumeProcess, 8 ) \ + SYSCALL_ENTRY( 0x00dd, NtRollbackTransaction, 16 ) \ + SYSCALL_ENTRY( 0x00de, NtSaveKey, 16 ) \ + SYSCALL_ENTRY( 0x00df, NtSecureConnectPort, 72 ) \ + SYSCALL_ENTRY( 0x00e0, NtSetContextThread, 16 ) \ + SYSCALL_ENTRY( 0x00e1, NtSetDebugFilterState, 24 ) \ + SYSCALL_ENTRY( 0x00e2, NtSetDefaultLocale, 16 ) \ + SYSCALL_ENTRY( 0x00e3, NtSetDefaultUILanguage, 8 ) \ + SYSCALL_ENTRY( 0x00e4, NtSetEaFile, 32 ) \ + SYSCALL_ENTRY( 0x00e5, NtSetInformationDebugObject, 40 ) \ + SYSCALL_ENTRY( 0x00e6, NtSetInformationJobObject, 32 ) \ + SYSCALL_ENTRY( 0x00e7, NtSetInformationKey, 32 ) \ + SYSCALL_ENTRY( 0x00e8, NtSetInformationToken, 32 ) \ + SYSCALL_ENTRY( 0x00e9, NtSetInformationVirtualMemory, 48 ) \ + SYSCALL_ENTRY( 0x00ea, NtSetIntervalProfile, 16 ) \ + SYSCALL_ENTRY( 0x00eb, NtSetIoCompletion, 40 ) \ + SYSCALL_ENTRY( 0x00ec, NtSetIoCompletionEx, 48 ) \ + SYSCALL_ENTRY( 0x00ed, NtSetLdtEntries, 48 ) \ + SYSCALL_ENTRY( 0x00ee, NtSetSecurityObject, 24 ) \ + SYSCALL_ENTRY( 0x00ef, NtSetSystemInformation, 24 ) \ + SYSCALL_ENTRY( 0x00f0, NtSetSystemTime, 16 ) \ + SYSCALL_ENTRY( 0x00f1, NtSetThreadExecutionState, 16 ) \ + SYSCALL_ENTRY( 0x00f2, NtSetTimerResolution, 24 ) \ + SYSCALL_ENTRY( 0x00f3, NtSetVolumeInformationFile, 40 ) \ + SYSCALL_ENTRY( 0x00f4, NtShutdownSystem, 8 ) \ + SYSCALL_ENTRY( 0x00f5, NtSignalAndWaitForSingleObject, 32 ) \ + SYSCALL_ENTRY( 0x00f6, NtSuspendProcess, 8 ) \ + SYSCALL_ENTRY( 0x00f7, NtSuspendThread, 16 ) \ + SYSCALL_ENTRY( 0x00f8, NtSystemDebugControl, 48 ) \ + SYSCALL_ENTRY( 0x00f9, NtTerminateJobObject, 16 ) \ + SYSCALL_ENTRY( 0x00fa, NtTestAlert, 0 ) \ + SYSCALL_ENTRY( 0x00fb, NtTraceControl, 48 ) \ + SYSCALL_ENTRY( 0x00fc, NtUnloadDriver, 8 ) \ + SYSCALL_ENTRY( 0x00fd, NtUnloadKey, 8 ) \ + SYSCALL_ENTRY( 0x00fe, NtUnlockFile, 40 ) \ + SYSCALL_ENTRY( 0x00ff, NtUnlockVirtualMemory, 32 ) \ + SYSCALL_ENTRY( 0x0100, NtUnmapViewOfSectionEx, 24 ) \ + SYSCALL_ENTRY( 0x0101, NtWaitForAlertByThreadId, 16 ) \ + SYSCALL_ENTRY( 0x0102, NtWaitForDebugEvent, 32 ) \ + SYSCALL_ENTRY( 0x0103, NtWaitForKeyedEvent, 32 ) #else #define ALL_SYSCALLS ALL_SYSCALLS32 #endif diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 954ace7824e..dd139721966 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)) @@ -531,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/tests/port.c b/dlls/ntdll/tests/port.c index 80c60d09683..2d907c7ba0b 100644 --- a/dlls/ntdll/tests/port.c +++ b/dlls/ntdll/tests/port.c @@ -102,6 +102,7 @@ union lpc_message #define LPC_CONNECTION_REQUEST 10 static const WCHAR PORTNAME[] = {'\\','M','y','P','o','r','t',0}; +static const WCHAR WAITABLEPORTNAME[] = {'\\','M','y','W','P','o','r','t',0}; #define REQUEST1 "Request1" #define REQUEST2 "Request2" @@ -120,6 +121,7 @@ static NTSTATUS (WINAPI *pNtReplyPort)(HANDLE,PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtReplyWaitReceivePort)(PHANDLE,PULONG,PLPC_MESSAGE, PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtCreatePort)(PHANDLE,POBJECT_ATTRIBUTES,ULONG,ULONG,ULONG); +static NTSTATUS (WINAPI *pNtCreateWaitablePort)(PHANDLE,POBJECT_ATTRIBUTES,ULONG,ULONG,ULONG); static NTSTATUS (WINAPI *pNtRequestWaitReplyPort)(HANDLE,PLPC_MESSAGE,PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtRequestPort)(HANDLE,PLPC_MESSAGE); static NTSTATUS (WINAPI *pNtRegisterThreadTerminatePort)(HANDLE); @@ -144,6 +146,7 @@ static BOOL init_function_ptrs(void) pNtReplyPort = (void *)GetProcAddress(hntdll, "NtReplyPort"); pNtReplyWaitReceivePort = (void *)GetProcAddress(hntdll, "NtReplyWaitReceivePort"); pNtCreatePort = (void *)GetProcAddress(hntdll, "NtCreatePort"); + pNtCreateWaitablePort = (void *)GetProcAddress(hntdll, "NtCreateWaitablePort"); pNtRequestWaitReplyPort = (void *)GetProcAddress(hntdll, "NtRequestWaitReplyPort"); pNtRequestPort = (void *)GetProcAddress(hntdll, "NtRequestPort"); pNtRegisterThreadTerminatePort = (void *)GetProcAddress(hntdll, "NtRegisterThreadTerminatePort"); @@ -151,7 +154,8 @@ static BOOL init_function_ptrs(void) pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString"); if (!pNtCompleteConnectPort || !pNtAcceptConnectPort || - !pNtReplyWaitReceivePort || !pNtCreatePort || !pNtRequestWaitReplyPort || + !pNtReplyWaitReceivePort || !pNtCreatePort || + !pNtCreateWaitablePort || !pNtRequestWaitReplyPort || !pNtRequestPort || !pNtRegisterThreadTerminatePort || !pNtConnectPort || !pRtlInitUnicodeString) { @@ -239,7 +243,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 +326,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 */ @@ -386,6 +387,25 @@ START_TEST(port) if (status == STATUS_ACCESS_DENIED) skip("Not enough rights\n"); else ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status); + if (status == STATUS_SUCCESS) + { + DWORD id; + HANDLE thread = CreateThread(NULL, 0, test_ports_client, NULL, 0, &id); + ok(thread != NULL, "Expected non-NULL thread handle!\n"); + + test_ports_server( port_handle ); + ok( WaitForSingleObject( thread, 10000 ) == 0, "thread didn't exit\n" ); + CloseHandle(thread); + } + + pRtlInitUnicodeString(&port, WAITABLEPORTNAME); + + obj.ObjectName = &port; + + status = pNtCreateWaitablePort(&port_handle, &obj, 100, 100, 0); + if (status == STATUS_ACCESS_DENIED) skip("Not enough rights\n"); + else ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + if (status == STATUS_SUCCESS) { DWORD id; diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index bc9bbf2c30d..5ceaeec5ed2 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,10 +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; +} /*********************************************************************** * NtConnectPort (NTDLL.@) @@ -3080,10 +3134,94 @@ 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. + * The server will signal the port's connect_event when NtCompleteConnectPort + * is called (or immediately for rejected connections). */ + for (;;) + { + unsigned int connect_status; + + /* Check current connection 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) + { + /* Connection complete */ + if (connect_status == STATUS_SUCCESS) + { + *handle = port_handle; + return STATUS_SUCCESS; + } + else + { + NtClose( port_handle ); + return connect_status; + } + } + + /* Still pending */ + ret = NtWaitForSingleObject( port_handle, FALSE, NULL ); + if (ret) + { + NtClose( port_handle ); + return ret; + } + } } @@ -3094,9 +3232,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 ); } @@ -3105,8 +3245,40 @@ 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 ); + /* 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 ? 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; + + /* Wait for a connection request */ + ret = NtWaitForSingleObject( handle, FALSE, NULL ); + if (ret) break; + } + return ret; } @@ -3116,8 +3288,30 @@ 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 ) + { + /* Note: handle is not used, the server finds the pending connection + * by message ID from a global list. */ + 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; } @@ -3126,8 +3320,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; } @@ -3157,8 +3360,46 @@ 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; } @@ -3167,44 +3408,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 d5b52a5c815..fd982d0aa3c 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -378,6 +378,26 @@ NTSTATUS WINAPI wow64_NtCreatePort( 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_NtCreateSection @@ -1332,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/include/wine/server_protocol.h b/include/wine/server_protocol.h index 85f636dd995..0b996367f97 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -6162,6 +6162,150 @@ struct d3dkmt_mutex_release_reply }; +struct create_lpc_port_request +{ + struct request_header __header; + unsigned int access; + unsigned int flags; + unsigned int max_msg_len; + unsigned int max_connect_info; + /* VARARG(objattr,object_attributes); */ + char __pad_28[4]; +}; +struct create_lpc_port_reply +{ + struct reply_header __header; + obj_handle_t handle; + char __pad_12[4]; +}; + + +struct connect_lpc_port_request +{ + struct request_header __header; + unsigned int access; + data_size_t info_size; + /* VARARG(objattr,object_attributes); */ + /* VARARG(info,bytes); */ + char __pad_20[4]; +}; +struct connect_lpc_port_reply +{ + struct reply_header __header; + obj_handle_t handle; + data_size_t info_size; + /* VARARG(info,bytes); */ +}; + + +struct listen_lpc_port_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct listen_lpc_port_reply +{ + struct reply_header __header; + client_ptr_t message; + data_size_t msg_size; + process_id_t client_pid; + thread_id_t client_tid; + unsigned int msg_id; + /* VARARG(info,bytes); */ +}; + + +struct accept_lpc_connect_request +{ + struct request_header __header; + obj_handle_t handle; + int accept; + unsigned int msg_id; + client_ptr_t context; + /* VARARG(info,bytes); */ +}; +struct accept_lpc_connect_reply +{ + struct reply_header __header; + obj_handle_t handle; + char __pad_12[4]; +}; + + +struct complete_lpc_connect_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct complete_lpc_connect_reply +{ + struct reply_header __header; +}; + + +struct get_lpc_connect_status_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_lpc_connect_status_reply +{ + struct reply_header __header; + unsigned int status; + char __pad_12[4]; +}; + + +struct request_lpc_reply_request +{ + struct request_header __header; + obj_handle_t handle; + data_size_t data_size; + unsigned int msg_type; + /* VARARG(data,bytes); */ +}; +struct request_lpc_reply_reply +{ + struct reply_header __header; + unsigned int msg_id; + char __pad_12[4]; +}; + + +struct reply_wait_receive_lpc_request +{ + struct request_header __header; + obj_handle_t handle; + unsigned int reply_msg_id; + data_size_t reply_size; + timeout_t timeout; + /* VARARG(reply,bytes); */ +}; +struct reply_wait_receive_lpc_reply +{ + struct reply_header __header; + 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); */ + char __pad_36[4]; +}; + + +struct register_lpc_terminate_port_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct register_lpc_terminate_port_reply +{ + struct reply_header __header; +}; + + enum request { REQ_new_process, @@ -6470,6 +6614,15 @@ enum request REQ_d3dkmt_object_open_name, REQ_d3dkmt_mutex_acquire, REQ_d3dkmt_mutex_release, + REQ_create_lpc_port, + REQ_connect_lpc_port, + REQ_listen_lpc_port, + REQ_accept_lpc_connect, + REQ_complete_lpc_connect, + REQ_get_lpc_connect_status, + REQ_request_lpc_reply, + REQ_reply_wait_receive_lpc, + REQ_register_lpc_terminate_port, REQ_NB_REQUESTS }; @@ -6783,6 +6936,15 @@ union generic_request struct d3dkmt_object_open_name_request d3dkmt_object_open_name_request; struct d3dkmt_mutex_acquire_request d3dkmt_mutex_acquire_request; struct d3dkmt_mutex_release_request d3dkmt_mutex_release_request; + struct create_lpc_port_request create_lpc_port_request; + struct connect_lpc_port_request connect_lpc_port_request; + struct listen_lpc_port_request listen_lpc_port_request; + struct accept_lpc_connect_request accept_lpc_connect_request; + struct complete_lpc_connect_request complete_lpc_connect_request; + struct get_lpc_connect_status_request get_lpc_connect_status_request; + struct request_lpc_reply_request request_lpc_reply_request; + struct reply_wait_receive_lpc_request reply_wait_receive_lpc_request; + struct register_lpc_terminate_port_request register_lpc_terminate_port_request; }; union generic_reply { @@ -7094,8 +7256,17 @@ union generic_reply struct d3dkmt_object_open_name_reply d3dkmt_object_open_name_reply; struct d3dkmt_mutex_acquire_reply d3dkmt_mutex_acquire_reply; struct d3dkmt_mutex_release_reply d3dkmt_mutex_release_reply; -}; - -#define SERVER_PROTOCOL_VERSION 931 + struct create_lpc_port_reply create_lpc_port_reply; + struct connect_lpc_port_reply connect_lpc_port_reply; + struct listen_lpc_port_reply listen_lpc_port_reply; + struct accept_lpc_connect_reply accept_lpc_connect_reply; + struct complete_lpc_connect_reply complete_lpc_connect_reply; + struct get_lpc_connect_status_reply get_lpc_connect_status_reply; + struct request_lpc_reply_reply request_lpc_reply_reply; + struct reply_wait_receive_lpc_reply reply_wait_receive_lpc_reply; + struct register_lpc_terminate_port_reply register_lpc_terminate_port_reply; +}; + +#define SERVER_PROTOCOL_VERSION 936 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ 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..2f1d4cc83ff --- /dev/null +++ b/server/lpc_port.c @@ -0,0 +1,1044 @@ +/* + * 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_ALL_ACCESS, combining standard rights with port-specific 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 */ +#define PORT_TYPE_CLIENT 0x02 /* Client's end of connection */ +#define PORT_TYPE_CHANNEL 0x03 /* Server's per-client communication channel */ +#define PORT_TYPE_UNNAMED 0x04 /* Unnamed port */ + +/* Port flags */ +#define PORT_FLAG_WAITABLE 0x0001 + +/* LPC message types */ +#define LPC_REQUEST 1 +#define LPC_REPLY 2 +#define LPC_DATAGRAM 3 +#define LPC_LOST_REPLY 4 +#define LPC_PORT_CLOSED 5 +#define LPC_CLIENT_DIED 6 +#define LPC_EXCEPTION 7 +#define LPC_DEBUG_EVENT 8 +#define LPC_ERROR_EVENT 9 +#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 across all ports */ +static unsigned int global_msg_id_counter = 0; + +/* Global list of pending connection requests, allows NtAcceptConnectPort to find + * connections by message ID without needing a server port handle */ +static struct list global_pending_connects = LIST_INIT(global_pending_connects); + +/* Global list of pending requests awaiting replies, tracks which client is + * waiting for a reply to which message ID */ +static struct list global_pending_requests = LIST_INIT(global_pending_requests); + +/* Pending request entry, tracks who sent a request and is waiting for reply */ +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 registered terminate ports */ +struct lpc_terminate_port_entry +{ + struct list entry; /* entry in thread's lpc_terminate_ports list */ + struct lpc_port *port; /* port to notify on thread termination */ +}; + +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 + }, +}; + +/* Internal message structure, holds queued messages */ +struct lpc_message +{ + struct list entry; /* queue entry (port's pending_connects list) */ + 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_REQUEST, LPC_REPLY, etc. */ + 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 */ +}; + +/* Thread waiting for a reply */ +struct lpc_reply_wait +{ + struct list entry; /* list entry in reply_waiters */ + struct thread *thread; /* thread waiting for reply */ + unsigned int msg_id; /* message ID we're waiting for */ + struct object *event; /* sync object to signal when reply arrives */ + struct lpc_message *reply; /* reply message (set when received) */ +}; + +/* LPC port object */ +struct lpc_port +{ + struct object obj; /* object header */ + unsigned int port_type; /* PORT_TYPE_* */ + unsigned int flags; /* PORT_FLAG_* */ + + /* Port relationships */ + struct lpc_port *connection_port; /* reference to connection port */ + struct lpc_port *connected_port; /* paired port: client <-> communication */ + + /* Message queue */ + struct list msg_queue; /* list of pending lpc_message */ + struct object *queue_event; /* event signaled when message arrives */ + + /* Connection tracking */ + struct list pending_connects;/* list of pending connection lpc_message */ + struct list reply_waiters; /* list of lpc_reply_wait entries */ + + /* Limits */ + unsigned int max_msg_len; /* maximum message length */ + unsigned int max_connect_info;/* maximum connection info length */ + + /* Waitable port support */ + struct object *wait_event; /* event for WaitForSingleObject (waitable ports) */ + + /* Security/context */ + struct process *server_process; /* server process (for connection ports) */ + client_ptr_t port_context; /* user-defined port context */ + + /* For communication ports: track the client thread during accept */ + struct thread *client_thread; /* client thread (for NtCompleteConnectPort) */ + + /* Message ID counter */ + unsigned int next_msg_id; + + /* Connection status */ + 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 ); +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 */ +}; + +/* 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; + } + + /* Allocate at least enough for the full struct (including 1-byte data array) */ + 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) + { + /* Remove from global list if present */ + 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->msg_queue ) && 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, + 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) + { + /* Initialize new port */ + port->flags = flags; + port->connection_port = NULL; + port->connected_port = NULL; + list_init( &port->msg_queue ); + port->queue_event = NULL; + list_init( &port->pending_connects ); + list_init( &port->reply_waiters ); + 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 = NULL; + port->port_context = 0; + port->client_thread = NULL; + port->next_msg_id = 0; + port->connect_event = NULL; + port->connect_status = STATUS_PENDING; + + /* Determine port type based on whether it has a name */ + if (name->len) + { + port->port_type = PORT_TYPE_SERVER; + port->connection_port = (struct lpc_port *)grab_object( port ); + port->server_process = (struct process *)grab_object( current->process ); + } + else + { + port->port_type = PORT_TYPE_UNNAMED; + port->connected_port = (struct lpc_port *)grab_object( port ); + } + + /* Create queue event for message notification */ + port->queue_event = create_internal_sync( 0, 0 ); + if (!port->queue_event) + { + release_object( port ); + return NULL; + } + + /* Create wait event for waitable ports */ + if (flags & PORT_FLAG_WAITABLE) + { + port->wait_event = create_internal_sync( 1, 0 ); + if (!port->wait_event) + { + release_object( port ); + return NULL; + } + } + } + } + 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->msg_queue ); + port->queue_event = create_internal_sync( 0, 0 ); + list_init( &port->pending_connects ); + list_init( &port->reply_waiters ); + 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->next_msg_id = 0; + port->connect_event = NULL; + port->connect_status = STATUS_PENDING; + + /* For client ports, create a connect event for NtConnectPort to wait on. + * Use create_server_internal_sync (not create_internal_sync) so this works + * even when inproc sync is available, inproc sync objects can't be waited + * on via server-side wait queues. */ + if (port_type == PORT_TYPE_CLIENT) + { + port->connect_event = (struct object *)create_server_internal_sync( 1, 0 ); /* manual reset, not signaled */ + 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 (global) */ +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; +} + +/* 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 ); + /* Don't release client_port, caller takes ownership */ + free( pr ); + return client; + } + } + 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", "UNNAMED" }; + const char *type_name = port->port_type < 5 ? type_names[port->port_type] : "???"; + + assert( obj->ops == &lpc_port_ops ); + 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 ) +{ + struct lpc_port *port = (struct lpc_port *)obj; + assert( obj->ops == &lpc_port_ops ); + + /* For client ports with a connect_event, always return it. The event + * will be signaled when the connection completes (either success or refused). + * This allows NtConnectPort to wait on the port handle until complete. */ + if (port->port_type == PORT_TYPE_CLIENT && port->connect_event) + return grab_object( port->connect_event ); + + /* For waitable ports, return the wait event */ + if (port->wait_event) + return grab_object( port->wait_event ); + + /* For non-waitable ports, return the queue event (allows internal waiting) */ + if (port->queue_event) + return grab_object( port->queue_event ); + + return grab_object( obj ); +} + +static void lpc_port_destroy( struct object *obj ) +{ + struct lpc_port *port = (struct lpc_port *)obj; + struct lpc_message *msg, *next_msg; + struct lpc_reply_wait *wait, *next_wait; + + assert( obj->ops == &lpc_port_ops ); + + /* Free all queued messages */ + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->msg_queue, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + + /* Free all pending connection messages */ + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->pending_connects, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + + /* Wake and clean up any reply waiters */ + LIST_FOR_EACH_ENTRY_SAFE( wait, next_wait, &port->reply_waiters, struct lpc_reply_wait, entry ) + { + list_remove( &wait->entry ); + if (wait->event) + { + signal_sync( wait->event ); + release_object( wait->event ); + } + if (wait->thread) release_object( wait->thread ); + if (wait->reply) free_lpc_message( wait->reply ); + free( wait ); + } + + /* Release references */ + 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 */ +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 ); +} + +/* 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; + + /* Look up the connection port by name */ + 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; + } + + /* Create the client port */ + client_port = create_port_internal( PORT_TYPE_CLIENT, connection_port ); + if (!client_port) + { + release_object( connection_port ); + return; + } + + /* Create connection request message */ + 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 ); + + /* Queue the connection request on the connection port and global list */ + list_add_tail( &connection_port->pending_connects, &msg->entry ); + list_add_tail( &global_pending_connects, &msg->global_entry ); + signal_port_queue( connection_port ); + + /* Return the client port handle, actual connection completes later */ + 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; + } + + /* Check for pending connection requests */ + if (list_empty( &port->pending_connects )) + { + /* No pending connections, caller should wait on the port */ + set_error( STATUS_PENDING ); + release_object( port ); + return; + } + + /* Get the first pending connection */ + msg = LIST_ENTRY( list_head( &port->pending_connects ), struct lpc_message, entry ); + + reply->message = 0; /* Currently unused */ + reply->msg_size = msg->data_size; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->msg_id = msg->msg_id; + + /* Return connection info */ + 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; + + /* Find the pending connection by message ID from global list. */ + msg = find_pending_connect_global( req->msg_id ); + if (!msg) + { + set_error( STATUS_INVALID_CID ); + return; + } + + /* Get the server port from the message */ + 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; + + /* Note: The message was already removed from the port's pending_connects list + * by reply_wait_receive_lpc. The global_entry is removed by free_lpc_message. */ + + if (req->accept) + { + /* Create server communication port */ + comm_port = create_port_internal( PORT_TYPE_CHANNEL, connection_port ); + if (!comm_port) + { + free_lpc_message( msg ); + return; + } + + /* Link client and communication ports */ + comm_port->connected_port = (struct lpc_port *)grab_object( client_port ); + client_port->connected_port = (struct lpc_port *)grab_object( comm_port ); + + /* Mark client connection as successful (will be completed by NtCompleteConnectPort) */ + client_port->connect_status = STATUS_SUCCESS; + + /* Set port context */ + comm_port->port_context = req->context; + + /* Save client thread for NtCompleteConnectPort */ + 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 + { + /* Connection refused. Set status and signal client immediately */ + 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; + } + + /* Signal the client port's connect_event to wake up the waiting NtConnectPort call */ + client_port = port->connected_port; + if (client_port && client_port->connect_event) + signal_sync( client_port->connect_event ); + + /* Release client thread reference */ + 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 ); +} + +/* Send a request and wait for reply */ +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; + + /* Determine target port based on port type */ + if (port->port_type == PORT_TYPE_CLIENT) + { + /* Client sends to server. Messages go to the server's connection port + * so they can be received via NtReplyWaitReceivePort on the server port. */ + if (!port->connected_port || !port->connected_port->connection_port) + { + set_error( STATUS_PORT_DISCONNECTED ); + release_object( port ); + return; + } + /* Target is the connection (server) port */ + target_port = port->connected_port->connection_port; + } + else if (port->port_type == PORT_TYPE_CHANNEL) + { + /* Server sends to client via connected client port */ + 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 ); + + /* For LPC_REQUEST messages, track that client is waiting for a reply */ + if (msg->msg_type == LPC_REQUEST && port->port_type == PORT_TYPE_CLIENT) + track_pending_request( msg->msg_id, port ); + + /* Queue message to target */ + 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; + + /* Find the client that's waiting for this reply */ + client_port = find_pending_request_client( req->reply_msg_id ); + if (client_port) + { + /* Create and queue reply message to client */ + 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 ); + } + } + + /* Determine which port to receive from */ + 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; + } + + /* Check for pending messages */ + if (list_empty( &receive_port->msg_queue )) + { + /* Also check pending connects for connection ports */ + if (port->port_type == PORT_TYPE_SERVER && !list_empty( &port->pending_connects )) + { + msg = LIST_ENTRY( list_head( &port->pending_connects ), struct lpc_message, entry ); + + /* Remove from port's pending list so it won't be returned again. + * Keep it in the global list so NtAcceptConnectPort can find it by message ID. */ + list_remove( &msg->entry ); + + /* Return message info */ + 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 + { + /* No messages. Caller should wait */ + set_error( STATUS_PENDING ); + release_object( port ); + return; + } + } + else + { + /* Get first message from queue */ + msg = LIST_ENTRY( list_head( &receive_port->msg_queue ), struct lpc_message, entry ); + list_remove( &msg->entry ); + } + + /* Return message info */ + 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 to receive LPC_CLIENT_DIED when thread terminates */ +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; + 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/object.h b/server/object.h index 4d96740de03..b0f0784d2e4 100644 --- a/server/object.h +++ b/server/object.h @@ -242,6 +242,10 @@ extern void reset_event( struct event *event ); extern void abandon_mutexes( struct thread *thread ); extern void abandon_d3dkmt_mutexes( struct thread *thread ); +/* LPC port functions */ + +extern void lpc_send_client_died( struct thread *thread ); + /* in-process synchronization functions */ struct inproc_sync; diff --git a/server/protocol.def b/server/protocol.def index 5bca381fd91..3b91cf4e5bb 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4274,3 +4274,93 @@ 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; + unsigned int flags; + unsigned int max_msg_len; + unsigned int max_connect_info; + VARARG(objattr,object_attributes); +@REPLY + obj_handle_t handle; +@END + +/* Connect to an LPC port */ +@REQ(connect_lpc_port) + unsigned int access; + data_size_t info_size; + VARARG(objattr,object_attributes); + VARARG(info,bytes); +@REPLY + obj_handle_t handle; + data_size_t info_size; + VARARG(info,bytes); +@END + +/* Listen fo connection requests */ +@REQ(listen_lpc_port) + obj_handle_t handle; +@REPLY + client_ptr_t message; + data_size_t msg_size; + process_id_t client_pid; + thread_id_t client_tid; + unsigned int msg_id; + VARARG(info,bytes); +@END + +/* Accept or reject a connection */ +@REQ(accept_lpc_connect) + obj_handle_t handle; + int accept; + unsigned int msg_id; + client_ptr_t context; + VARARG(info,bytes); +@REPLY + obj_handle_t handle; +@END + +/* Complete connection */ +@REQ(complete_lpc_connect) + obj_handle_t handle; +@END + +/* Get connection status for client port */ +@REQ(get_lpc_connect_status) + obj_handle_t handle; +@REPLY + unsigned int 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 a port to receive LPC_CLIENT_DIED when thread terminates */ +@REQ(register_lpc_terminate_port) + obj_handle_t handle; +@END diff --git a/server/request_handlers.h b/server/request_handlers.h index 3c518795340..b337d210aaa 100644 --- a/server/request_handlers.h +++ b/server/request_handlers.h @@ -313,6 +313,15 @@ DECL_HANDLER(d3dkmt_share_objects); DECL_HANDLER(d3dkmt_object_open_name); DECL_HANDLER(d3dkmt_mutex_acquire); DECL_HANDLER(d3dkmt_mutex_release); +DECL_HANDLER(create_lpc_port); +DECL_HANDLER(connect_lpc_port); +DECL_HANDLER(listen_lpc_port); +DECL_HANDLER(accept_lpc_connect); +DECL_HANDLER(complete_lpc_connect); +DECL_HANDLER(get_lpc_connect_status); +DECL_HANDLER(request_lpc_reply); +DECL_HANDLER(reply_wait_receive_lpc); +DECL_HANDLER(register_lpc_terminate_port); typedef void (*req_handler)( const void *req, void *reply ); static const req_handler req_handlers[REQ_NB_REQUESTS] = @@ -623,6 +632,15 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_d3dkmt_object_open_name, (req_handler)req_d3dkmt_mutex_acquire, (req_handler)req_d3dkmt_mutex_release, + (req_handler)req_create_lpc_port, + (req_handler)req_connect_lpc_port, + (req_handler)req_listen_lpc_port, + (req_handler)req_accept_lpc_connect, + (req_handler)req_complete_lpc_connect, + (req_handler)req_get_lpc_connect_status, + (req_handler)req_request_lpc_reply, + (req_handler)req_reply_wait_receive_lpc, + (req_handler)req_register_lpc_terminate_port, }; C_ASSERT( sizeof(abstime_t) == 8 ); @@ -2375,3 +2393,57 @@ C_ASSERT( offsetof(struct d3dkmt_mutex_release_request, key_value) == 20 ); C_ASSERT( offsetof(struct d3dkmt_mutex_release_request, fence_value) == 24 ); C_ASSERT( offsetof(struct d3dkmt_mutex_release_request, runtime_size) == 32 ); C_ASSERT( sizeof(struct d3dkmt_mutex_release_request) == 40 ); +C_ASSERT( offsetof(struct create_lpc_port_request, access) == 12 ); +C_ASSERT( offsetof(struct create_lpc_port_request, flags) == 16 ); +C_ASSERT( offsetof(struct create_lpc_port_request, max_msg_len) == 20 ); +C_ASSERT( offsetof(struct create_lpc_port_request, max_connect_info) == 24 ); +C_ASSERT( sizeof(struct create_lpc_port_request) == 32 ); +C_ASSERT( offsetof(struct create_lpc_port_reply, handle) == 8 ); +C_ASSERT( sizeof(struct create_lpc_port_reply) == 16 ); +C_ASSERT( offsetof(struct connect_lpc_port_request, access) == 12 ); +C_ASSERT( offsetof(struct connect_lpc_port_request, info_size) == 16 ); +C_ASSERT( sizeof(struct connect_lpc_port_request) == 24 ); +C_ASSERT( offsetof(struct connect_lpc_port_reply, handle) == 8 ); +C_ASSERT( offsetof(struct connect_lpc_port_reply, info_size) == 12 ); +C_ASSERT( sizeof(struct connect_lpc_port_reply) == 16 ); +C_ASSERT( offsetof(struct listen_lpc_port_request, handle) == 12 ); +C_ASSERT( sizeof(struct listen_lpc_port_request) == 16 ); +C_ASSERT( offsetof(struct listen_lpc_port_reply, message) == 8 ); +C_ASSERT( offsetof(struct listen_lpc_port_reply, msg_size) == 16 ); +C_ASSERT( offsetof(struct listen_lpc_port_reply, client_pid) == 20 ); +C_ASSERT( offsetof(struct listen_lpc_port_reply, client_tid) == 24 ); +C_ASSERT( offsetof(struct listen_lpc_port_reply, msg_id) == 28 ); +C_ASSERT( sizeof(struct listen_lpc_port_reply) == 32 ); +C_ASSERT( offsetof(struct accept_lpc_connect_request, handle) == 12 ); +C_ASSERT( offsetof(struct accept_lpc_connect_request, accept) == 16 ); +C_ASSERT( offsetof(struct accept_lpc_connect_request, msg_id) == 20 ); +C_ASSERT( offsetof(struct accept_lpc_connect_request, context) == 24 ); +C_ASSERT( sizeof(struct accept_lpc_connect_request) == 32 ); +C_ASSERT( offsetof(struct accept_lpc_connect_reply, handle) == 8 ); +C_ASSERT( sizeof(struct accept_lpc_connect_reply) == 16 ); +C_ASSERT( offsetof(struct complete_lpc_connect_request, handle) == 12 ); +C_ASSERT( sizeof(struct complete_lpc_connect_request) == 16 ); +C_ASSERT( offsetof(struct get_lpc_connect_status_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_lpc_connect_status_request) == 16 ); +C_ASSERT( offsetof(struct get_lpc_connect_status_reply, status) == 8 ); +C_ASSERT( sizeof(struct get_lpc_connect_status_reply) == 16 ); +C_ASSERT( offsetof(struct request_lpc_reply_request, handle) == 12 ); +C_ASSERT( offsetof(struct request_lpc_reply_request, data_size) == 16 ); +C_ASSERT( offsetof(struct request_lpc_reply_request, msg_type) == 20 ); +C_ASSERT( sizeof(struct request_lpc_reply_request) == 24 ); +C_ASSERT( offsetof(struct request_lpc_reply_reply, msg_id) == 8 ); +C_ASSERT( sizeof(struct request_lpc_reply_reply) == 16 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_request, handle) == 12 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_request, reply_msg_id) == 16 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_request, reply_size) == 20 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_request, timeout) == 24 ); +C_ASSERT( sizeof(struct reply_wait_receive_lpc_request) == 32 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_reply, msg_id) == 8 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_reply, msg_type) == 12 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_reply, client_pid) == 16 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_reply, client_tid) == 20 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_reply, context) == 24 ); +C_ASSERT( offsetof(struct reply_wait_receive_lpc_reply, data_size) == 32 ); +C_ASSERT( sizeof(struct reply_wait_receive_lpc_reply) == 40 ); +C_ASSERT( offsetof(struct register_lpc_terminate_port_request, handle) == 12 ); +C_ASSERT( sizeof(struct register_lpc_terminate_port_request) == 16 ); diff --git a/server/request_trace.h b/server/request_trace.h index d31263cbfb4..80f7b467796 100644 --- a/server/request_trace.h +++ b/server/request_trace.h @@ -3512,6 +3512,117 @@ static void dump_d3dkmt_mutex_release_request( const struct d3dkmt_mutex_release dump_varargs_bytes( ", runtime=", cur_size ); } +static void dump_create_lpc_port_request( const struct create_lpc_port_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", flags=%08x", req->flags ); + fprintf( stderr, ", max_msg_len=%08x", req->max_msg_len ); + fprintf( stderr, ", max_connect_info=%08x", req->max_connect_info ); + dump_varargs_object_attributes( ", objattr=", cur_size ); +} + +static void dump_create_lpc_port_reply( const struct create_lpc_port_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_connect_lpc_port_request( const struct connect_lpc_port_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", info_size=%u", req->info_size ); + dump_varargs_object_attributes( ", objattr=", cur_size ); + dump_varargs_bytes( ", info=", cur_size ); +} + +static void dump_connect_lpc_port_reply( const struct connect_lpc_port_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", info_size=%u", req->info_size ); + dump_varargs_bytes( ", info=", cur_size ); +} + +static void dump_listen_lpc_port_request( const struct listen_lpc_port_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_listen_lpc_port_reply( const struct listen_lpc_port_reply *req ) +{ + dump_uint64( " message=", &req->message ); + fprintf( stderr, ", msg_size=%u", req->msg_size ); + fprintf( stderr, ", client_pid=%04x", req->client_pid ); + fprintf( stderr, ", client_tid=%04x", req->client_tid ); + fprintf( stderr, ", msg_id=%08x", req->msg_id ); + dump_varargs_bytes( ", info=", cur_size ); +} + +static void dump_accept_lpc_connect_request( const struct accept_lpc_connect_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", accept=%d", req->accept ); + fprintf( stderr, ", msg_id=%08x", req->msg_id ); + dump_uint64( ", context=", &req->context ); + dump_varargs_bytes( ", info=", cur_size ); +} + +static void dump_accept_lpc_connect_reply( const struct accept_lpc_connect_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_complete_lpc_connect_request( const struct complete_lpc_connect_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_lpc_connect_status_request( const struct get_lpc_connect_status_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_lpc_connect_status_reply( const struct get_lpc_connect_status_reply *req ) +{ + fprintf( stderr, " status=%08x", req->status ); +} + +static void dump_request_lpc_reply_request( const struct request_lpc_reply_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", data_size=%u", req->data_size ); + fprintf( stderr, ", msg_type=%08x", req->msg_type ); + dump_varargs_bytes( ", data=", cur_size ); +} + +static void dump_request_lpc_reply_reply( const struct request_lpc_reply_reply *req ) +{ + fprintf( stderr, " msg_id=%08x", req->msg_id ); +} + +static void dump_reply_wait_receive_lpc_request( const struct reply_wait_receive_lpc_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", reply_msg_id=%08x", req->reply_msg_id ); + fprintf( stderr, ", reply_size=%u", req->reply_size ); + dump_timeout( ", timeout=", &req->timeout ); + dump_varargs_bytes( ", reply=", cur_size ); +} + +static void dump_reply_wait_receive_lpc_reply( const struct reply_wait_receive_lpc_reply *req ) +{ + fprintf( stderr, " msg_id=%08x", req->msg_id ); + fprintf( stderr, ", msg_type=%08x", req->msg_type ); + fprintf( stderr, ", client_pid=%04x", req->client_pid ); + fprintf( stderr, ", client_tid=%04x", req->client_tid ); + dump_uint64( ", context=", &req->context ); + fprintf( stderr, ", data_size=%u", req->data_size ); + dump_varargs_bytes( ", data=", cur_size ); +} + +static void dump_register_lpc_terminate_port_request( const struct register_lpc_terminate_port_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + typedef void (*dump_func)( const void *req ); static const dump_func req_dumpers[REQ_NB_REQUESTS] = @@ -3822,6 +3933,15 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_d3dkmt_object_open_name_request, (dump_func)dump_d3dkmt_mutex_acquire_request, (dump_func)dump_d3dkmt_mutex_release_request, + (dump_func)dump_create_lpc_port_request, + (dump_func)dump_connect_lpc_port_request, + (dump_func)dump_listen_lpc_port_request, + (dump_func)dump_accept_lpc_connect_request, + (dump_func)dump_complete_lpc_connect_request, + (dump_func)dump_get_lpc_connect_status_request, + (dump_func)dump_request_lpc_reply_request, + (dump_func)dump_reply_wait_receive_lpc_request, + (dump_func)dump_register_lpc_terminate_port_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = @@ -4132,6 +4252,15 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_d3dkmt_object_open_name_reply, (dump_func)dump_d3dkmt_mutex_acquire_reply, NULL, + (dump_func)dump_create_lpc_port_reply, + (dump_func)dump_connect_lpc_port_reply, + (dump_func)dump_listen_lpc_port_reply, + (dump_func)dump_accept_lpc_connect_reply, + NULL, + (dump_func)dump_get_lpc_connect_status_reply, + (dump_func)dump_request_lpc_reply_reply, + (dump_func)dump_reply_wait_receive_lpc_reply, + NULL, }; static const char * const req_names[REQ_NB_REQUESTS] = @@ -4442,6 +4571,15 @@ static const char * const req_names[REQ_NB_REQUESTS] = "d3dkmt_object_open_name", "d3dkmt_mutex_acquire", "d3dkmt_mutex_release", + "create_lpc_port", + "connect_lpc_port", + "listen_lpc_port", + "accept_lpc_connect", + "complete_lpc_connect", + "get_lpc_connect_status", + "request_lpc_reply", + "reply_wait_receive_lpc", + "register_lpc_terminate_port", }; static const struct @@ -4519,6 +4657,7 @@ static const struct { "INVALID_PARAMETER", STATUS_INVALID_PARAMETER }, { "INVALID_PARAMETER_2", STATUS_INVALID_PARAMETER_2 }, { "INVALID_PIPE_STATE", STATUS_INVALID_PIPE_STATE }, + { "INVALID_PORT_HANDLE", STATUS_INVALID_PORT_HANDLE }, { "INVALID_READ_MODE", STATUS_INVALID_READ_MODE }, { "INVALID_SECURITY_DESCR", STATUS_INVALID_SECURITY_DESCR }, { "INVALID_USER_BUFFER", STATUS_INVALID_USER_BUFFER }, @@ -4568,6 +4707,8 @@ static const struct { "PIPE_EMPTY", STATUS_PIPE_EMPTY }, { "PIPE_LISTENING", STATUS_PIPE_LISTENING }, { "PIPE_NOT_AVAILABLE", STATUS_PIPE_NOT_AVAILABLE }, + { "PORT_CONNECTION_REFUSED", STATUS_PORT_CONNECTION_REFUSED }, + { "PORT_DISCONNECTED", STATUS_PORT_DISCONNECTED }, { "PORT_NOT_SET", STATUS_PORT_NOT_SET }, { "PREDEFINED_HANDLE", STATUS_PREDEFINED_HANDLE }, { "PRIVILEGE_NOT_HELD", STATUS_PRIVILEGE_NOT_HELD }, diff --git a/server/thread.c b/server/thread.c index 7207f918400..ebff38815dc 100644 --- a/server/thread.c +++ b/server/thread.c @@ -437,6 +437,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 +610,9 @@ static void cleanup_thread( struct thread *thread ) { int i; + /* Send LPC_CLIENT_DIED to registered terminate ports */ + lpc_send_client_died( thread ); + cleanup_thread_completion( thread ); if (thread->context) { diff --git a/server/thread.h b/server/thread.h index 77ea355483d..2d0337410c9 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 ports to notify on thread termination */ }; extern struct thread *current; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611