Windows2000/private/ntos/lpc/lpcreply.c
2020-09-30 17:12:32 +02:00

710 lines
27 KiB
C

/*++
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
--*/
#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)
/*++
Routine Description:
A client and server process can send a reply to a previous request message with the NtReplyPort service:
The Type field of the message is set to LPC_REPLY by the service.
If the MapInfoOffset field of the reply message is non-zero, then the
PORT_MAP_INFORMATION structure it points to will be processed and the
relevant pages in the caller's address space will be unmapped.
The ClientId and MessageId fields of the ReplyMessage structure are used to identify the thread waiting for this reply.
If the target thread is in fact waiting for this reply message, then the reply message is copied
into the thread's message buffer and the thread's wait is satisfied.
If the thread is not waiting for a reply or is waiting for a reply to
some other MessageId, then the message is placed in the message queue of
the port that is connected to the communication port specified by the
PortHandle parameter and the Type field of the message is set to LPC_LOST_REPLY.
Arguments:
PortHandle - Specifies the handle of the communication port that the original message was received from.
ReplyMessage - Specifies a pointer to the reply message to be sent.
The ClientId and MessageId fields determine which thread will get the reply.
Return Value:
Status code that indicates whether or not the operation was successful.
--*/
{
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;
}
// Make sure DataLength is valid with respect to header size and total length
if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
return STATUS_INVALID_PARAMETER;
}
// Make sure the user didn't give us a bogus reply message id
if (CapturedReplyMessage.MessageId == 0) {
return STATUS_INVALID_PARAMETER;
}
// Reference the port object by handle
Status = LpcpReferencePortObject( PortHandle, 0, PreviousMode, &PortObject );
if (!NT_SUCCESS( Status )) {
return Status;
}
LpcpSaveThread (PortObject);
// Validate the message length
if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
ObDereferenceObject( PortObject );
return STATUS_PORT_MESSAGE_TOO_LONG;
}
// 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 guards the LpcReplyMessage field of the thread
// and get the pointer to the message that the thread is waiting for a reply to.
LpcpAcquireLpcpLock();
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
if (Msg == NULL) {
LpcpReleaseLpcpLock();
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.
// We also fail this request if the caller isn't replying to a request
// message. For example, if the caller is replying to a connection request
if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) ||
((WakeupThread->LpcReplyMessage != NULL) &&
(((PLPCP_MESSAGE)(WakeupThread->LpcReplyMessage))->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)) {
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 );
LpcpReleaseLpcpLock();
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return STATUS_REPLY_MESSAGE_MISMATCH;
}
// Copy the reply message to the request message buffer.
// Do this before we actually fiddle with the wakeup threads fields.
// Otherwise we could mess up its state
try {
LpcpMoveMessage( &Msg->Request, &CapturedReplyMessage, (ReplyMessage + 1), LPC_REPLY, NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
LpcpFreeToPortZone( Msg, TRUE );
LpcpReleaseLpcpLock();
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return (Status = GetExceptionCode());
}
// At this point we know the thread is waiting for our reply
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 ));
// Locate and free the messsage from the port.
// This call use to test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
// prerequisite for doing the call.
LpcpFreeDataInfoMessage( PortObject,
CapturedReplyMessage.MessageId,
CapturedReplyMessage.CallbackId );
// Add an extra reference so LpcExitThread does not evaporate the
// pointer before we get to the wait below
ObReferenceObject( WakeupThread );
// 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;
}
LpcpReleaseLpcpLock();
// 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 );
ObDereferenceObject( WakeupThread );
// Dereference port object and return the system service status.
ObDereferenceObject( PortObject );
return Status;
}
NTSTATUS NtReplyWaitReplyPort (IN HANDLE PortHandle, IN OUT PPORT_MESSAGE ReplyMessage)
/*++
Routine Description:
A client and server process can send a reply to a previous message and
block waiting for a reply using the NtReplyWaitReplyPort service:
This service works the same as NtReplyPort, except that after delivering
the reply message, it blocks waiting for a reply to a previous message.
When the reply is received, it will be placed in the location specified
by the ReplyMessage parameter.
Arguments:
PortHandle - Specifies the handle of the communication port that the original message was received from.
ReplyMessage - Specifies a pointer to the reply message to be sent.
The ClientId and MessageId fields determine which thread will get the reply.
This buffer also receives any reply that comes back from the wait.
Return Value:
Status code that indicates whether or not the operation was successful.
--*/
{
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 {
ProbeForWrite( ReplyMessage, sizeof( *ReplyMessage ), sizeof( ULONG ));
CapturedReplyMessage = *ReplyMessage;
} except( EXCEPTION_EXECUTE_HANDLER ) {
return GetExceptionCode();
}
} else {
CapturedReplyMessage = *ReplyMessage;
}
// Make sure DataLength is valid with respect to header size and total length
if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
return STATUS_INVALID_PARAMETER;
}
// Make sure the user didn't give us a bogus reply message id
if (CapturedReplyMessage.MessageId == 0) {
return STATUS_INVALID_PARAMETER;
}
// Reference the communication port object by handle. Return status if unsuccessful.
Status = LpcpReferencePortObject( PortHandle, 0, PreviousMode, &PortObject );
if (!NT_SUCCESS( Status )) {
return Status;
}
LpcpSaveThread (PortObject);
// Validate the message length
if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
ObDereferenceObject( PortObject );
return STATUS_PORT_MESSAGE_TOO_LONG;
}
// 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.
LpcpAcquireLpcpLock();
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
if (Msg == NULL) {
LpcpReleaseLpcpLock();
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.
// We also fail this request if the caller isn't replying to a request
// message. For example, if the caller is replying to a connection request
if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) ||
((WakeupThread->LpcReplyMessage != NULL) &&
(((PLPCP_MESSAGE)(WakeupThread->LpcReplyMessage))->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)) {
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 );
LpcpReleaseLpcpLock();
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return STATUS_REPLY_MESSAGE_MISMATCH;
}
// Copy the reply message to the request message buffer.
// Do this before we actually fiddle with the wakeup threads fields.
// Otherwise we could mess up its state
try {
LpcpMoveMessage( &Msg->Request, &CapturedReplyMessage, (ReplyMessage + 1), LPC_REPLY, NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
LpcpFreeToPortZone( Msg, TRUE );
LpcpReleaseLpcpLock();
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return (Status = GetExceptionCode());
}
// At this point we know the thread is waiting for our reply
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 ));
// Locate and free the messsage from the port. This call use to
// test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
// prerequisite for doing the call.
LpcpFreeDataInfoMessage( PortObject, CapturedReplyMessage.MessageId, CapturedReplyMessage.CallbackId );
// Add an extra reference so LpcExitThread does not evaporate
// the pointer before we get to the wait below
ObReferenceObject( WakeupThread );
// 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 );
}
// Set ourselves up to get the following reply
CurrentThread->LpcReplyMessageId = CapturedReplyMessage.MessageId;
CurrentThread->LpcReplyMessage = NULL;
if ((CurrentThread->LpcReceivedMsgIdValid) &&
(CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
CurrentThread->LpcReceivedMessageId = 0;
CurrentThread->LpcReceivedMsgIdValid = FALSE;
}
LpcpReleaseLpcpLock();
// 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, 1, 1, FALSE );
ObDereferenceObject( WakeupThread );
// And wait for a reply
Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore, Executive, PreviousMode, FALSE, NULL );
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 guards 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.
LpcpAcquireLpcpLock();
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
LpcpReleaseLpcpLock();
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.
LpcpAcquireLpcpLock();
if (Msg->RepliedToThread != NULL) {
ObDereferenceObject( Msg->RepliedToThread );
Msg->RepliedToThread = NULL;
}
LpcpFreeToPortZone( Msg, TRUE );
LpcpReleaseLpcpLock();
}
}
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)
/*++
Routine Description:
This routine is used to copy data from a port message into the user supplied buffer.
Arguments:
PortHandle - Supplies the port from which the message is being read
Message - Supplies the message that we are trying to read
DataEntryIndex - Supplies the index of the port data entry in the preceeding message that we are reading
Buffer - Supplies the location into which the data is to be read
BufferSize - Supplies the size, in bytes, of the preceeding buffer
NumberOfBytesRead - Optionally returns the number of bytes read into the buffer
Return Value:
NTSTATUS - An appropriate status value
--*/
{
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)
/*++
Routine Description:
This routine is used to copy data from the user supplied buffer into the port message
Arguments:
PortHandle - Supplies the port into which the message is being written
Message - Supplies the message that we are trying to write
DataEntryIndex - Supplies the index of the port data entry in the preceeding message that we are writing
Buffer - Supplies the location into which the data is to be written
BufferSize - Supplies the size, in bytes, of the preceeding buffer
NumberOfBytesRead - Optionally returns the number of bytes written from the buffer
Return Value:
NTSTATUS - An appropriate status value
--*/
{
PAGED_CODE();
return LpcpCopyRequestData( TRUE, PortHandle, Message, DataEntryIndex, Buffer, BufferSize, NumberOfBytesWritten );
}
// Local support routine
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)
/*++
Routine Description:
This routine will copy data to or from the user supplied buffer and the port message data information buffer
Arguments:
WriteToMessageData - TRUE if the data is to be copied from the user buffer to the message and FALSE otherwise
PortHandle - Supplies the port into which the message is being manipulated
Message - Supplies the message that we are trying to manipulate
DataEntryIndex - Supplies the index of the port data entry in the preceeding message that we are transfering
Buffer - Supplies the location into which the data is to be transfered
BufferSize - Supplies the size, in bytes, of the preceeding buffer
NumberOfBytesRead - Optionally returns the number of bytes transfered from the buffer
Return Value:
NTSTATUS - An appropriate status value
--*/
{
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 {
// We are either reading or writing the user buffer
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;
}
// The message better have at least one data entry
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;
}
LpcpSaveThread (PortObject);
// 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 guards the LpcReplyMessage field of
// the thread and get the pointer to the message that the thread is waiting for a reply to.
LpcpAcquireLpcpLock();
// 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);
// Make sure the caller isn't asking for an index beyond what's in the message
if (DataInfo->CountDataEntries > DataEntryIndex) {
DataEntry = &DataInfo->DataEntries[ DataEntryIndex ];
CapturedDataEntry = *DataEntry;
if (CapturedDataEntry.Size >= BufferSize) {
Status = STATUS_SUCCESS;
}
}
}
}
if (!NT_SUCCESS( Status )) {
LpcpReleaseLpcpLock();
ObDereferenceObject( ClientThread );
ObDereferenceObject( PortObject );
return Status;
}
// Release the mutex that guards the LpcReplyMessage field
LpcpReleaseLpcpLock();
// 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;
}