|
|
/*++
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 ); }
|