VOID NTAPI LpcExitThread(IN PETHREAD Thread) { PLPCP_MESSAGE Message; ASSERT(Thread == PsGetCurrentThread()); /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Make sure that the Reply Chain is empty */ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* It's not, remove the entry */ RemoveEntryList(&Thread->LpcReplyChain); } /* Set the thread in exit mode */ Thread->LpcExitThreadCalled = TRUE; Thread->LpcReplyMessageId = 0; /* Check if there's a reply message */ Message = LpcpGetMessageFromThread(Thread); if (Message) { /* FIXME: TODO */ ASSERT(FALSE); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); }
PVOID NTAPI LpcpFreeConMsg(IN OUT PLPCP_MESSAGE *Message, IN OUT PLPCP_CONNECTION_MESSAGE *ConnectMessage, IN PETHREAD CurrentThread) { PVOID SectionToMap; PLPCP_MESSAGE ReplyMessage; /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if the reply chain is not empty */ if (!IsListEmpty(&CurrentThread->LpcReplyChain)) { /* Remove this entry and re-initialize it */ RemoveEntryList(&CurrentThread->LpcReplyChain); InitializeListHead(&CurrentThread->LpcReplyChain); } /* Check if there's a reply message */ ReplyMessage = LpcpGetMessageFromThread(CurrentThread); if (ReplyMessage) { /* Get the message */ *Message = ReplyMessage; /* Check if it's got messages */ if (!IsListEmpty(&ReplyMessage->Entry)) { /* Clear the list */ RemoveEntryList(&ReplyMessage->Entry); InitializeListHead(&ReplyMessage->Entry); } /* Clear message data */ CurrentThread->LpcReceivedMessageId = 0; CurrentThread->LpcReplyMessage = NULL; /* Get the connection message and clear the section */ *ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(ReplyMessage + 1); SectionToMap = (*ConnectMessage)->SectionToMap; (*ConnectMessage)->SectionToMap = NULL; } else { /* No message to return */ *Message = NULL; SectionToMap = NULL; } /* Release the lock and return the section */ KeReleaseGuardedMutex(&LpcpLock); return SectionToMap; }
/* * @implemented */ NTSTATUS NTAPI NtRequestWaitReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest, IN OUT PPORT_MESSAGE LpcReply) { PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback; PKSEMAPHORE Semaphore; ULONG MessageType; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Handle: %lx. Messages: %p/%p. Type: %lx\n", PortHandle, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check if this is an LPC Request */ if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST) { /* Then it's a callback */ Callback = TRUE; } else if (LpcpGetMessageType(LpcRequest)) { /* This is a not kernel-mode message */ return STATUS_INVALID_PARAMETER; } else { /* This is a kernel-mode message without a callback */ LpcRequest->u2.s2.Type |= LPC_REQUEST; Callback = FALSE; } /* Get the message type */ MessageType = LpcRequest->u2.s2.Type; /* Validate the length */ if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)LpcRequest->u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); } else { /* No callback, just copy the message */ _SEH2_TRY { /* Copy it */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Fail */ LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } /* This will be the rundown port */ ReplyPort = QueuePort; /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; } /* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore; /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } } /* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion(); /* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0; /* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Move the message */ _SEH2_TRY { LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Check if this is an LPC request with data information */ if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) && (Message->Request.u2.s2.DataInfoOffset)) { /* Save the data information */ LpcpSaveDataInfoMessage(Port, Message, 0); } else { /* Otherwise, just free it */ LpcpFreeToPortZone(Message, 0); } } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } }
/* * @implemented */ NTSTATUS NTAPI LpcRequestWaitReplyPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcRequest, OUT PPORT_MESSAGE LpcReply) { PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback = FALSE; PKSEMAPHORE Semaphore; USHORT MessageType; PAGED_CODE(); Port = (PLPCP_PORT_OBJECT)PortObject; LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Messages: %p/%p. Type: %lx\n", Port, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check if this is an LPC Request */ MessageType = LpcpGetMessageType(LpcRequest); switch (MessageType) { /* No type */ case 0: /* Assume LPC request */ MessageType = LPC_REQUEST; break; /* LPC request callback */ case LPC_REQUEST: /* This is a callback */ Callback = TRUE; break; /* Anything else */ case LPC_CLIENT_DIED: case LPC_PORT_CLOSED: case LPC_EXCEPTION: case LPC_DEBUG_EVENT: case LPC_ERROR_EVENT: /* Nothing to do */ break; default: /* Invalid message type */ return STATUS_INVALID_PARAMETER; } /* Set the request type */ LpcRequest->u2.s2.Type = MessageType; /* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ return STATUS_NO_MEMORY; } /* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } else { /* No callback, just copy the message */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, 0, &Thread->Cid); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } /* This will be the rundown port */ ReplyPort = QueuePort; /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; } /* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore; /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } } /* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion(); /* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0; /* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Move the message */ LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if we replied to a thread */ if (Message->RepliedToThread) { /* Dereference */ ObDereferenceObject(Message->RepliedToThread); Message->RepliedToThread = NULL; } /* Free the message */ LpcpFreeToPortZone(Message, 3); } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } } else { /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 0); } /* All done */ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %p\n", Port, Status); /* Dereference the connection port */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; }
VOID NTAPI LpcpDestroyPortQueue(IN PLPCP_PORT_OBJECT Port, IN BOOLEAN Destroy) { PLIST_ENTRY ListHead, NextEntry; PETHREAD Thread; PLPCP_MESSAGE Message; PLPCP_PORT_OBJECT ConnectionPort = NULL; PLPCP_CONNECTION_MESSAGE ConnectMessage; PLPCP_NONPAGED_PORT_QUEUE MessageQueue; PAGED_CODE(); LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags); /* Hold the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if we have a connected port */ if (((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_UNCONNECTED_PORT) && (Port->ConnectedPort)) { /* Disconnect it */ Port->ConnectedPort->ConnectedPort = NULL; ConnectionPort = Port->ConnectedPort->ConnectionPort; if (ConnectionPort) { /* Clear connection port */ Port->ConnectedPort->ConnectionPort = NULL; } } /* Check if this is a connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) { /* Delete the name */ Port->Flags |= LPCP_NAME_DELETED; } /* Walk all the threads waiting and signal them */ ListHead = &Port->LpcReplyChainHead; NextEntry = ListHead->Flink; while ((NextEntry) && (NextEntry != ListHead)) { /* Get the Thread */ Thread = CONTAINING_RECORD(NextEntry, ETHREAD, LpcReplyChain); /* Make sure we're not in exit */ if (Thread->LpcExitThreadCalled) break; /* Move to the next entry */ NextEntry = NextEntry->Flink; /* Remove and reinitialize the List */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); /* Check if someone is waiting */ if (!KeReadStateSemaphore(&Thread->LpcReplySemaphore)) { /* Get the message */ Message = LpcpGetMessageFromThread(Thread); if (Message) { /* Check if it's a connection request */ if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST) { /* Get the connection message */ ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1); /* Check if it had a section */ if (ConnectMessage->SectionToMap) { /* Dereference it */ ObDereferenceObject(ConnectMessage->SectionToMap); } } /* Clear the reply message */ Thread->LpcReplyMessage = NULL; /* And remove the message from the port zone */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); NextEntry = Port->LpcReplyChainHead.Flink; } /* Release the semaphore and reset message id count */ Thread->LpcReplyMessageId = 0; KeReleaseSemaphore(&Thread->LpcReplySemaphore, 0, 1, FALSE); } } /* Reinitialize the list head */ InitializeListHead(&Port->LpcReplyChainHead); /* Loop queued messages */ while ((Port->MsgQueue.ReceiveHead.Flink) && !(IsListEmpty(&Port->MsgQueue.ReceiveHead))) { /* Get the message */ Message = CONTAINING_RECORD(Port->MsgQueue.ReceiveHead.Flink, LPCP_MESSAGE, Entry); /* Free and reinitialize it's list head */ RemoveEntryList(&Message->Entry); InitializeListHead(&Message->Entry); /* Remove it from the port zone */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Dereference the connection port */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); /* Check if we have to free the port entirely */ if (Destroy) { /* Check if the semaphore exists */ if (Port->MsgQueue.Semaphore) { /* Use the semaphore to find the port queue and free it */ MessageQueue = CONTAINING_RECORD(Port->MsgQueue.Semaphore, LPCP_NONPAGED_PORT_QUEUE, Semaphore); ExFreePoolWithTag(MessageQueue, 'troP'); } } }