/*++ Copyright (c) 1989 Microsoft Corporation Module Name: lpcrecv.c Abstract: Local Inter-Process Communication (LPC) receive system services. Author: Steve Wood (stevewo) 15-May-1989 Revision History: --*/ #include "lpcp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,NtReplyWaitReceivePort) #endif NTSTATUS NtReplyWaitReceivePort( IN HANDLE PortHandle, OUT PVOID *PortContext OPTIONAL, IN PPORT_MESSAGE ReplyMessage OPTIONAL, OUT PPORT_MESSAGE ReceiveMessage ) { PLPCP_PORT_OBJECT PortObject; PLPCP_PORT_OBJECT ReceivePort; PORT_MESSAGE CapturedReplyMessage; KPROCESSOR_MODE PreviousMode; KPROCESSOR_MODE WaitMode; NTSTATUS Status; PLPCP_MESSAGE Msg; PETHREAD CurrentThread; PETHREAD WakeupThread; PAGED_CODE(); CurrentThread = PsGetCurrentThread(); // // Get previous processor mode // PreviousMode = KeGetPreviousMode(); WaitMode = PreviousMode; if (PreviousMode != KernelMode) { try { if (ARGUMENT_PRESENT( PortContext )) { ProbeForWriteUlong( (PULONG)PortContext ); } if (ARGUMENT_PRESENT( ReplyMessage)) { ProbeForRead( ReplyMessage, sizeof( *ReplyMessage ), sizeof( ULONG ) ); CapturedReplyMessage = *ReplyMessage; } ProbeForWrite( ReceiveMessage, sizeof( *ReceiveMessage ), sizeof( ULONG ) ); } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } else { // // Kernel mode threads call with wait mode of user so that their kernel // stacks are swappable. Main consumer of this is SepRmCommandThread // if ( IS_SYSTEM_THREAD(CurrentThread) ) { WaitMode = UserMode; } if (ARGUMENT_PRESENT( ReplyMessage)) { CapturedReplyMessage = *ReplyMessage; } } // // Reference the port object by handle // Status = LpcpReferencePortObject( PortHandle, 0, PreviousMode, &PortObject ); if (!NT_SUCCESS( Status )) { return( Status ); } if ((PortObject->Flags & PORT_TYPE) != CLIENT_COMMUNICATION_PORT) { ReceivePort = PortObject->ConnectionPort; } else { ReceivePort = PortObject; } // // If ReplyMessage argument present, then send reply // if (ARGUMENT_PRESENT( ReplyMessage )) { // // Translate the ClientId from the connection request into a // thread pointer. This is a referenced pointer to keep the thread // from evaporating out from under us. // Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId, NULL, &WakeupThread ); if (!NT_SUCCESS( Status )) { ObDereferenceObject( PortObject ); return( Status ); } // // Acquire the global Lpc mutex that gaurds the LpcReplyMessage // field of the thread and get the pointer to the message that // the thread is waiting for a reply to. // ExAcquireFastMutex( &LpcpLock ); Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength ); if (Msg == NULL) { ExReleaseFastMutex( &LpcpLock ); ObDereferenceObject( WakeupThread ); return( STATUS_NO_MEMORY ); } // // See if the thread is waiting for a reply to the message // specified on this call. If not then a bogus message // has been specified, so release the mutex, dereference the thread // and return failure. // if (WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId ) { LpcpPrint(( "%s Attempted ReplyWaitReceive to Thread %lx (%s)\n", PsGetCurrentProcess()->ImageFileName, WakeupThread, THREAD_TO_PROCESS( WakeupThread )->ImageFileName )); LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n", CapturedReplyMessage.MessageId, CapturedReplyMessage.ClientId.UniqueProcess, CapturedReplyMessage.ClientId.UniqueThread )); LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n", WakeupThread->LpcReplyMessageId, WakeupThread->Cid.UniqueProcess, WakeupThread->Cid.UniqueThread )); #if DBG if (LpcpStopOnReplyMismatch) { DbgBreakPoint(); } #endif LpcpFreeToPortZone( Msg, TRUE ); ExReleaseFastMutex( &LpcpLock ); ObDereferenceObject( WakeupThread ); ObDereferenceObject( PortObject ); return (STATUS_REPLY_MESSAGE_MISMATCH); } LpcpTrace(( "%s Sending Reply Msg %lx (%u.%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n", PsGetCurrentProcess()->ImageFileName, Msg, CapturedReplyMessage.MessageId, CapturedReplyMessage.CallbackId, CapturedReplyMessage.u2.s2.DataInfoOffset, *((PULONG)(Msg+1)+0), *((PULONG)(Msg+1)+1), *((PULONG)(Msg+1)+2), *((PULONG)(Msg+1)+3), WakeupThread, THREAD_TO_PROCESS( WakeupThread )->ImageFileName )); if (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) { LpcpFreeDataInfoMessage( PortObject, CapturedReplyMessage.MessageId, CapturedReplyMessage.CallbackId ); } // // Release the mutex that guards the LpcReplyMessage field // after marking message as being replied to. // Msg->RepliedToThread = WakeupThread; WakeupThread->LpcReplyMessageId = 0; WakeupThread->LpcReplyMessage = (PVOID)Msg; // // Remove the thread from the reply rundown list as we are sending the reply. // if (!WakeupThread->LpcExitThreadCalled && !IsListEmpty( &WakeupThread->LpcReplyChain )) { RemoveEntryList( &WakeupThread->LpcReplyChain ); InitializeListHead( &WakeupThread->LpcReplyChain ); } if (CurrentThread->LpcReceivedMsgIdValid && CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId ) { CurrentThread->LpcReceivedMessageId = 0; CurrentThread->LpcReceivedMsgIdValid = FALSE; } LpcpTrace(( "%s Waiting for message to Port %x (%s)\n", PsGetCurrentProcess()->ImageFileName, ReceivePort, LpcpGetCreatorName( ReceivePort ) )); ExReleaseFastMutex( &LpcpLock ); // Copy the reply message to the request message buffer // try { LpcpMoveMessage( &Msg->Request, &CapturedReplyMessage, (ReplyMessage + 1), LPC_REPLY, NULL ); } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); // FIX, FIX } // // Wake up the thread that is waiting for an answer to its request // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort // Status = KeReleaseWaitForSemaphore( &WakeupThread->LpcReplySemaphore, ReceivePort->MsgQueue.Semaphore, WrLpcReceive, WaitMode ); // // Fall into receive code. Client thread reference will be // returned by the client when it wakes up. // } else { // // Wait for a message // LpcpTrace(( "%s Waiting for message to Port %x (%s)\n", PsGetCurrentProcess()->ImageFileName, ReceivePort, LpcpGetCreatorName( ReceivePort ) )); Status = KeWaitForSingleObject( ReceivePort->MsgQueue.Semaphore, WrLpcReceive, WaitMode, FALSE, NULL ); } if (Status == STATUS_SUCCESS) { ExAcquireFastMutex( &LpcpLock ); if (IsListEmpty( &ReceivePort->MsgQueue.ReceiveHead )) { ExReleaseFastMutex( &LpcpLock ); ObDereferenceObject( PortObject ); return( STATUS_UNSUCCESSFUL ); } Msg = (PLPCP_MESSAGE)RemoveHeadList( &ReceivePort->MsgQueue.ReceiveHead ); InitializeListHead( &Msg->Entry ); LpcpTrace(( "%s Receive Msg %lx (%u) from Port %lx (%s)\n", PsGetCurrentProcess()->ImageFileName, Msg, Msg->Request.MessageId, ReceivePort, LpcpGetCreatorName( ReceivePort ) )); CurrentThread->LpcReceivedMessageId = Msg->Request.MessageId; CurrentThread->LpcReceivedMsgIdValid = TRUE; ExReleaseFastMutex( &LpcpLock ); try { if (Msg->Request.u2.s2.Type == LPC_CONNECTION_REQUEST) { PLPCP_CONNECTION_MESSAGE ConnectMsg; ULONG ConnectionInfoLength; ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1); ConnectionInfoLength = Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg ); *ReceiveMessage = Msg->Request; ReceiveMessage->u1.s1.TotalLength = sizeof( *ReceiveMessage ) + ConnectionInfoLength; ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength; RtlMoveMemory( ReceiveMessage+1, ConnectMsg + 1, ConnectionInfoLength ); if (ARGUMENT_PRESENT( PortContext )) { *PortContext = NULL; } // // Dont free message until NtAcceptConnectPort called. // Msg = NULL; } else if (Msg->Request.u2.s2.Type != LPC_REPLY) { LpcpMoveMessage( ReceiveMessage, &Msg->Request, (&Msg->Request) + 1, 0, NULL ); if (ARGUMENT_PRESENT( PortContext )) { *PortContext = Msg->PortContext; } // // If message contains DataInfo for access via NtRead/WriteRequestData // then put the message on a list in the communication port and dont // free it. It will be freed when the server replies to the message. // if (Msg->Request.u2.s2.DataInfoOffset != 0) { LpcpSaveDataInfoMessage( PortObject, Msg ); Msg = NULL; } } else { LpcpPrint(( "LPC: Bogus reply message (%08x) in receive queue of connection port %08x\n", Msg, ReceivePort )); KdBreakPoint(); } } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); // FIX, FIX } // // Acquire the LPC mutex and decrement the reference count for the // message. If the reference count goes to zero the message will be // deleted. // if (Msg != NULL) { LpcpFreeToPortZone( Msg, FALSE ); } } ObDereferenceObject( PortObject ); return( Status ); }