/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/lpc/reply.c * PURPOSE: Local Procedure Call: Receive (Replies) * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include /* PRIVATE FUNCTIONS *********************************************************/ VOID NTAPI LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port, IN ULONG MessageId, IN ULONG CallbackId, IN CLIENT_ID ClientId) { PLPCP_MESSAGE Message; PLIST_ENTRY ListHead, NextEntry; /* Check if the port we want is the connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT) { /* Use it */ Port = Port->ConnectionPort; if (!Port) return; } /* Loop the list */ ListHead = &Port->LpcDataInfoChainHead; NextEntry = ListHead->Flink; while (ListHead != NextEntry) { /* Get the message */ Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry); /* Make sure it matches */ if ((Message->Request.MessageId == MessageId) && (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread) && (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess)) { /* Unlink and free it */ RemoveEntryList(&Message->Entry); InitializeListHead(&Message->Entry); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); break; } /* Go to the next entry */ NextEntry = NextEntry->Flink; } } VOID NTAPI LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port, IN PLPCP_MESSAGE Message, IN ULONG LockFlags) { BOOLEAN LockHeld = (LockFlags & LPCP_LOCK_HELD); PAGED_CODE(); /* Acquire the lock */ if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock); /* Check if the port we want is the connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT) { /* Use it */ Port = Port->ConnectionPort; if (!Port) { /* Release the lock and return */ if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock); return; } } /* Link the message */ InsertTailList(&Port->LpcDataInfoChainHead, &Message->Entry); /* Release the lock */ if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock); } PLPCP_MESSAGE NTAPI LpcpFindDataInfoMessage( IN PLPCP_PORT_OBJECT Port, IN ULONG MessageId, IN LPC_CLIENT_ID ClientId) { PLPCP_MESSAGE Message; PLIST_ENTRY ListEntry; PAGED_CODE(); /* Check if the port we want is the connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT) { /* Use it */ Port = Port->ConnectionPort; if (!Port) { /* Return NULL */ return NULL; } } /* Loop all entries in the list */ for (ListEntry = Port->LpcDataInfoChainHead.Flink; ListEntry != &Port->LpcDataInfoChainHead; ListEntry = ListEntry->Flink) { Message = CONTAINING_RECORD(ListEntry, LPCP_MESSAGE, Entry); /* Check if this is the desired message */ if ((Message->Request.MessageId == MessageId) && (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) && (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread)) { /* It is, return it */ return Message; } } return NULL; } VOID NTAPI LpcpMoveMessage(IN PPORT_MESSAGE Destination, IN PPORT_MESSAGE Origin, IN PVOID Data, IN ULONG MessageType, IN PCLIENT_ID ClientId) { LPCTRACE((LPC_REPLY_DEBUG | LPC_SEND_DEBUG), "Destination/Origin: %p/%p. Data: %p. Length: %lx\n", Destination, Origin, Data, Origin->u1.Length); /* Set the Message size */ Destination->u1.Length = Origin->u1.Length; /* Set the Message Type */ Destination->u2.s2.Type = !MessageType ? Origin->u2.s2.Type : MessageType & 0xFFFF; /* Check if we have a Client ID */ if (ClientId) { /* Set the Client ID */ Destination->ClientId.UniqueProcess = ClientId->UniqueProcess; Destination->ClientId.UniqueThread = ClientId->UniqueThread; } else { /* Otherwise, copy it */ Destination->ClientId.UniqueProcess = Origin->ClientId.UniqueProcess; Destination->ClientId.UniqueThread = Origin->ClientId.UniqueThread; } /* Copy the MessageId and ClientViewSize */ Destination->MessageId = Origin->MessageId; Destination->ClientViewSize = Origin->ClientViewSize; /* Copy the Message Data */ RtlCopyMemory(Destination + 1, Data, ALIGN_UP_BY(Destination->u1.s1.DataLength, sizeof(ULONG))); } /* PUBLIC FUNCTIONS **********************************************************/ /* * @implemented */ NTSTATUS NTAPI NtReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage) { NTSTATUS Status; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PORT_MESSAGE CapturedReplyMessage; PLPCP_PORT_OBJECT Port; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(), WakeupThread; PAGED_CODE(); LPCTRACE(LPC_REPLY_DEBUG, "Handle: %p. Message: %p.\n", PortHandle, ReplyMessage); /* Check if the call comes from user mode */ if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForRead(ReplyMessage, sizeof(*ReplyMessage), sizeof(ULONG)); CapturedReplyMessage = *(volatile PORT_MESSAGE*)ReplyMessage; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { CapturedReplyMessage = *ReplyMessage; } /* Validate its length */ if (((ULONG)CapturedReplyMessage.u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)CapturedReplyMessage.u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Make sure it has a valid ID */ if (!CapturedReplyMessage.MessageId) return STATUS_INVALID_PARAMETER; /* Get the Port object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Validate its length in respect to the port object */ if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) { /* Too large, fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } /* Get the ETHREAD corresponding to it */ Status = PsLookupProcessThreadByCid(&CapturedReplyMessage.ClientId, NULL, &WakeupThread); if (!NT_SUCCESS(Status)) { /* No thread found, fail */ ObDereferenceObject(Port); return Status; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ ObDereferenceObject(WakeupThread); ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* Keep the lock acquired */ KeAcquireGuardedMutex(&LpcpLock); /* Make sure this is the reply the thread is waiting for */ if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) || ((LpcpGetMessageFromThread(WakeupThread)) && (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)-> Request) != LPC_REQUEST))) { /* It isn't, fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(WakeupThread); ObDereferenceObject(Port); return STATUS_REPLY_MESSAGE_MISMATCH; } /* Copy the message */ _SEH2_TRY { LpcpMoveMessage(&Message->Request, &CapturedReplyMessage, ReplyMessage + 1, LPC_REPLY, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Cleanup and return the exception code */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(WakeupThread); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Reference the thread while we use it */ ObReferenceObject(WakeupThread); Message->RepliedToThread = WakeupThread; /* Set this as the reply message */ WakeupThread->LpcReplyMessageId = 0; WakeupThread->LpcReplyMessage = (PVOID)Message; /* Check if we have messages on the reply chain */ if (!(WakeupThread->LpcExitThreadCalled) && !(IsListEmpty(&WakeupThread->LpcReplyChain))) { /* Remove us from it and reinitialize it */ RemoveEntryList(&WakeupThread->LpcReplyChain); InitializeListHead(&WakeupThread->LpcReplyChain); } /* Check if this is the message the thread had received */ if ((Thread->LpcReceivedMsgIdValid) && (Thread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) { /* Clear this data */ Thread->LpcReceivedMessageId = 0; Thread->LpcReceivedMsgIdValid = FALSE; } /* Free any data information */ LpcpFreeDataInfoMessage(Port, CapturedReplyMessage.MessageId, CapturedReplyMessage.CallbackId, CapturedReplyMessage.ClientId); /* Release the lock and release the LPC semaphore to wake up waiters */ KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(&WakeupThread->LpcReplySemaphore); /* Now we can let go of the thread */ ObDereferenceObject(WakeupThread); /* Dereference port object */ ObDereferenceObject(Port); return Status; } /* * @implemented */ NTSTATUS NTAPI NtReplyWaitReceivePortEx(IN HANDLE PortHandle, OUT PVOID *PortContext OPTIONAL, IN PPORT_MESSAGE ReplyMessage OPTIONAL, OUT PPORT_MESSAGE ReceiveMessage, IN PLARGE_INTEGER Timeout OPTIONAL) { NTSTATUS Status; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode; PORT_MESSAGE CapturedReplyMessage; LARGE_INTEGER CapturedTimeout; PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(), WakeupThread; PLPCP_CONNECTION_MESSAGE ConnectMessage; ULONG ConnectionInfoLength; PAGED_CODE(); LPCTRACE(LPC_REPLY_DEBUG, "Handle: %p. Messages: %p/%p. Context: %p\n", PortHandle, ReplyMessage, ReceiveMessage, PortContext); /* Check if the call comes from user mode */ if (PreviousMode != KernelMode) { _SEH2_TRY { if (PortContext != NULL) ProbeForWritePointer(PortContext); if (ReplyMessage != NULL) { ProbeForRead(ReplyMessage, sizeof(*ReplyMessage), sizeof(ULONG)); CapturedReplyMessage = *(volatile PORT_MESSAGE*)ReplyMessage; } if (Timeout != NULL) { ProbeForReadLargeInteger(Timeout); CapturedTimeout = *(volatile LARGE_INTEGER*)Timeout; Timeout = &CapturedTimeout; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { /* If this is a system thread, then let it page out its stack */ if (Thread->SystemThread) WaitMode = UserMode; if (ReplyMessage != NULL) CapturedReplyMessage = *ReplyMessage; } /* Check if caller has a reply message */ if (ReplyMessage) { /* Validate its length */ if (((ULONG)CapturedReplyMessage.u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)CapturedReplyMessage.u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Make sure it has a valid ID */ if (!CapturedReplyMessage.MessageId) return STATUS_INVALID_PARAMETER; } /* Get the Port object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Check if the caller has a reply message */ if (ReplyMessage) { /* Validate its length in respect to the port object */ if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) { /* Too large, fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } } /* Check if this is anything but a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT) { /* Check if this is the connection port */ if (Port->ConnectionPort == Port) { /* Use this port */ ConnectionPort = ReceivePort = Port; ObReferenceObject(ConnectionPort); } else { /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the port */ ConnectionPort = ReceivePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ KeReleaseGuardedMutex(&LpcpLock); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } /* Release lock and reference */ ObReferenceObject(ConnectionPort); KeReleaseGuardedMutex(&LpcpLock); } } else { /* Otherwise, use the port itself */ ReceivePort = Port; } /* Check if the caller gave a reply message */ if (ReplyMessage) { /* Get the ETHREAD corresponding to it */ Status = PsLookupProcessThreadByCid(&CapturedReplyMessage.ClientId, NULL, &WakeupThread); if (!NT_SUCCESS(Status)) { /* No thread found, fail */ ObDereferenceObject(Port); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(WakeupThread); ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* Keep the lock acquired */ KeAcquireGuardedMutex(&LpcpLock); /* Make sure this is the reply the thread is waiting for */ if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) || ((LpcpGetMessageFromThread(WakeupThread)) && (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->Request) != LPC_REQUEST))) { /* It isn't, fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(WakeupThread); ObDereferenceObject(Port); return STATUS_REPLY_MESSAGE_MISMATCH; } /* Copy the message */ _SEH2_TRY { LpcpMoveMessage(&Message->Request, &CapturedReplyMessage, ReplyMessage + 1, LPC_REPLY, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Cleanup and return the exception code */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(WakeupThread); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Reference the thread while we use it */ ObReferenceObject(WakeupThread); Message->RepliedToThread = WakeupThread; /* Set this as the reply message */ WakeupThread->LpcReplyMessageId = 0; WakeupThread->LpcReplyMessage = (PVOID)Message; /* Check if we have messages on the reply chain */ if (!(WakeupThread->LpcExitThreadCalled) && !(IsListEmpty(&WakeupThread->LpcReplyChain))) { /* Remove us from it and reinitialize it */ RemoveEntryList(&WakeupThread->LpcReplyChain); InitializeListHead(&WakeupThread->LpcReplyChain); } /* Check if this is the message the thread had received */ if ((Thread->LpcReceivedMsgIdValid) && (Thread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) { /* Clear this data */ Thread->LpcReceivedMessageId = 0; Thread->LpcReceivedMsgIdValid = FALSE; } /* Free any data information */ LpcpFreeDataInfoMessage(Port, CapturedReplyMessage.MessageId, CapturedReplyMessage.CallbackId, CapturedReplyMessage.ClientId); /* Release the lock and release the LPC semaphore to wake up waiters */ KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(&WakeupThread->LpcReplySemaphore); /* Now we can let go of the thread */ ObDereferenceObject(WakeupThread); } /* Now wait for someone to reply to us */ LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode); if (Status != STATUS_SUCCESS) goto Cleanup; /* Wait done, get the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if we've received nothing */ if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead)) { /* Check if this was a waitable port and wake it */ if (ReceivePort->Flags & LPCP_WAITABLE_PORT) { /* Reset its event */ KeClearEvent(&ReceivePort->WaitEvent); } /* Release the lock and fail */ KeReleaseGuardedMutex(&LpcpLock); if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(Port); return STATUS_UNSUCCESSFUL; } /* Get the message on the queue */ Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->MsgQueue.ReceiveHead), LPCP_MESSAGE, Entry); /* Check if the queue is empty now */ if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead)) { /* Check if this was a waitable port */ if (ReceivePort->Flags & LPCP_WAITABLE_PORT) { /* Reset its event */ KeClearEvent(&ReceivePort->WaitEvent); } } /* Re-initialize the message's list entry */ InitializeListHead(&Message->Entry); /* Set this as the received message */ Thread->LpcReceivedMessageId = Message->Request.MessageId; Thread->LpcReceivedMsgIdValid = TRUE; _SEH2_TRY { /* Check if this was a connection request */ if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST) { /* Get the connection message */ ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1); LPCTRACE(LPC_REPLY_DEBUG, "Request Messages: %p/%p\n", Message, ConnectMessage); /* Get its length */ ConnectionInfoLength = Message->Request.u1.s1.DataLength - sizeof(LPCP_CONNECTION_MESSAGE); /* Return it as the receive message */ *ReceiveMessage = Message->Request; /* Clear our stack variable so the message doesn't get freed */ Message = NULL; /* Setup the receive message */ ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof(LPCP_MESSAGE) + ConnectionInfoLength); ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength; RtlCopyMemory(ReceiveMessage + 1, ConnectMessage + 1, ConnectionInfoLength); /* Clear the port context if the caller requested one */ if (PortContext) *PortContext = NULL; } else if (LpcpGetMessageType(&Message->Request) != LPC_REPLY) { /* Otherwise, this is a new message or event */ LPCTRACE(LPC_REPLY_DEBUG, "Non-Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Copy it */ LpcpMoveMessage(ReceiveMessage, &Message->Request, (&Message->Request) + 1, 0, NULL); /* Return its context */ if (PortContext) *PortContext = Message->PortContext; /* And check if it has data information */ if (Message->Request.u2.s2.DataInfoOffset) { /* It does, save it, and don't free the message below */ LpcpSaveDataInfoMessage(Port, Message, LPCP_LOCK_HELD); Message = NULL; } } else { /* This is a reply message, should never happen! */ ASSERT(FALSE); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Check if we have a message pointer here */ if (Message) { /* Free it and release the lock */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); } else { /* Just release the lock */ KeReleaseGuardedMutex(&LpcpLock); } Cleanup: /* All done, dereference the port and return the status */ LPCTRACE(LPC_REPLY_DEBUG, "Port: %p. Status: %d\n", Port, Status); if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(Port); return Status; } /* * @implemented */ NTSTATUS NTAPI NtReplyWaitReceivePort(IN HANDLE PortHandle, OUT PVOID *PortContext OPTIONAL, IN PPORT_MESSAGE ReplyMessage OPTIONAL, OUT PPORT_MESSAGE ReceiveMessage) { /* Call the newer API */ return NtReplyWaitReceivePortEx(PortHandle, PortContext, ReplyMessage, ReceiveMessage, NULL); } /* * @unimplemented */ NTSTATUS NTAPI NtReplyWaitReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } NTSTATUS NTAPI LpcpCopyRequestData( IN BOOLEAN Write, IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG Index, IN PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength) { NTSTATUS Status; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PORT_MESSAGE CapturedMessage; PLPCP_PORT_OBJECT Port = NULL; PETHREAD ClientThread = NULL; SIZE_T LocalReturnLength; PLPCP_MESSAGE InfoMessage; PLPCP_DATA_INFO DataInfo; PVOID DataInfoBaseAddress; PAGED_CODE(); /* Check if the call comes from user mode */ if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForRead(Message, sizeof(*Message), sizeof(PVOID)); CapturedMessage = *(volatile PORT_MESSAGE*)Message; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { CapturedMessage = *Message; } /* Make sure there is any data to copy */ if (CapturedMessage.u2.s2.DataInfoOffset == 0) { return STATUS_INVALID_PARAMETER; } /* Reference the port handle */ Status = ObReferenceObjectByHandle(PortHandle, PORT_ALL_ACCESS, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to reference port handle: 0x%ls\n", Status); return Status; } /* Look up the client thread */ Status = PsLookupProcessThreadByCid(&CapturedMessage.ClientId, NULL, &ClientThread); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to lookup client thread for [0x%lx:0x%lx]: 0x%ls\n", CapturedMessage.ClientId.UniqueProcess, CapturedMessage.ClientId.UniqueThread, Status); goto Cleanup; } /* Acquire the global LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check for message id mismatch */ if ((ClientThread->LpcReplyMessageId != CapturedMessage.MessageId) || (CapturedMessage.MessageId == 0)) { DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n", ClientThread->LpcReplyMessageId, CapturedMessage.MessageId); Status = STATUS_REPLY_MESSAGE_MISMATCH; goto CleanupWithLock; } /* Validate the port */ if (!LpcpValidateClientPort(ClientThread, Port)) { DPRINT1("LpcpValidateClientPort failed\n"); Status = STATUS_REPLY_MESSAGE_MISMATCH; goto CleanupWithLock; } /* Find the message with the data */ InfoMessage = LpcpFindDataInfoMessage(Port, CapturedMessage.MessageId, CapturedMessage.ClientId); if (InfoMessage == NULL) { DPRINT1("LpcpFindDataInfoMessage failed\n"); Status = STATUS_INVALID_PARAMETER; goto CleanupWithLock; } /* Get the data info */ DataInfo = LpcpGetDataInfoFromMessage(&InfoMessage->Request); /* Check if the index is within bounds */ if (Index >= DataInfo->NumberOfEntries) { DPRINT1("Message data index %lu out of bounds (%lu in msg)\n", Index, DataInfo->NumberOfEntries); Status = STATUS_INVALID_PARAMETER; goto CleanupWithLock; } /* Check if the caller wants to read/write more data than expected */ if (BufferLength > DataInfo->Entries[Index].DataLength) { DPRINT1("Trying to read more data (%lu) than available (%lu)\n", BufferLength, DataInfo->Entries[Index].DataLength); Status = STATUS_INVALID_PARAMETER; goto CleanupWithLock; } /* Get the data pointer */ DataInfoBaseAddress = DataInfo->Entries[Index].BaseAddress; /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); if (Write) { /* Copy data from the caller to the message sender */ Status = MmCopyVirtualMemory(PsGetCurrentProcess(), Buffer, ClientThread->ThreadsProcess, DataInfoBaseAddress, BufferLength, PreviousMode, &LocalReturnLength); } else { /* Copy data from the message sender to the caller */ Status = MmCopyVirtualMemory(ClientThread->ThreadsProcess, DataInfoBaseAddress, PsGetCurrentProcess(), Buffer, BufferLength, PreviousMode, &LocalReturnLength); } if (!NT_SUCCESS(Status)) { DPRINT1("MmCopyVirtualMemory failed: 0x%ls\n", Status); goto Cleanup; } /* Check if the caller asked to return the copied length */ if (ReturnLength != NULL) { _SEH2_TRY { *ReturnLength = LocalReturnLength; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Ignore */ DPRINT1("Exception writing ReturnLength, ignoring\n"); } _SEH2_END; } Cleanup: if (ClientThread != NULL) ObDereferenceObject(ClientThread); ObDereferenceObject(Port); return Status; CleanupWithLock: /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); goto Cleanup; } /* * @implemented */ NTSTATUS NTAPI NtReadRequestData(IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG Index, IN PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength) { /* Call the internal function */ return LpcpCopyRequestData(FALSE, PortHandle, Message, Index, Buffer, BufferLength, ReturnLength); } /* * @implemented */ NTSTATUS NTAPI NtWriteRequestData(IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG Index, IN PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength) { /* Call the internal function */ return LpcpCopyRequestData(TRUE, PortHandle, Message, Index, Buffer, BufferLength, ReturnLength); } /* EOF */