/*++ Copyright (c) 1989 Microsoft Corporation Module Name: lpcreply.c Abstract: Local Inter-Process Communication (LPC) reply system services. Author: Steve Wood (stevewo) 15-May-1989 Revision History: --*/ #include "lpcp.h" NTSTATUS LpcpCopyRequestData( IN BOOLEAN WriteToMessageData, IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, IN PVOID Buffer, IN ULONG BufferSize, OUT PULONG NumberOfBytesCopied OPTIONAL ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,NtReplyPort) #pragma alloc_text(PAGE,NtReplyWaitReplyPort) #pragma alloc_text(PAGE,NtReadRequestData) #pragma alloc_text(PAGE,NtWriteRequestData) #pragma alloc_text(PAGE,LpcpCopyRequestData) #endif NTSTATUS NtReplyPort( IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage ) { KPROCESSOR_MODE PreviousMode; PLPCP_PORT_OBJECT PortObject; PORT_MESSAGE CapturedReplyMessage; NTSTATUS Status; PLPCP_MESSAGE Msg; PETHREAD CurrentThread; PETHREAD WakeupThread; PAGED_CODE(); CurrentThread = PsGetCurrentThread(); // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForRead( ReplyMessage, sizeof( *ReplyMessage ), sizeof( ULONG ) ); CapturedReplyMessage = *ReplyMessage; } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } else { CapturedReplyMessage = *ReplyMessage; } // // Reference the port object by handle // Status = LpcpReferencePortObject( PortHandle, 0, PreviousMode, &PortObject ); if (!NT_SUCCESS( Status )) { return( Status ); } // // 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 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 ); ObDereferenceObject( PortObject ); 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 reply 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, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n", PsGetCurrentProcess()->ImageFileName, Msg, CapturedReplyMessage.MessageId, 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; } 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(); } // // Wake up the thread that is waiting for an answer to its request // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort. That // will dereference itself when it wakes up. // KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore, 0, 1L, FALSE ); // // Dereference port object and return the system service status. // ObDereferenceObject( PortObject ); return( Status ); } NTSTATUS NtReplyWaitReplyPort( IN HANDLE PortHandle, IN OUT PPORT_MESSAGE ReplyMessage ) { KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PLPCP_PORT_OBJECT PortObject; PORT_MESSAGE CapturedReplyMessage; PLPCP_MESSAGE Msg; PETHREAD CurrentThread; PETHREAD WakeupThread; PAGED_CODE(); CurrentThread = PsGetCurrentThread(); // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForRead( ReplyMessage, sizeof( *ReplyMessage ), sizeof( ULONG ) ); CapturedReplyMessage = *ReplyMessage; } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } else { CapturedReplyMessage = *ReplyMessage; } // // Reference the communication port object by handle. Return status if // unsuccessful. // Status = LpcpReferencePortObject( PortHandle, 0, PreviousMode, &PortObject ); if (!NT_SUCCESS( Status )) { return( Status ); } // // 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 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 ); ObDereferenceObject( PortObject ); 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 reply wait reply 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 Wait Reply Msg %lx (%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n", PsGetCurrentProcess()->ImageFileName, Msg, CapturedReplyMessage.MessageId, 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 ); } CurrentThread->LpcReplyMessageId = CapturedReplyMessage.MessageId; CurrentThread->LpcReplyMessage = NULL; if (CurrentThread->LpcReceivedMsgIdValid && CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId ) { CurrentThread->LpcReceivedMessageId = 0; CurrentThread->LpcReceivedMsgIdValid = FALSE; } 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(); ObDereferenceObject( WakeupThread ); ObDereferenceObject( PortObject ); return Status; } // // Wake up the thread that is waiting for an answer to its request // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort. That // will dereference itself when it wakes up. // Status = KeReleaseWaitForSemaphore( &WakeupThread->LpcReplySemaphore, &CurrentThread->LpcReplySemaphore, Executive, PreviousMode ); if (Status == STATUS_USER_APC) { // // if the semaphore is signaled, then clear it // if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) { KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore, WrExecutive, KernelMode, FALSE, NULL ); Status = STATUS_SUCCESS; } } // // If the wait succeeded, copy the reply to the reply buffer. // if (Status == STATUS_SUCCESS ) { // // Acquire the mutex that gaurds the request message // queue. Remove the request message from the list of // messages being processed and free the message back to the queue's zone. // If the zone's free list was zero before freeing this message then // pulse the free event after free the message so that threads waiting // to allocate a request message buffer will wake up. Finally, // release the mutex and return the system service status. // ExAcquireFastMutex( &LpcpLock ); Msg = CurrentThread->LpcReplyMessage; CurrentThread->LpcReplyMessage = NULL; #if DBG if (Msg != NULL) { LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n", PsGetCurrentProcess()->ImageFileName, Msg, Msg->Request.MessageId, *((PULONG)(Msg+1)+0), *((PULONG)(Msg+1)+1), *((PULONG)(Msg+1)+2), *((PULONG)(Msg+1)+3), CurrentThread, THREAD_TO_PROCESS( CurrentThread )->ImageFileName )); if (!IsListEmpty( &Msg->Entry )) { LpcpTrace(( "Reply Msg %lx has non-empty list entry\n", Msg )); } } #endif ExReleaseFastMutex( &LpcpLock ); if (Msg != NULL) { try { LpcpMoveMessage( ReplyMessage, &Msg->Request, (&Msg->Request) + 1, 0, NULL ); } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } // // Acquire the LPC mutex and decrement the reference count for the // message. If the reference count goes to zero the message will be // deleted. // ExAcquireFastMutex( &LpcpLock ); if (Msg->RepliedToThread != NULL) { ObDereferenceObject( Msg->RepliedToThread ); Msg->RepliedToThread = NULL; } LpcpFreeToPortZone( Msg, TRUE ); ExReleaseFastMutex( &LpcpLock ); } } ObDereferenceObject( PortObject ); return( Status ); } NTSTATUS NtReadRequestData( IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, OUT PVOID Buffer, IN ULONG BufferSize, OUT PULONG NumberOfBytesRead OPTIONAL ) { PAGED_CODE(); return LpcpCopyRequestData( FALSE, PortHandle, Message, DataEntryIndex, Buffer, BufferSize, NumberOfBytesRead ); } NTSTATUS NtWriteRequestData( IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, IN PVOID Buffer, IN ULONG BufferSize, OUT PULONG NumberOfBytesWritten OPTIONAL ) { PAGED_CODE(); return LpcpCopyRequestData( TRUE, PortHandle, Message, DataEntryIndex, Buffer, BufferSize, NumberOfBytesWritten ); } NTSTATUS LpcpCopyRequestData( IN BOOLEAN WriteToMessageData, IN HANDLE PortHandle, IN PPORT_MESSAGE Message, IN ULONG DataEntryIndex, IN PVOID Buffer, IN ULONG BufferSize, OUT PULONG NumberOfBytesCopied OPTIONAL ) { KPROCESSOR_MODE PreviousMode; PLPCP_PORT_OBJECT PortObject; PLPCP_MESSAGE Msg; PLIST_ENTRY Head, Next; NTSTATUS Status; PETHREAD ClientThread; PPORT_DATA_INFORMATION DataInfo; PPORT_DATA_ENTRY DataEntry; PORT_MESSAGE CapturedMessage; PORT_DATA_INFORMATION CapturedDataInfo; PORT_DATA_ENTRY CapturedDataEntry; ULONG BytesCopied; PAGED_CODE(); // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { if (WriteToMessageData) { ProbeForRead( Buffer, BufferSize, 1 ); } else { ProbeForWrite( Buffer, BufferSize, 1 ); } ProbeForRead( Message, sizeof( *Message ), sizeof( ULONG ) ); CapturedMessage = *Message; if (ARGUMENT_PRESENT( NumberOfBytesCopied )) { ProbeForWriteUlong( NumberOfBytesCopied ); } } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } else { CapturedMessage = *Message; } if (CapturedMessage.u2.s2.DataInfoOffset == 0) { return( STATUS_INVALID_PARAMETER ); } // // Reference the port object by handle // Status = LpcpReferencePortObject( PortHandle, 0, PreviousMode, &PortObject ); if (!NT_SUCCESS( Status )) { return( Status ); } // // 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( &CapturedMessage.ClientId, NULL, &ClientThread ); if (!NT_SUCCESS( Status )) { ObDereferenceObject( PortObject ); return( Status ); } // // Acquire the 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 ); // // 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 (ClientThread->LpcReplyMessageId != CapturedMessage.MessageId) { Status = STATUS_REPLY_MESSAGE_MISMATCH; } else { Status = STATUS_INVALID_PARAMETER; Msg = LpcpFindDataInfoMessage( PortObject, CapturedMessage.MessageId, CapturedMessage.CallbackId ); if (Msg != NULL) { DataInfo = (PPORT_DATA_INFORMATION)((PUCHAR)&Msg->Request + Msg->Request.u2.s2.DataInfoOffset ); if (DataInfo->CountDataEntries > DataEntryIndex) { DataEntry = &DataInfo->DataEntries[ DataEntryIndex ]; CapturedDataEntry = *DataEntry; if (CapturedDataEntry.Size >= BufferSize) { Status = STATUS_SUCCESS; } } } } if (!NT_SUCCESS( Status )) { ExReleaseFastMutex( &LpcpLock ); ObDereferenceObject( ClientThread ); ObDereferenceObject( PortObject ); return( Status ); } // // Release the mutex that guards the LpcReplyMessage field // ExReleaseFastMutex( &LpcpLock ); // // Copy the message data // if (WriteToMessageData) { Status = MmCopyVirtualMemory( PsGetCurrentProcess(), Buffer, THREAD_TO_PROCESS( ClientThread ), CapturedDataEntry.Base, BufferSize, PreviousMode, &BytesCopied ); } else { Status = MmCopyVirtualMemory( THREAD_TO_PROCESS( ClientThread ), CapturedDataEntry.Base, PsGetCurrentProcess(), Buffer, BufferSize, PreviousMode, &BytesCopied ); } if (ARGUMENT_PRESENT( NumberOfBytesCopied )) { try { *NumberOfBytesCopied = BytesCopied; } except( EXCEPTION_EXECUTE_HANDLER ) { NOTHING; } } // // Dereference client thread and return the system service status. // ObDereferenceObject( ClientThread ); ObDereferenceObject( PortObject ); return( Status ); }