// Kernel Port IPC mechanism. Ports provide copied messaging, synchronous calls, waitability, and capability transfer. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PORT_KIND_LISTENER 1u #define PORT_KIND_ENDPOINT 2u #define PORT_CALL_PENDING 1u #define PORT_CALL_DELIVERED 2u #define PORT_CALL_REPLYING 3u #define PORT_CALL_COMPLETED 4u #define PORT_CALL_CANCELLED 5u #define PORT_PROCESS_MESSAGE_LIMIT 256u #define PORT_PROCESS_BYTE_LIMIT (256u * 1024u) #define PORT_PROCESS_HANDLE_LIMIT 512u #define PORT_PROCESS_CALL_LIMIT 128u typedef struct PORT_CONNECTION PORT_CONNECTION; typedef struct PORT_MESSAGE PORT_MESSAGE; typedef struct PORT_CALL PORT_CALL; typedef struct PORT_RECEIVE_WAIT PORT_RECEIVE_WAIT; typedef struct PORT_PENDING_CONNECTION { LIST_ENTRY Link; struct PORT* ServerEndpoint; SYS_PORT_CONNECTION_INFO Info; } PORT_PENDING_CONNECTION; struct PORT_MESSAGE { LIST_ENTRY Link; SYS_PORT_MESSAGE_INFO Info; void* Payload; struct PROCESS* AccountingProcess; uint32_t AccountedBytes; uint32_t AccountedHandles; struct KE_CAPTURED_HANDLE* Captured[SYS_PORT_MAX_TRANSFER_HANDLES]; uint32_t CapturedCount; PORT_CALL* Call; }; struct PORT_RECEIVE_WAIT { LIST_ENTRY Link; struct PROCESS* Process; void* Payload; uint32_t PayloadCapacity; uint32_t HandleCapacity; MM_LOCKED_USER_BUFFER* LockedPayload; EVENT* CompletionEvent; SYS_PORT_MESSAGE_INFO Info; SYS_HANDLE Handles[SYS_PORT_MAX_TRANSFER_HANDLES]; uint32_t HandleCount; KSTATUS Status; volatile int32_t Claimed; volatile int32_t Completed; }; struct PORT_CALL { LIST_ENTRY Link; volatile int32_t RefCount; PORT_CONNECTION* Connection; uint32_t MessageId; uint32_t CallerSide; volatile int32_t State; struct PROCESS* CallerProcess; EVENT* CompletionEvent; MM_LOCKED_USER_BUFFER* LockedReplyPayload; void* ReplyPayload; uint32_t ReplyCapacity; uint32_t ReplyHandleCapacity; SYS_PORT_MESSAGE_INFO ReplyInfo; SYS_HANDLE ReplyHandles[SYS_PORT_MAX_TRANSFER_HANDLES]; uint32_t ReplyHandleCount; KSTATUS Status; }; struct PORT_CONNECTION { volatile int32_t RefCount; SPINLOCK Lock; struct PORT* Endpoints[2]; struct PROCESS* PeerProcesses[2]; SYS_RIGHTS PeerProcessRights[2]; uint8_t Disconnected[2]; LIST_ENTRY Calls; }; typedef struct PORT_LISTENER_STATE { SPINLOCK Lock; LIST_ENTRY Pending; uint32_t PendingCount; uint32_t Backlog; SYS_RIGHTS PeerProcessRights; EVENT* WaitEvent; } PORT_LISTENER_STATE; typedef struct PORT_ENDPOINT_STATE { PORT_CONNECTION* Connection; uint32_t Side; EVENT* WaitEvent; EVENT* SpaceEvent; LIST_ENTRY Incoming; uint32_t IncomingCount; uint32_t IncomingBytes; uint32_t IncomingHandles; LIST_ENTRY WaitingReceivers; } PORT_ENDPOINT_STATE; struct PORT { OBJECT Header; uint32_t Kind; union { PORT_LISTENER_STATE Listener; PORT_ENDPOINT_STATE Endpoint; } u; }; static KE_ID_ALLOCATOR gPortMessageIds = KE_ID_ALLOCATOR_INIT(1u); typedef struct PORT_COUNTERS { volatile int64_t ConnectionsCreated; volatile int64_t DirectDeliveries; volatile int64_t DirectBytes; volatile int64_t QueuedMessages; volatile int64_t QueuedBytes; volatile int64_t QueuedDeliveries; volatile int64_t RepliesCompleted; volatile int64_t Disconnects; volatile int64_t ProcessQuotaRejects; } PORT_COUNTERS; static PORT_COUNTERS gPortCounters; // Reads one 64-bit diagnostic counter atomically without changing it. static uint64_t KiPortReadCounter(volatile int64_t* counter) { return (uint64_t)NtlAtomicLoad64(counter); } // Copies current Port transport diagnostics for the kernel stats syscall. void KePortQueryStats(KE_PORT_STATS* statsOut) { if (!statsOut) return; statsOut->ConnectionsCreated = KiPortReadCounter(&gPortCounters.ConnectionsCreated); statsOut->DirectDeliveries = KiPortReadCounter(&gPortCounters.DirectDeliveries); statsOut->DirectBytes = KiPortReadCounter(&gPortCounters.DirectBytes); statsOut->QueuedMessages = KiPortReadCounter(&gPortCounters.QueuedMessages); statsOut->QueuedBytes = KiPortReadCounter(&gPortCounters.QueuedBytes); statsOut->QueuedDeliveries = KiPortReadCounter(&gPortCounters.QueuedDeliveries); statsOut->RepliesCompleted = KiPortReadCounter(&gPortCounters.RepliesCompleted); statsOut->Disconnects = KiPortReadCounter(&gPortCounters.Disconnects); statsOut->ProcessQuotaRejects = KiPortReadCounter(&gPortCounters.ProcessQuotaRejects); } // Returns the complete capability rights granted to a newly created endpoint handle. static SYS_RIGHTS KiPortEndpointRights(bool openPeerProcess) { SYS_RIGHTS rights = SYS_RIGHT_MANAGE | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_PORT_RIGHT_SEND | SYS_PORT_RIGHT_RECEIVE | SYS_PORT_RIGHT_CALL | SYS_PORT_RIGHT_REPLY | SYS_PORT_RIGHT_DISCONNECT; if (openPeerProcess) rights |= SYS_PORT_RIGHT_OPEN_PEER_PROCESS; return rights; } // Returns one process security identity for kernel-stamped IPC metadata. static uint32_t KiPortSecurityId(const PROCESS* process) { return process && process->SecurityContext ? process->SecurityContext->SecurityId : 0u; } // Charges one process for the kernel resources retained by a submitted Port message. static KSTATUS KiPortChargeProcess(PROCESS* process, uint32_t payloadBytes, uint32_t handleCount) { if (!process || payloadBytes > SYS_PORT_MAX_INLINE_BYTES || handleCount > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER; if (!ObTryReferenceObject(&process->Header)) return NTLSTATUS_OBJECT_DEAD; uintptr_t flags = KeAcquireSpinlock(&process->PortQuotaLock); if (process->PortOutstandingMessages >= PORT_PROCESS_MESSAGE_LIMIT || payloadBytes > PORT_PROCESS_BYTE_LIMIT - process->PortOutstandingBytes || handleCount > PORT_PROCESS_HANDLE_LIMIT - process->PortOutstandingHandles) { KeReleaseSpinlock(&process->PortQuotaLock, flags); ObDereferenceObject(&process->Header); NtlAtomicIncrement64(&gPortCounters.ProcessQuotaRejects); return NTLSTATUS_LIMIT_REACHED; } process->PortOutstandingMessages++; process->PortOutstandingBytes += payloadBytes; process->PortOutstandingHandles += handleCount; KeReleaseSpinlock(&process->PortQuotaLock, flags); return NTLSTATUS_SUCCESS; } // Releases one process Port quota charge after delivery, cancellation, or disconnect. static void KiPortReleaseProcessCharge(PROCESS* process, uint32_t payloadBytes, uint32_t handleCount) { if (!process) return; uintptr_t flags = KeAcquireSpinlock(&process->PortQuotaLock); if (process->PortOutstandingMessages == 0 || process->PortOutstandingBytes < payloadBytes || process->PortOutstandingHandles < handleCount) { KeReleaseSpinlock(&process->PortQuotaLock, flags); KePanic(KE_PANIC_CODE_GENERIC, "port: process quota accounting underflow pid=%u\n", process->ProcessId); } process->PortOutstandingMessages--; process->PortOutstandingBytes -= payloadBytes; process->PortOutstandingHandles -= handleCount; KeReleaseSpinlock(&process->PortQuotaLock, flags); ObDereferenceObject(&process->Header); } // Charges one caller for a retained synchronous call context and locked reply buffer. static KSTATUS KiPortChargeCall(PROCESS* process) { if (!process) return NTLSTATUS_INVALID_PARAMETER; if (!ObTryReferenceObject(&process->Header)) return NTLSTATUS_OBJECT_DEAD; uintptr_t flags = KeAcquireSpinlock(&process->PortQuotaLock); if (process->PortOutstandingCalls >= PORT_PROCESS_CALL_LIMIT) { KeReleaseSpinlock(&process->PortQuotaLock, flags); ObDereferenceObject(&process->Header); NtlAtomicIncrement64(&gPortCounters.ProcessQuotaRejects); return NTLSTATUS_LIMIT_REACHED; } process->PortOutstandingCalls++; KeReleaseSpinlock(&process->PortQuotaLock, flags); return NTLSTATUS_SUCCESS; } // Releases a retained synchronous call quota charge and its process reference. static void KiPortReleaseCallCharge(PROCESS* process) { if (!process) return; uintptr_t flags = KeAcquireSpinlock(&process->PortQuotaLock); if (process->PortOutstandingCalls == 0) { KeReleaseSpinlock(&process->PortQuotaLock, flags); KePanic(KE_PANIC_CODE_GENERIC, "port: process call quota underflow pid=%u\n", process->ProcessId); } process->PortOutstandingCalls--; KeReleaseSpinlock(&process->PortQuotaLock, flags); ObDereferenceObject(&process->Header); } // Drops one shared connection reference after an endpoint object releases it. static void KiPortDereferenceConnection(PORT_CONNECTION* connection) { if (!connection) return; int32_t refs = NtlAtomicDecrement32(&connection->RefCount); if (refs > 0) return; if (refs < 0) { KePanic(KE_PANIC_CODE_GENERIC, "port: connection ref underflow connection=0x%016llX refs=%ld\n", (uint64_t)(uintptr_t)connection, refs); } if (!LIST_IS_EMPTY(&connection->Calls)) { KePanic(KE_PANIC_CODE_GENERIC, "port: destroying connection with pending calls connection=0x%016llX\n", (uint64_t)(uintptr_t)connection); } for (uint32_t side = 0; side < 2u; ++side) { if (connection->PeerProcesses[side]) { ObDereferenceObject(&connection->PeerProcesses[side]->Header); connection->PeerProcesses[side] = (struct PROCESS*)0; } } MmFreePool(connection); } // Releases one synchronous-call reference and destroys the context after the last owner leaves. static void KiPortReleaseCall(PORT_CALL* call); // Releases every capability capture and payload buffer owned by one queued message. static void KiPortFreeMessage(PORT_MESSAGE* message) { if (!message) return; for (uint32_t i = 0; i < message->CapturedCount; ++i) { if (message->Captured[i]) { KeReleaseCapturedHandle(message->Captured[i]); message->Captured[i] = (struct KE_CAPTURED_HANDLE*)0; } } if (message->Payload) MmFreePool(message->Payload); if (message->Call) { KiPortReleaseCall(message->Call); message->Call = (PORT_CALL*)0; } if (message->AccountingProcess) { KiPortReleaseProcessCharge(message->AccountingProcess, message->AccountedBytes, message->AccountedHandles); message->AccountingProcess = (PROCESS*)0; } MmFreePool(message); } // Closes a batch of handles that were materialized but could not be returned to the receiver. static void KiPortCloseHandles(PROCESS* process, SYS_HANDLE* handles, uint32_t count) { if (!process || !handles) return; for (uint32_t i = 0; i < count; ++i) { if (handles[i] != SYS_HANDLE_INVALID) { (void)KeCloseHandle(process, handles[i]); handles[i] = SYS_HANDLE_INVALID; } } } // Captures user-specified handles as kernel capability grants for later delivery. static KSTATUS KiPortCaptureHandles(PROCESS* sourceProcess, const SYS_PORT_HANDLE_TRANSFER* transfers, uint32_t count, struct KE_CAPTURED_HANDLE** captured) { if (count == 0) return NTLSTATUS_SUCCESS; if (!sourceProcess || !transfers || !captured || count > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER; for (uint32_t i = 0; i < count; ++i) captured[i] = (struct KE_CAPTURED_HANDLE*)0; for (uint32_t i = 0; i < count; ++i) { uint32_t flags = 0; if (transfers[i].Flags & SYS_PORT_TRANSFER_FLAG_SAME_RIGHTS) flags |= KE_HANDLE_TRANSFER_FLAG_SAME_RIGHTS; if (transfers[i].Flags & SYS_PORT_TRANSFER_FLAG_DERIVE) flags |= KE_HANDLE_TRANSFER_FLAG_DERIVE; if (transfers[i].Flags & ~(SYS_PORT_TRANSFER_FLAG_SAME_RIGHTS | SYS_PORT_TRANSFER_FLAG_DERIVE)) { for (uint32_t j = 0; j < i; ++j) KeReleaseCapturedHandle(captured[j]); return NTLSTATUS_INVALID_PARAMETER; } KSTATUS status = KeCaptureHandleForTransfer(sourceProcess, transfers[i].Handle, transfers[i].DesiredRights, flags, &captured[i]); if (!KSUCCESS(status)) { for (uint32_t j = 0; j < i; ++j) KeReleaseCapturedHandle(captured[j]); return status; } } return NTLSTATUS_SUCCESS; } // Allocates one message header and captures its monotonic identity and sender metadata. static KSTATUS KiPortCreateMessage(PROCESS* senderProcess, THREAD* senderThread, uint32_t type, uint32_t messageId, uint32_t payloadLength, const SYS_PORT_HANDLE_TRANSFER* transfers, uint32_t transferCount, PORT_CALL* call, PORT_MESSAGE** messageOut) { if (!senderProcess || !senderThread || !messageOut) return NTLSTATUS_INVALID_PARAMETER; *messageOut = (PORT_MESSAGE*)0; if (payloadLength > SYS_PORT_MAX_INLINE_BYTES || transferCount > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER; KSTATUS quotaStatus = KiPortChargeProcess(senderProcess, payloadLength, transferCount); if (!KSUCCESS(quotaStatus)) return quotaStatus; PORT_MESSAGE* message = (PORT_MESSAGE*)MmAllocatePoolTag(sizeof(PORT_MESSAGE), MM_POOL_TAG('p', 'm', 's', 'g')); if (!message) { KiPortReleaseProcessCharge(senderProcess, payloadLength, transferCount); return NTLSTATUS_NO_MEMORY; } memset(message, 0, sizeof(*message)); LIST_INIT(&message->Link); message->AccountingProcess = senderProcess; message->AccountedBytes = payloadLength; message->AccountedHandles = transferCount; if (messageId == 0) { KSTATUS status = KeAllocateId32(&gPortMessageIds, &messageId); if (!KSUCCESS(status)) { KiPortFreeMessage(message); return status; } } message->Info.Size = sizeof(message->Info); message->Info.MessageId = messageId; message->Info.Type = type; message->Info.PayloadLength = payloadLength; message->Info.SenderProcessId = senderProcess->ProcessId; message->Info.SenderThreadId = senderThread->ThreadId; message->Info.SenderSecurityId = KiPortSecurityId(senderProcess); message->CapturedCount = transferCount; message->Info.HandleCount = transferCount; message->Call = call; if (call) NtlAtomicIncrement32(&call->RefCount); KSTATUS status = KiPortCaptureHandles(senderProcess, transfers, transferCount, message->Captured); if (!KSUCCESS(status)) { KiPortFreeMessage(message); return status; } *messageOut = message; return NTLSTATUS_SUCCESS; } // Copies the sender payload into kernel-owned storage when immediate rendezvous is unavailable. static KSTATUS KiPortSnapshotPayload(PORT_MESSAGE* message, const void* payload, uint32_t payloadLength) { if (!message) return NTLSTATUS_INVALID_PARAMETER; if (message->Payload) return NTLSTATUS_SUCCESS; if (payloadLength == 0) { message->Info.PayloadLength = 0; return NTLSTATUS_SUCCESS; } if (!payload || payloadLength > SYS_PORT_MAX_INLINE_BYTES) return NTLSTATUS_INVALID_PARAMETER; void* copy = MmAllocatePoolTag(payloadLength, MM_POOL_TAG('p', 'd', 'a', 't')); if (!copy) return NTLSTATUS_NO_MEMORY; KSTATUS status = CopyFromUser(copy, payload, payloadLength); if (!KSUCCESS(status)) { MmFreePool(copy); return status; } message->Payload = copy; message->Info.PayloadLength = payloadLength; return NTLSTATUS_SUCCESS; } // Updates endpoint queue events after one message is removed. static void KiPortNoteDequeuedLocked(PORT* endpoint, PORT_MESSAGE* message) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !message) return; PORT_ENDPOINT_STATE* state = &endpoint->u.Endpoint; if (state->IncomingCount == 0 || state->IncomingBytes < message->Info.PayloadLength || state->IncomingHandles < message->CapturedCount) { KePanic(KE_PANIC_CODE_GENERIC, "port: queue accounting underflow\n"); } state->IncomingCount--; state->IncomingBytes -= message->Info.PayloadLength; state->IncomingHandles -= message->CapturedCount; KeSignalEvent(state->SpaceEvent); if (state->IncomingCount == 0 && !state->Connection->Disconnected[1u - state->Side]) KeClearEvent(state->WaitEvent); } // Returns whether one queued message can fit within the peer endpoint quotas. static bool KiPortMessageFitsLocked(const PORT* endpoint, const PORT_MESSAGE* message) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !message) return false; const PORT_ENDPOINT_STATE* state = &endpoint->u.Endpoint; if (state->IncomingCount >= SYS_PORT_QUEUE_MESSAGE_LIMIT) return false; if (message->Info.PayloadLength > SYS_PORT_QUEUE_BYTE_LIMIT - state->IncomingBytes) return false; if (message->CapturedCount > SYS_PORT_QUEUE_HANDLE_LIMIT - state->IncomingHandles) return false; return true; } // Completes a direct receive rendezvous after the connection lock has claimed the waiting receiver. static KSTATUS KiPortDeliverDirect(PORT* targetEndpoint, PORT_RECEIVE_WAIT* receiver, PORT_MESSAGE* message, PROCESS* senderProcess, const void* sourcePayload) { if (!targetEndpoint || !receiver || !message || !senderProcess) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NTLSTATUS_SUCCESS; SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES] = {0}; uint32_t handleCount = message->CapturedCount; if (handleCount) { status = KeMaterializeCapturedHandles(receiver->Process, message->Captured, handleCount, handles); } if (KSUCCESS(status) && message->Info.PayloadLength) { status = MmCopyUserToUser(senderProcess, sourcePayload, receiver->Process, receiver->Payload, message->Info.PayloadLength); } if (!KSUCCESS(status)) { KiPortCloseHandles(receiver->Process, handles, handleCount); receiver->HandleCount = 0; } else { memcpy(receiver->Handles, handles, sizeof(SYS_HANDLE) * handleCount); receiver->HandleCount = handleCount; } receiver->Info = message->Info; receiver->Status = status; if (KSUCCESS(status)) { NtlAtomicIncrement64(&gPortCounters.DirectDeliveries); NtlAtomicExchangeAdd64(&gPortCounters.DirectBytes, (int64_t)message->Info.PayloadLength); } NtlAtomicExchange32(&receiver->Completed, 1); KeSignalEvent(receiver->CompletionEvent); return status; } // Completes one synchronous call with an error that does not include a reply payload. static void KiPortCompleteCallError(PORT_CALL* call, KSTATUS status) { if (!call) return; call->Status = status; call->ReplyHandleCount = 0; NtlAtomicExchange32(&call->State, PORT_CALL_COMPLETED); KeSignalEvent(call->CompletionEvent); } // Removes one queued request belonging to a call while the connection lock is held. static PORT_MESSAGE* KiPortRemoveQueuedCallLocked(PORT_CONNECTION* connection, PORT_CALL* call) { if (!connection || !call) return (PORT_MESSAGE*)0; uint32_t targetSide = 1u - call->CallerSide; PORT* endpoint = connection->Endpoints[targetSide]; if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT) return (PORT_MESSAGE*)0; LIST_ENTRY* head = &endpoint->u.Endpoint.Incoming; for (LIST_ENTRY* link = head->Fwd; link != head; link = link->Fwd) { PORT_MESSAGE* message = CONTAINING_RECORD(link, PORT_MESSAGE, Link); if (message->Call == call) { KeListRemoveChecked(&message->Link); KiPortNoteDequeuedLocked(endpoint, message); return message; } } return (PORT_MESSAGE*)0; } // Disconnects one endpoint side, drains undelivered messages, and completes pending calls. static void KiPortMarkDisconnected(PORT* endpoint, bool removeEndpointPointer) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT) return; PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; if (!connection) return; LIST_ENTRY abandonedMessages; LIST_ENTRY completedCalls; LIST_INIT(&abandonedMessages); LIST_INIT(&completedCalls); uint32_t side = endpoint->u.Endpoint.Side; uintptr_t flags = KeAcquireSpinlock(&connection->Lock); if (!connection->Disconnected[side]) { connection->Disconnected[side] = 1; NtlAtomicIncrement64(&gPortCounters.Disconnects); } if (removeEndpointPointer && connection->Endpoints[side] == endpoint) connection->Endpoints[side] = (PORT*)0; while (!LIST_IS_EMPTY(&endpoint->u.Endpoint.WaitingReceivers)) { LIST_ENTRY* link = KeListPopHeadChecked(&endpoint->u.Endpoint.WaitingReceivers); PORT_RECEIVE_WAIT* waiter = CONTAINING_RECORD(link, PORT_RECEIVE_WAIT, Link); if (NtlAtomicCompareExchange32(&waiter->Claimed, 1, 0) == 0) { waiter->Status = NTLSTATUS_PORT_DISCONNECTED; NtlAtomicExchange32(&waiter->Completed, 1); KeSignalEvent(waiter->CompletionEvent); } } while (!LIST_IS_EMPTY(&endpoint->u.Endpoint.Incoming)) { LIST_ENTRY* link = KeListPopHeadChecked(&endpoint->u.Endpoint.Incoming); PORT_MESSAGE* message = CONTAINING_RECORD(link, PORT_MESSAGE, Link); if (endpoint->u.Endpoint.IncomingCount) endpoint->u.Endpoint.IncomingCount--; if (endpoint->u.Endpoint.IncomingBytes >= message->Info.PayloadLength) endpoint->u.Endpoint.IncomingBytes -= message->Info.PayloadLength; if (endpoint->u.Endpoint.IncomingHandles >= message->CapturedCount) endpoint->u.Endpoint.IncomingHandles -= message->CapturedCount; KeListInsertTailChecked(&abandonedMessages, &message->Link); } LIST_ENTRY* callHead = &connection->Calls; LIST_ENTRY* callLink = callHead->Fwd; while (callLink != callHead) { PORT_CALL* call = CONTAINING_RECORD(callLink, PORT_CALL, Link); callLink = callLink->Fwd; int32_t state = NtlAtomicLoad32(&call->State); if (state == PORT_CALL_REPLYING || state == PORT_CALL_COMPLETED || state == PORT_CALL_CANCELLED) continue; PORT_MESSAGE* queuedMessage = KiPortRemoveQueuedCallLocked(connection, call); if (queuedMessage) KeListInsertTailChecked(&abandonedMessages, &queuedMessage->Link); KeListRemoveChecked(&call->Link); call->Status = NTLSTATUS_PORT_DISCONNECTED; NtlAtomicExchange32(&call->State, PORT_CALL_COMPLETED); KeListInsertTailChecked(&completedCalls, &call->Link); } PORT* peer = connection->Endpoints[1u - side]; if (peer) { KeSignalEvent(peer->u.Endpoint.WaitEvent); KeSignalEvent(peer->u.Endpoint.SpaceEvent); } KeSignalEvent(endpoint->u.Endpoint.WaitEvent); KeSignalEvent(endpoint->u.Endpoint.SpaceEvent); KeReleaseSpinlock(&connection->Lock, flags); while (!LIST_IS_EMPTY(&abandonedMessages)) { LIST_ENTRY* link = KeListPopHeadChecked(&abandonedMessages); PORT_MESSAGE* message = CONTAINING_RECORD(link, PORT_MESSAGE, Link); KiPortFreeMessage(message); } while (!LIST_IS_EMPTY(&completedCalls)) { LIST_ENTRY* link = KeListPopHeadChecked(&completedCalls); PORT_CALL* call = CONTAINING_RECORD(link, PORT_CALL, Link); KeSignalEvent(call->CompletionEvent); } } // Destroys either a listener or endpoint Port object. static void KiPortDestroy(OBJECT* object) { PORT* port = (PORT*)object; if (!port) return; if (port->Kind == PORT_KIND_LISTENER) { KeSystemServiceUnregisterListener(port); while (true) { PORT_PENDING_CONNECTION* pending = (PORT_PENDING_CONNECTION*)0; uintptr_t flags = KeAcquireSpinlock(&port->u.Listener.Lock); LIST_ENTRY* link = KeListPopHeadChecked(&port->u.Listener.Pending); if (link) { pending = CONTAINING_RECORD(link, PORT_PENDING_CONNECTION, Link); if (port->u.Listener.PendingCount) port->u.Listener.PendingCount--; } KeReleaseSpinlock(&port->u.Listener.Lock, flags); if (!pending) break; ObDereferenceObject((OBJECT*)pending->ServerEndpoint); MmFreePool(pending); } if (port->u.Listener.WaitEvent) ObDereferenceObject(&port->u.Listener.WaitEvent->Header); return; } if (port->Kind == PORT_KIND_ENDPOINT) { KiPortMarkDisconnected(port, true); if (port->u.Endpoint.WaitEvent) ObDereferenceObject(&port->u.Endpoint.WaitEvent->Header); if (port->u.Endpoint.SpaceEvent) ObDereferenceObject(&port->u.Endpoint.SpaceEvent->Header); KiPortDereferenceConnection(port->u.Endpoint.Connection); port->u.Endpoint.Connection = (PORT_CONNECTION*)0; } } // Allocates one endpoint object attached to a shared connection side. static KSTATUS KiPortCreateEndpoint(PORT_CONNECTION* connection, uint32_t side, PORT** endpointOut) { if (!connection || !endpointOut || side > 1u) return NTLSTATUS_INVALID_PARAMETER; *endpointOut = (PORT*)0; PORT* endpoint = (PORT*)ObAllocateObject(ObjectTypePort, sizeof(PORT)); if (!endpoint) return NTLSTATUS_NO_MEMORY; endpoint->Header.Destroy = KiPortDestroy; endpoint->Kind = PORT_KIND_ENDPOINT; endpoint->u.Endpoint.Connection = connection; endpoint->u.Endpoint.Side = side; LIST_INIT(&endpoint->u.Endpoint.Incoming); LIST_INIT(&endpoint->u.Endpoint.WaitingReceivers); KSTATUS status = KeEventCreate(EventManualReset, 0, &endpoint->u.Endpoint.WaitEvent); if (!KSUCCESS(status)) { endpoint->u.Endpoint.Connection = (PORT_CONNECTION*)0; ObDereferenceObject(&endpoint->Header); return status; } status = KeEventCreate(EventManualReset, 1, &endpoint->u.Endpoint.SpaceEvent); if (!KSUCCESS(status)) { endpoint->u.Endpoint.Connection = (PORT_CONNECTION*)0; ObDereferenceObject(&endpoint->Header); return status; } *endpointOut = endpoint; return NTLSTATUS_SUCCESS; } // Allocates one paired connection and its two endpoint objects. static KSTATUS KiPortCreateConnection(PROCESS* clientProcess, SYS_RIGHTS serverPeerProcessRights, PORT** clientOut, PORT** serverOut) { if (!clientProcess || !clientOut || !serverOut) return NTLSTATUS_INVALID_PARAMETER; *clientOut = (PORT*)0; *serverOut = (PORT*)0; PORT_CONNECTION* connection = (PORT_CONNECTION*)MmAllocatePoolTag(sizeof(PORT_CONNECTION), MM_POOL_TAG('p', 'c', 'o', 'n')); if (!connection) return NTLSTATUS_NO_MEMORY; memset(connection, 0, sizeof(*connection)); connection->RefCount = 2; connection->Lock = (SPINLOCK)SPINLOCK_INIT; LIST_INIT(&connection->Calls); if (serverPeerProcessRights != 0) { if (!ObTryReferenceObject(&clientProcess->Header)) { connection->RefCount = 1; KiPortDereferenceConnection(connection); return NTLSTATUS_OBJECT_DEAD; } connection->PeerProcesses[1] = clientProcess; connection->PeerProcessRights[1] = serverPeerProcessRights; } PORT* client = (PORT*)0; PORT* server = (PORT*)0; KSTATUS status = KiPortCreateEndpoint(connection, 0, &client); if (!KSUCCESS(status)) { connection->RefCount = 1; KiPortDereferenceConnection(connection); return status; } status = KiPortCreateEndpoint(connection, 1, &server); if (!KSUCCESS(status)) { connection->RefCount = 1; ObDereferenceObject(&client->Header); return status; } uintptr_t flags = KeAcquireSpinlock(&connection->Lock); connection->Endpoints[0] = client; connection->Endpoints[1] = server; KeReleaseSpinlock(&connection->Lock, flags); *clientOut = client; *serverOut = server; return NTLSTATUS_SUCCESS; } // Creates one bounded listener Port with a waitable pending-connection event. KSTATUS KePortCreate(uint32_t backlog, SYS_RIGHTS peerProcessRights, PORT** portOut) { if (!portOut) return NTLSTATUS_INVALID_PARAMETER; *portOut = (PORT*)0; if ((peerProcessRights & ~KeGetValidHandleRights(ObjectTypeProcess)) != 0) return NTLSTATUS_INVALID_PARAMETER; if (backlog == 0) backlog = SYS_PORT_DEFAULT_BACKLOG; if (backlog > SYS_PORT_MAX_BACKLOG) return NTLSTATUS_INVALID_PARAMETER; PORT* port = (PORT*)ObAllocateObject(ObjectTypePort, sizeof(PORT)); if (!port) return NTLSTATUS_NO_MEMORY; port->Header.Destroy = KiPortDestroy; port->Kind = PORT_KIND_LISTENER; port->u.Listener.Lock = (SPINLOCK)SPINLOCK_INIT; port->u.Listener.Backlog = backlog; port->u.Listener.PeerProcessRights = peerProcessRights; LIST_INIT(&port->u.Listener.Pending); KSTATUS status = KeEventCreate(EventManualReset, 0, &port->u.Listener.WaitEvent); if (!KSUCCESS(status)) { ObDereferenceObject(&port->Header); return status; } *portOut = port; return NTLSTATUS_SUCCESS; } // Returns whether one Port object is a listener. bool KePortIsListener(const PORT* port) { return port && port->Kind == PORT_KIND_LISTENER; } // Returns the event whose signaled state represents listener or endpoint readiness. EVENT* KePortWaitEvent(PORT* port) { if (!port) return (EVENT*)0; if (port->Kind == PORT_KIND_LISTENER) return port->u.Listener.WaitEvent; if (port->Kind == PORT_KIND_ENDPOINT) return port->u.Endpoint.WaitEvent; return (EVENT*)0; } // Connects one client process to a listener by queueing the server endpoint for later acceptance. KSTATUS KePortConnect(PORT* listener, PROCESS* clientProcess, THREAD* clientThread, SYS_HANDLE* endpointHandleOut) { if (!listener || listener->Kind != PORT_KIND_LISTENER || !clientProcess || !clientThread || !endpointHandleOut) return NTLSTATUS_INVALID_PARAMETER; *endpointHandleOut = SYS_HANDLE_INVALID; uintptr_t flags = KeAcquireSpinlock(&listener->u.Listener.Lock); if (listener->u.Listener.PendingCount >= listener->u.Listener.Backlog) { KeReleaseSpinlock(&listener->u.Listener.Lock, flags); return NTLSTATUS_LIMIT_REACHED; } KeReleaseSpinlock(&listener->u.Listener.Lock, flags); PORT* clientEndpoint = (PORT*)0; PORT* serverEndpoint = (PORT*)0; KSTATUS status = KiPortCreateConnection(clientProcess, listener->u.Listener.PeerProcessRights, &clientEndpoint, &serverEndpoint); if (!KSUCCESS(status)) return status; SYS_HANDLE clientHandle = SYS_HANDLE_INVALID; status = KeCreateHandle(clientProcess, &clientEndpoint->Header, KiPortEndpointRights(false), &clientHandle); if (!KSUCCESS(status)) { ObDereferenceObject(&serverEndpoint->Header); ObDereferenceObject(&clientEndpoint->Header); return status; } ObDereferenceObject(&clientEndpoint->Header); PORT_PENDING_CONNECTION* pending = (PORT_PENDING_CONNECTION*)MmAllocatePoolTag(sizeof(*pending), MM_POOL_TAG('p', 'c', 'n', 'q')); if (!pending) { (void)KeCloseHandle(clientProcess, clientHandle); ObDereferenceObject(&serverEndpoint->Header); return NTLSTATUS_NO_MEMORY; } memset(pending, 0, sizeof(*pending)); LIST_INIT(&pending->Link); pending->ServerEndpoint = serverEndpoint; pending->Info.Size = sizeof(pending->Info); pending->Info.ClientProcessId = clientProcess->ProcessId; pending->Info.ClientThreadId = clientThread->ThreadId; pending->Info.ClientSecurityId = KiPortSecurityId(clientProcess); flags = KeAcquireSpinlock(&listener->u.Listener.Lock); if (listener->u.Listener.PendingCount >= listener->u.Listener.Backlog) { KeReleaseSpinlock(&listener->u.Listener.Lock, flags); MmFreePool(pending); ObDereferenceObject(&serverEndpoint->Header); (void)KeCloseHandle(clientProcess, clientHandle); return NTLSTATUS_LIMIT_REACHED; } KeListInsertTailChecked(&listener->u.Listener.Pending, &pending->Link); listener->u.Listener.PendingCount++; NtlAtomicIncrement64(&gPortCounters.ConnectionsCreated); KeSignalEvent(listener->u.Listener.WaitEvent); KeReleaseSpinlock(&listener->u.Listener.Lock, flags); *endpointHandleOut = clientHandle; return NTLSTATUS_SUCCESS; } // Accepts the oldest queued connection, waiting on the listener event when requested. KSTATUS KePortAccept(PORT* listener, PROCESS* serverProcess, uint64_t timeoutMilliseconds, SYS_HANDLE* endpointHandleOut, SYS_PORT_CONNECTION_INFO* infoOut) { if (!listener || listener->Kind != PORT_KIND_LISTENER || !serverProcess || !endpointHandleOut) return NTLSTATUS_INVALID_PARAMETER; *endpointHandleOut = SYS_HANDLE_INVALID; uint64_t deadline = 0; if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) deadline = KeMonotonicDeadlineAfterMilliseconds(timeoutMilliseconds); while (true) { PORT_PENDING_CONNECTION* pending = (PORT_PENDING_CONNECTION*)0; uintptr_t flags = KeAcquireSpinlock(&listener->u.Listener.Lock); LIST_ENTRY* link = KeListPopHeadChecked(&listener->u.Listener.Pending); if (link) { pending = CONTAINING_RECORD(link, PORT_PENDING_CONNECTION, Link); if (listener->u.Listener.PendingCount == 0) { KeReleaseSpinlock(&listener->u.Listener.Lock, flags); KePanic(KE_PANIC_CODE_GENERIC, "port: listener pending count underflow\n"); } listener->u.Listener.PendingCount--; } if (listener->u.Listener.PendingCount == 0) KeClearEvent(listener->u.Listener.WaitEvent); KeReleaseSpinlock(&listener->u.Listener.Lock, flags); if (pending) { SYS_HANDLE handle = SYS_HANDLE_INVALID; KSTATUS status = KeCreateHandle(serverProcess, &pending->ServerEndpoint->Header, KiPortEndpointRights(listener->u.Listener.PeerProcessRights != 0), &handle); if (KSUCCESS(status)) { if (infoOut) *infoOut = pending->Info; *endpointHandleOut = handle; } ObDereferenceObject(&pending->ServerEndpoint->Header); MmFreePool(pending); return status; } if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK; uint64_t wait = timeoutMilliseconds; if (timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) { wait = KeMonotonicMillisecondsUntil(deadline); if (wait == 0) return NTLSTATUS_TIMEOUT; } EVENT* events[1] = { listener->u.Listener.WaitEvent }; KSTATUS status = KeWaitForMultipleEvents(events, 1, 0, wait, (uint32_t*)0); if (!KSUCCESS(status)) return status; } } // Submits a prepared message to the peer, using direct rendezvous when a receiver is already waiting. // This function consumes the message on every return path; queued success transfers ownership to the peer queue. static KSTATUS KiPortSubmitMessage(PORT* endpoint, PROCESS* senderProcess, const void* sourcePayload, uint32_t payloadLength, PORT_MESSAGE* message, uint64_t timeoutMilliseconds) { if (!message) return NTLSTATUS_INVALID_PARAMETER; if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !senderProcess || payloadLength > SYS_PORT_MAX_INLINE_BYTES || (payloadLength && !sourcePayload)) { KiPortFreeMessage(message); return NTLSTATUS_INVALID_PARAMETER; } message->Info.PayloadLength = payloadLength; PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; uint32_t peerSide = 1u - endpoint->u.Endpoint.Side; uint64_t deadline = 0; if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) deadline = KeMonotonicDeadlineAfterMilliseconds(timeoutMilliseconds); bool snapshotted = false; while (true) { PORT* peer = (PORT*)0; PORT_RECEIVE_WAIT* receiver = (PORT_RECEIVE_WAIT*)0; uintptr_t flags = KeAcquireSpinlock(&connection->Lock); if (connection->Disconnected[endpoint->u.Endpoint.Side] || connection->Disconnected[peerSide] || !(peer = connection->Endpoints[peerSide])) { KeReleaseSpinlock(&connection->Lock, flags); KiPortFreeMessage(message); return NTLSTATUS_PORT_DISCONNECTED; } if (!snapshotted && peer->u.Endpoint.IncomingCount == 0) { LIST_ENTRY* waitHead = &peer->u.Endpoint.WaitingReceivers; for (LIST_ENTRY* link = waitHead->Fwd; link != waitHead; link = link->Fwd) { PORT_RECEIVE_WAIT* candidate = CONTAINING_RECORD(link, PORT_RECEIVE_WAIT, Link); if (payloadLength > candidate->PayloadCapacity || message->CapturedCount > candidate->HandleCapacity) continue; if (NtlAtomicCompareExchange32(&candidate->Claimed, 1, 0) != 0) continue; receiver = candidate; KeListRemoveChecked(&receiver->Link); break; } if (receiver) { if (message->Call) NtlAtomicExchange32(&message->Call->State, PORT_CALL_DELIVERED); if (!ObTryReferenceObject(&peer->Header)) { receiver->Claimed = 0; KeListInsertHeadChecked(&peer->u.Endpoint.WaitingReceivers, &receiver->Link); KeReleaseSpinlock(&connection->Lock, flags); KiPortFreeMessage(message); return NTLSTATUS_OBJECT_DEAD; } KeReleaseSpinlock(&connection->Lock, flags); KSTATUS status = KiPortDeliverDirect(peer, receiver, message, senderProcess, sourcePayload); ObDereferenceObject(&peer->Header); KiPortFreeMessage(message); return status; } } KeReleaseSpinlock(&connection->Lock, flags); if (!snapshotted) { KSTATUS status = KiPortSnapshotPayload(message, sourcePayload, payloadLength); if (!KSUCCESS(status)) { KiPortFreeMessage(message); return status; } snapshotted = true; } flags = KeAcquireSpinlock(&connection->Lock); if (connection->Disconnected[endpoint->u.Endpoint.Side] || connection->Disconnected[peerSide] || !(peer = connection->Endpoints[peerSide])) { KeReleaseSpinlock(&connection->Lock, flags); KiPortFreeMessage(message); return NTLSTATUS_PORT_DISCONNECTED; } if (KiPortMessageFitsLocked(peer, message)) { KeListInsertTailChecked(&peer->u.Endpoint.Incoming, &message->Link); peer->u.Endpoint.IncomingCount++; peer->u.Endpoint.IncomingBytes += message->Info.PayloadLength; peer->u.Endpoint.IncomingHandles += message->CapturedCount; NtlAtomicIncrement64(&gPortCounters.QueuedMessages); NtlAtomicExchangeAdd64(&gPortCounters.QueuedBytes, (int64_t)message->Info.PayloadLength); if (message->Call) NtlAtomicExchange32(&message->Call->State, PORT_CALL_PENDING); KeSignalEvent(peer->u.Endpoint.WaitEvent); if (peer->u.Endpoint.IncomingCount >= SYS_PORT_QUEUE_MESSAGE_LIMIT || peer->u.Endpoint.IncomingBytes >= SYS_PORT_QUEUE_BYTE_LIMIT || peer->u.Endpoint.IncomingHandles >= SYS_PORT_QUEUE_HANDLE_LIMIT) KeClearEvent(peer->u.Endpoint.SpaceEvent); KeReleaseSpinlock(&connection->Lock, flags); return NTLSTATUS_SUCCESS; } KeClearEvent(peer->u.Endpoint.SpaceEvent); EVENT* spaceEvent = peer->u.Endpoint.SpaceEvent; if (!ObTryReferenceObject(&spaceEvent->Header)) { KeReleaseSpinlock(&connection->Lock, flags); KiPortFreeMessage(message); return NTLSTATUS_OBJECT_DEAD; } KeReleaseSpinlock(&connection->Lock, flags); if (timeoutMilliseconds == 0) { ObDereferenceObject(&spaceEvent->Header); KiPortFreeMessage(message); return NTLSTATUS_WOULD_BLOCK; } uint64_t wait = timeoutMilliseconds; if (timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) { wait = KeMonotonicMillisecondsUntil(deadline); if (wait == 0) { ObDereferenceObject(&spaceEvent->Header); KiPortFreeMessage(message); return NTLSTATUS_TIMEOUT; } } EVENT* events[1] = { spaceEvent }; KSTATUS status = KeWaitForMultipleEvents(events, 1, 0, wait, (uint32_t*)0); ObDereferenceObject(&spaceEvent->Header); if (!KSUCCESS(status)) { KiPortFreeMessage(message); return status; } } } // Sends one one-way copied notification to the peer endpoint. KSTATUS KePortSend(PORT* endpoint, PROCESS* senderProcess, THREAD* senderThread, const void* payload, uint32_t payloadLength, const SYS_PORT_HANDLE_TRANSFER* transfers, uint32_t transferCount, uint64_t timeoutMilliseconds) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !senderProcess || !senderThread) return NTLSTATUS_INVALID_PARAMETER; PORT_MESSAGE* message = (PORT_MESSAGE*)0; KSTATUS status = KiPortCreateMessage(senderProcess, senderThread, SYS_PORT_MESSAGE_NOTIFICATION, 0, payloadLength, transfers, transferCount, (PORT_CALL*)0, &message); if (!KSUCCESS(status)) return status; return KiPortSubmitMessage(endpoint, senderProcess, payload, payloadLength, message, timeoutMilliseconds); } // Delivers one already-dequeued kernel-buffered message to the current receiver. static KSTATUS KiPortDeliverQueued(PORT* endpoint, PROCESS* receiverProcess, PORT_MESSAGE* message, void* payload, SYS_HANDLE* handlesOut, SYS_PORT_MESSAGE_INFO* infoOut) { if (!endpoint || !receiverProcess || !message || !infoOut) return NTLSTATUS_INVALID_PARAMETER; SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES] = {0}; KSTATUS status = NTLSTATUS_SUCCESS; if (message->CapturedCount) { status = KeMaterializeCapturedHandles(receiverProcess, message->Captured, message->CapturedCount, handles); } if (KSUCCESS(status) && message->Info.PayloadLength) status = CopyToUser(payload, message->Payload, message->Info.PayloadLength); if (!KSUCCESS(status)) { KiPortCloseHandles(receiverProcess, handles, message->CapturedCount); if (message->Call) { PORT_CALL* call = message->Call; PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; bool completeCall = false; uintptr_t flags = KeAcquireSpinlock(&connection->Lock); int32_t callState = NtlAtomicLoad32(&call->State); if (callState != PORT_CALL_CANCELLED && callState != PORT_CALL_COMPLETED) { if (call->Link.Fwd != &call->Link && call->Link.Bwd != &call->Link) KeListRemoveChecked(&call->Link); completeCall = true; } KeReleaseSpinlock(&connection->Lock, flags); if (completeCall) KiPortCompleteCallError(call, status); } return status; } if (message->CapturedCount) memcpy(handlesOut, handles, sizeof(SYS_HANDLE) * message->CapturedCount); *infoOut = message->Info; NtlAtomicIncrement64(&gPortCounters.QueuedDeliveries); return NTLSTATUS_SUCCESS; } // Receives one copied message or registers one direct-delivery rendezvous while blocked. KSTATUS KePortReceive(PORT* endpoint, PROCESS* receiverProcess, void* payload, uint32_t payloadCapacity, SYS_HANDLE* handlesOut, uint32_t handleCapacity, SYS_PORT_MESSAGE_INFO* infoOut, uint64_t timeoutMilliseconds) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !receiverProcess || !infoOut) return NTLSTATUS_INVALID_PARAMETER; if (payloadCapacity > SYS_PORT_MAX_INLINE_BYTES || handleCapacity > SYS_PORT_MAX_TRANSFER_HANDLES || (payloadCapacity && !payload) || (handleCapacity && !handlesOut)) return NTLSTATUS_INVALID_PARAMETER; PORT_RECEIVE_WAIT wait; memset(&wait, 0, sizeof(wait)); LIST_INIT(&wait.Link); wait.Process = receiverProcess; wait.Payload = payload; wait.PayloadCapacity = payloadCapacity; wait.HandleCapacity = handleCapacity; wait.Status = NTLSTATUS_PENDING; if (payloadCapacity) { KSTATUS status = MmLockUserBuffer(receiverProcess, payload, payloadCapacity, 1, &wait.LockedPayload); if (!KSUCCESS(status)) return status; } if (timeoutMilliseconds != 0) { KSTATUS status = KeEventCreate(EventAutoReset, 0, &wait.CompletionEvent); if (!KSUCCESS(status)) { if (wait.LockedPayload) MmUnlockUserBuffer(wait.LockedPayload); return status; } } PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; uint64_t deadline = 0; if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) deadline = KeMonotonicDeadlineAfterMilliseconds(timeoutMilliseconds); KSTATUS result = NTLSTATUS_FAILURE; while (true) { PORT_MESSAGE* message = (PORT_MESSAGE*)0; uintptr_t flags = KeAcquireSpinlock(&connection->Lock); if (!LIST_IS_EMPTY(&endpoint->u.Endpoint.Incoming)) { message = CONTAINING_RECORD(endpoint->u.Endpoint.Incoming.Fwd, PORT_MESSAGE, Link); if (message->Info.PayloadLength > payloadCapacity || message->CapturedCount > handleCapacity) { *infoOut = message->Info; KeReleaseSpinlock(&connection->Lock, flags); result = NTLSTATUS_BUFFER_TOO_SMALL; break; } KeListRemoveChecked(&message->Link); KiPortNoteDequeuedLocked(endpoint, message); if (message->Call) NtlAtomicExchange32(&message->Call->State, PORT_CALL_DELIVERED); KeReleaseSpinlock(&connection->Lock, flags); result = KiPortDeliverQueued(endpoint, receiverProcess, message, payload, handlesOut, infoOut); KiPortFreeMessage(message); break; } uint32_t peerSide = 1u - endpoint->u.Endpoint.Side; if (connection->Disconnected[endpoint->u.Endpoint.Side] || connection->Disconnected[peerSide]) { KeReleaseSpinlock(&connection->Lock, flags); result = NTLSTATUS_PORT_DISCONNECTED; break; } if (timeoutMilliseconds == 0) { KeReleaseSpinlock(&connection->Lock, flags); result = NTLSTATUS_WOULD_BLOCK; break; } if (wait.Link.Fwd == &wait.Link && wait.Link.Bwd == &wait.Link) KeListInsertTailChecked(&endpoint->u.Endpoint.WaitingReceivers, &wait.Link); KeClearEvent(endpoint->u.Endpoint.WaitEvent); KeReleaseSpinlock(&connection->Lock, flags); uint64_t waitMilliseconds = timeoutMilliseconds; if (timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) { waitMilliseconds = KeMonotonicMillisecondsUntil(deadline); if (waitMilliseconds == 0) waitMilliseconds = 1; } EVENT* events[2] = { wait.CompletionEvent, endpoint->u.Endpoint.WaitEvent }; KSTATUS waitStatus = KeWaitForMultipleEvents(events, 2, 0, waitMilliseconds, (uint32_t*)0); flags = KeAcquireSpinlock(&connection->Lock); if (NtlAtomicLoad32(&wait.Completed) != 0) { if (wait.Link.Fwd != &wait.Link || wait.Link.Bwd != &wait.Link) KeListRemoveChecked(&wait.Link); if (endpoint->u.Endpoint.IncomingCount == 0 && !connection->Disconnected[1u - endpoint->u.Endpoint.Side]) KeClearEvent(endpoint->u.Endpoint.WaitEvent); KeReleaseSpinlock(&connection->Lock, flags); result = wait.Status; if (KSUCCESS(result)) { *infoOut = wait.Info; if (wait.HandleCount) memcpy(handlesOut, wait.Handles, sizeof(SYS_HANDLE) * wait.HandleCount); } break; } if (NtlAtomicLoad32(&wait.Claimed) != 0) { KeReleaseSpinlock(&connection->Lock, flags); EVENT* claimedEvents[1] = { wait.CompletionEvent }; (void)KeWaitForMultipleEvents(claimedEvents, 1, 0, SYS_PORT_TIMEOUT_INFINITE, (uint32_t*)0); continue; } if (wait.Link.Fwd != &wait.Link || wait.Link.Bwd != &wait.Link) KeListRemoveChecked(&wait.Link); KeReleaseSpinlock(&connection->Lock, flags); if (!KSUCCESS(waitStatus)) { result = waitStatus; break; } if (timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE && KeMonotonicMillisecondsUntil(deadline) == 0) { result = NTLSTATUS_TIMEOUT; break; } } if (wait.CompletionEvent) ObDereferenceObject(&wait.CompletionEvent->Header); if (wait.LockedPayload) MmUnlockUserBuffer(wait.LockedPayload); return result; } // Creates one synchronous call context and holds caller resources until reply, timeout, or disconnect. static KSTATUS KiPortCreateCall(PORT* endpoint, PROCESS* callerProcess, void* replyPayload, uint32_t replyCapacity, uint32_t replyHandleCapacity, PORT_CALL** callOut) { if (!endpoint || !callerProcess || !callOut) return NTLSTATUS_INVALID_PARAMETER; *callOut = (PORT_CALL*)0; KSTATUS quotaStatus = KiPortChargeCall(callerProcess); if (!KSUCCESS(quotaStatus)) return quotaStatus; PORT_CALL* call = (PORT_CALL*)MmAllocatePoolTag(sizeof(PORT_CALL), MM_POOL_TAG('p', 'c', 'a', 'l')); if (!call) { KiPortReleaseCallCharge(callerProcess); return NTLSTATUS_NO_MEMORY; } memset(call, 0, sizeof(*call)); LIST_INIT(&call->Link); call->RefCount = 1; call->Connection = endpoint->u.Endpoint.Connection; call->CallerSide = endpoint->u.Endpoint.Side; call->State = PORT_CALL_PENDING; call->CallerProcess = callerProcess; call->ReplyPayload = replyPayload; call->ReplyCapacity = replyCapacity; call->ReplyHandleCapacity = replyHandleCapacity; call->Status = NTLSTATUS_PENDING; KSTATUS status = KeAllocateId32(&gPortMessageIds, &call->MessageId); if (!KSUCCESS(status)) { KiPortReleaseCallCharge(callerProcess); MmFreePool(call); return status; } status = KeEventCreate(EventManualReset, 0, &call->CompletionEvent); if (!KSUCCESS(status)) { KiPortReleaseCallCharge(callerProcess); MmFreePool(call); return status; } if (replyCapacity) { status = MmLockUserBuffer(callerProcess, replyPayload, replyCapacity, 1, &call->LockedReplyPayload); if (!KSUCCESS(status)) { ObDereferenceObject(&call->CompletionEvent->Header); KiPortReleaseCallCharge(callerProcess); MmFreePool(call); return status; } } uintptr_t flags = KeAcquireSpinlock(&call->Connection->Lock); if (call->Connection->Disconnected[call->CallerSide] || call->Connection->Disconnected[1u - call->CallerSide]) { KeReleaseSpinlock(&call->Connection->Lock, flags); if (call->LockedReplyPayload) MmUnlockUserBuffer(call->LockedReplyPayload); ObDereferenceObject(&call->CompletionEvent->Header); KiPortReleaseCallCharge(callerProcess); MmFreePool(call); return NTLSTATUS_PORT_DISCONNECTED; } KeListInsertTailChecked(&call->Connection->Calls, &call->Link); KeReleaseSpinlock(&call->Connection->Lock, flags); *callOut = call; return NTLSTATUS_SUCCESS; } // Releases one synchronous-call reference and frees its retained resources after the final owner leaves. static void KiPortReleaseCall(PORT_CALL* call) { if (!call) return; int32_t refs = NtlAtomicDecrement32(&call->RefCount); if (refs > 0) return; if (refs < 0) { KePanic(KE_PANIC_CODE_GENERIC, "port: call ref underflow message=%u refs=%ld\n", call->MessageId, refs); } if (call->LockedReplyPayload) MmUnlockUserBuffer(call->LockedReplyPayload); if (call->CompletionEvent) ObDereferenceObject(&call->CompletionEvent->Header); if (call->CallerProcess) KiPortReleaseCallCharge(call->CallerProcess); MmFreePool(call); } // Performs one synchronous Port request and waits for exactly one matching reply. KSTATUS KePortCall(PORT* endpoint, PROCESS* callerProcess, THREAD* callerThread, const void* requestPayload, uint32_t requestLength, const SYS_PORT_HANDLE_TRANSFER* requestTransfers, uint32_t requestTransferCount, void* replyPayload, uint32_t replyCapacity, SYS_HANDLE* replyHandlesOut, uint32_t replyHandleCapacity, SYS_PORT_MESSAGE_INFO* replyInfoOut, uint64_t timeoutMilliseconds) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !callerProcess || !callerThread) return NTLSTATUS_INVALID_PARAMETER; if (timeoutMilliseconds == 0) return NTLSTATUS_WOULD_BLOCK; if (requestLength > SYS_PORT_MAX_INLINE_BYTES || replyCapacity > SYS_PORT_MAX_INLINE_BYTES || requestTransferCount > SYS_PORT_MAX_TRANSFER_HANDLES || replyHandleCapacity > SYS_PORT_MAX_TRANSFER_HANDLES || (requestLength && !requestPayload) || (replyCapacity && !replyPayload) || (replyHandleCapacity && !replyHandlesOut)) return NTLSTATUS_INVALID_PARAMETER; uint64_t deadline = 0; if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) deadline = KeMonotonicDeadlineAfterMilliseconds(timeoutMilliseconds); PORT_CALL* call = (PORT_CALL*)0; KSTATUS status = KiPortCreateCall(endpoint, callerProcess, replyPayload, replyCapacity, replyHandleCapacity, &call); if (!KSUCCESS(status)) return status; PORT_MESSAGE* message = (PORT_MESSAGE*)0; status = KiPortCreateMessage(callerProcess, callerThread, SYS_PORT_MESSAGE_REQUEST, call->MessageId, requestLength, requestTransfers, requestTransferCount, call, &message); if (!KSUCCESS(status)) { uintptr_t flags = KeAcquireSpinlock(&call->Connection->Lock); if (call->Link.Fwd != &call->Link && call->Link.Bwd != &call->Link) KeListRemoveChecked(&call->Link); KeReleaseSpinlock(&call->Connection->Lock, flags); KiPortReleaseCall(call); return status; } uint64_t submitTimeout = timeoutMilliseconds; if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) submitTimeout = KeMonotonicMillisecondsUntil(deadline); status = KiPortSubmitMessage(endpoint, callerProcess, requestPayload, requestLength, message, submitTimeout); if (!KSUCCESS(status)) { uintptr_t flags = KeAcquireSpinlock(&call->Connection->Lock); if (call->Link.Fwd != &call->Link && call->Link.Bwd != &call->Link) KeListRemoveChecked(&call->Link); KeReleaseSpinlock(&call->Connection->Lock, flags); KiPortReleaseCall(call); return status; } uint64_t replyWait = timeoutMilliseconds; if (timeoutMilliseconds != 0 && timeoutMilliseconds != SYS_PORT_TIMEOUT_INFINITE) replyWait = KeMonotonicMillisecondsUntil(deadline); EVENT* events[1] = { call->CompletionEvent }; KSTATUS waitStatus = KeWaitForMultipleEvents(events, 1, 0, replyWait, (uint32_t*)0); if (!KSUCCESS(waitStatus)) { PORT_MESSAGE* queuedMessage = (PORT_MESSAGE*)0; uintptr_t flags = KeAcquireSpinlock(&call->Connection->Lock); int32_t state = NtlAtomicLoad32(&call->State); if (state == PORT_CALL_REPLYING) { KeReleaseSpinlock(&call->Connection->Lock, flags); (void)KeWaitForMultipleEvents(events, 1, 0, SYS_PORT_TIMEOUT_INFINITE, (uint32_t*)0); } else if (state == PORT_CALL_COMPLETED) { KeReleaseSpinlock(&call->Connection->Lock, flags); } else { if (call->Link.Fwd != &call->Link && call->Link.Bwd != &call->Link) KeListRemoveChecked(&call->Link); NtlAtomicExchange32(&call->State, PORT_CALL_CANCELLED); queuedMessage = KiPortRemoveQueuedCallLocked(call->Connection, call); KeReleaseSpinlock(&call->Connection->Lock, flags); if (queuedMessage) KiPortFreeMessage(queuedMessage); KiPortReleaseCall(call); return waitStatus; } } status = call->Status; if (KSUCCESS(status)) { if (replyInfoOut) *replyInfoOut = call->ReplyInfo; if (call->ReplyHandleCount && replyHandlesOut) memcpy(replyHandlesOut, call->ReplyHandles, sizeof(SYS_HANDLE) * call->ReplyHandleCount); } else if (call->ReplyHandleCount) { KiPortCloseHandles(callerProcess, call->ReplyHandles, call->ReplyHandleCount); call->ReplyHandleCount = 0; } KiPortReleaseCall(call); return status; } // Replies to one delivered synchronous request and directly copies the reply into the blocked caller buffer. KSTATUS KePortReply(PORT* endpoint, PROCESS* senderProcess, THREAD* senderThread, uint32_t messageId, const void* payload, uint32_t payloadLength, const SYS_PORT_HANDLE_TRANSFER* transfers, uint32_t transferCount) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !senderProcess || !senderThread || messageId == 0) return NTLSTATUS_INVALID_PARAMETER; if (payloadLength > SYS_PORT_MAX_INLINE_BYTES || transferCount > SYS_PORT_MAX_TRANSFER_HANDLES || (payloadLength && !payload)) return NTLSTATUS_INVALID_PARAMETER; struct KE_CAPTURED_HANDLE* captured[SYS_PORT_MAX_TRANSFER_HANDLES] = {0}; KSTATUS status = KiPortCaptureHandles(senderProcess, transfers, transferCount, captured); if (!KSUCCESS(status)) return status; PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; PORT_CALL* call = (PORT_CALL*)0; uintptr_t flags = KeAcquireSpinlock(&connection->Lock); LIST_ENTRY* head = &connection->Calls; for (LIST_ENTRY* link = head->Fwd; link != head; link = link->Fwd) { PORT_CALL* candidate = CONTAINING_RECORD(link, PORT_CALL, Link); if (candidate->MessageId == messageId && candidate->CallerSide != endpoint->u.Endpoint.Side) { call = candidate; break; } } if (!call || NtlAtomicLoad32(&call->State) != PORT_CALL_DELIVERED) { KeReleaseSpinlock(&connection->Lock, flags); for (uint32_t i = 0; i < transferCount; ++i) KeReleaseCapturedHandle(captured[i]); return NTLSTATUS_NOT_FOUND; } if (payloadLength > call->ReplyCapacity || transferCount > call->ReplyHandleCapacity) { KeReleaseSpinlock(&connection->Lock, flags); for (uint32_t i = 0; i < transferCount; ++i) KeReleaseCapturedHandle(captured[i]); return NTLSTATUS_BUFFER_TOO_SMALL; } NtlAtomicExchange32(&call->State, PORT_CALL_REPLYING); KeListRemoveChecked(&call->Link); KeReleaseSpinlock(&connection->Lock, flags); SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES] = {0}; if (transferCount) status = KeMaterializeCapturedHandles(call->CallerProcess, captured, transferCount, handles); if (KSUCCESS(status) && payloadLength) { status = MmCopyUserToUser(senderProcess, payload, call->CallerProcess, call->ReplyPayload, payloadLength); } if (!KSUCCESS(status)) { KiPortCloseHandles(call->CallerProcess, handles, transferCount); for (uint32_t i = 0; i < transferCount; ++i) if (captured[i]) KeReleaseCapturedHandle(captured[i]); call->ReplyHandleCount = 0; } else { memcpy(call->ReplyHandles, handles, sizeof(SYS_HANDLE) * transferCount); call->ReplyHandleCount = transferCount; call->ReplyInfo.Size = sizeof(call->ReplyInfo); call->ReplyInfo.MessageId = messageId; call->ReplyInfo.Type = SYS_PORT_MESSAGE_REPLY; call->ReplyInfo.PayloadLength = payloadLength; call->ReplyInfo.HandleCount = transferCount; call->ReplyInfo.SenderProcessId = senderProcess->ProcessId; call->ReplyInfo.SenderThreadId = senderThread->ThreadId; call->ReplyInfo.SenderSecurityId = KiPortSecurityId(senderProcess); } if (KSUCCESS(status)) NtlAtomicIncrement64(&gPortCounters.RepliesCompleted); call->Status = status; NtlAtomicExchange32(&call->State, PORT_CALL_COMPLETED); KeSignalEvent(call->CompletionEvent); return status; } // Explicitly disconnects one endpoint capability without requiring its last handle to close. KSTATUS KePortDisconnect(PORT* endpoint) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT) return NTLSTATUS_INVALID_PARAMETER; KiPortMarkDisconnected(endpoint, false); return NTLSTATUS_SUCCESS; } // Opens one process capability through an accepted endpoint's peer-authority ceiling. KSTATUS KePortOpenPeerProcess(PORT* endpoint, PROCESS* callerProcess, SYS_RIGHTS desiredRights, SYS_HANDLE* processHandleOut) { if (!endpoint || endpoint->Kind != PORT_KIND_ENDPOINT || !callerProcess || desiredRights == 0 || !processHandleOut) return NTLSTATUS_INVALID_PARAMETER; PORT_CONNECTION* connection = endpoint->u.Endpoint.Connection; uint32_t side = endpoint->u.Endpoint.Side; if (!connection || side > 1u) return NTLSTATUS_PORT_DISCONNECTED; PROCESS* peer = connection->PeerProcesses[side]; SYS_RIGHTS ceiling = connection->PeerProcessRights[side]; if (!peer || ceiling == 0 || (desiredRights & ~ceiling) != 0) return NTLSTATUS_ACCESS_DENIED; return KeCreateHandle(callerProcess, &peer->Header, desiredRights, processHandleOut); }