1443 lines
38 KiB
C
1443 lines
38 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
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "lpcp.h"
|
||
|
||
NTSTATUS
|
||
LpcpCopyRequestData (
|
||
IN BOOLEAN WriteToMessageData,
|
||
IN HANDLE PortHandle,
|
||
IN PPORT_MESSAGE Message,
|
||
IN ULONG DataEntryIndex,
|
||
IN PVOID Buffer,
|
||
IN SIZE_T BufferSize,
|
||
OUT PSIZE_T NumberOfBytesCopied OPTIONAL
|
||
);
|
||
|
||
#if 0
|
||
VOID
|
||
LpcpAuditInvalidUse (
|
||
IN PVOID Context
|
||
);
|
||
#endif
|
||
|
||
#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)
|
||
#pragma alloc_text(PAGE,LpcpValidateClientPort)
|
||
#if 0
|
||
#pragma alloc_text(PAGE,LpcpAuditInvalidUse)
|
||
#endif
|
||
|
||
ULONG LpcMaxEventLogs = 10;
|
||
|
||
#define LPCP_PORT_NAME_MAX 256
|
||
|
||
typedef struct _LPC_WORK_CONTEXT {
|
||
WORK_QUEUE_ITEM WorkItem;
|
||
PVOID Buffer;
|
||
} LPC_WORK_CONTEXT, *PLPC_WORK_CONTEXT;
|
||
|
||
#endif
|
||
|
||
//
|
||
// The current number of events registered
|
||
//
|
||
|
||
ULONG LpcpEventCounts = 0;
|
||
|
||
|
||
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 {
|
||
|
||
ProbeForReadSmallStructure( 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 )) {
|
||
|
||
Status = ObReferenceObjectByHandle( PortHandle,
|
||
0,
|
||
LpcWaitablePortObjectType,
|
||
PreviousMode,
|
||
&PortObject,
|
||
NULL );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
|
||
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
|
||
|
||
if (Msg == NULL) {
|
||
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
//
|
||
// 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)
|
||
|
||
||
|
||
|
||
((LpcpGetThreadMessage(WakeupThread) != NULL) &&
|
||
(LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
|
||
|
||
||
|
||
|
||
(!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
|
||
|
||
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, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
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, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return 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 message 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;
|
||
PLPCP_PORT_OBJECT RundownPort;
|
||
|
||
PAGED_CODE();
|
||
|
||
CurrentThread = PsGetCurrentThread();
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForWriteSmallStructure( 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;
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
|
||
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
|
||
|
||
if (Msg == NULL) {
|
||
|
||
ObDereferenceObject( WakeupThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
//
|
||
// 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)
|
||
|
||
||
|
||
|
||
((LpcpGetThreadMessage(WakeupThread) != NULL) &&
|
||
(LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
|
||
|
||
||
|
||
|
||
(!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
|
||
|
||
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, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
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, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
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 message 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;
|
||
}
|
||
|
||
//
|
||
// Insert the current thread into the rundown queue
|
||
//
|
||
|
||
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
|
||
|
||
RundownPort = PortObject->ConnectedPort;
|
||
|
||
} else {
|
||
|
||
RundownPort = PortObject;
|
||
}
|
||
|
||
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
|
||
|
||
//
|
||
// Save the port context in the current thread, because
|
||
// it waits a reply from the same message
|
||
//
|
||
|
||
LpcpSetPortToThread(CurrentThread, PortObject);
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Remove the thread from the reply rundown list in case we did not wakeup due to
|
||
// a reply
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
|
||
|
||
RemoveEntryList( &CurrentThread->LpcReplyChain );
|
||
|
||
InitializeListHead( &CurrentThread->LpcReplyChain );
|
||
}
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
Msg = LpcpGetThreadMessage(CurrentThread);
|
||
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.
|
||
//
|
||
|
||
LpcpAcquireLpcpLockByThread(CurrentThread);
|
||
|
||
if (Msg->RepliedToThread != NULL) {
|
||
|
||
ObDereferenceObject( Msg->RepliedToThread );
|
||
|
||
Msg->RepliedToThread = NULL;
|
||
}
|
||
|
||
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
|
||
|
||
} else {
|
||
|
||
Status = STATUS_LPC_REPLY_LOST;
|
||
}
|
||
}
|
||
else {
|
||
LpcpReleaseLpcpLock();
|
||
}
|
||
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtReadRequestData (
|
||
IN HANDLE PortHandle,
|
||
IN PPORT_MESSAGE Message,
|
||
IN ULONG DataEntryIndex,
|
||
OUT PVOID Buffer,
|
||
IN SIZE_T BufferSize,
|
||
OUT PSIZE_T 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
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
status = LpcpCopyRequestData( FALSE,
|
||
PortHandle,
|
||
Message,
|
||
DataEntryIndex,
|
||
Buffer,
|
||
BufferSize,
|
||
NumberOfBytesRead );
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtWriteRequestData (
|
||
IN HANDLE PortHandle,
|
||
IN PPORT_MESSAGE Message,
|
||
IN ULONG DataEntryIndex,
|
||
IN PVOID Buffer,
|
||
IN SIZE_T BufferSize,
|
||
OUT PSIZE_T 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
|
||
|
||
NumberOfBytesWritten - Optionally returns the number of bytes written from
|
||
the buffer
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
status = LpcpCopyRequestData( TRUE,
|
||
PortHandle,
|
||
Message,
|
||
DataEntryIndex,
|
||
Buffer,
|
||
BufferSize,
|
||
NumberOfBytesWritten );
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
NTSTATUS
|
||
LpcpCopyRequestData (
|
||
IN BOOLEAN WriteToMessageData,
|
||
IN HANDLE PortHandle,
|
||
IN PPORT_MESSAGE Message,
|
||
IN ULONG DataEntryIndex,
|
||
IN PVOID Buffer,
|
||
IN SIZE_T BufferSize,
|
||
OUT PSIZE_T 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 transferring
|
||
|
||
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;
|
||
NTSTATUS Status;
|
||
PETHREAD ClientThread;
|
||
PPORT_DATA_INFORMATION DataInfo;
|
||
PPORT_DATA_ENTRY DataEntry;
|
||
PORT_MESSAGE CapturedMessage;
|
||
PORT_DATA_ENTRY CapturedDataEntry;
|
||
SIZE_T 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 );
|
||
}
|
||
|
||
ProbeForReadSmallStructure( Message,
|
||
sizeof( *Message ),
|
||
sizeof( ULONG ));
|
||
|
||
CapturedMessage = *Message;
|
||
|
||
if (ARGUMENT_PRESENT( NumberOfBytesCopied )) {
|
||
|
||
ProbeForWriteUlong_ptr( 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;
|
||
}
|
||
|
||
//
|
||
// 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) ||
|
||
!LpcpValidateClientPort(ClientThread, PortObject, LPCP_VALIDATE_REASON_WRONG_DATA) ) {
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Release the mutex that guards the LpcReplyMessage field
|
||
//
|
||
|
||
LpcpReleaseLpcpLock();
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
ObDereferenceObject( ClientThread );
|
||
ObDereferenceObject( PortObject );
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FASTCALL
|
||
LpcpValidateClientPort(
|
||
IN PETHREAD Thread,
|
||
IN PLPCP_PORT_OBJECT ReplyPort,
|
||
IN ULONG Reason
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine validates whether the reply for a request come from
|
||
an appropriate port
|
||
|
||
Arguments:
|
||
|
||
Thread - The thread waiting for a reply
|
||
|
||
ReplyPort - The port object that is replying
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the reply come from a valid port
|
||
|
||
Environment:
|
||
|
||
This is called holding the global LPC mutex.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLPCP_PORT_OBJECT PortThread;
|
||
|
||
PortThread = LpcpGetThreadPort(Thread);
|
||
|
||
//
|
||
// The thread must have a port set
|
||
//
|
||
|
||
if (PortThread == NULL) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// We only allow a port connected with the requestor.
|
||
// Also csrss is giving the server connection port to clients
|
||
// and we can have a client sending a request from a server connection port
|
||
// and csrss can reply with a server communication port
|
||
//
|
||
|
||
if ( ( ReplyPort == PortThread->ConnectionPort )
|
||
||
|
||
( ReplyPort == PortThread->ConnectedPort )
|
||
||
|
||
( ReplyPort == PortThread )
|
||
||
|
||
(
|
||
((ReplyPort->Flags & PORT_TYPE) == SERVER_COMMUNICATION_PORT)
|
||
&&
|
||
(ReplyPort->ConnectionPort == PortThread)
|
||
)
|
||
) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#if 0
|
||
if (LpcpEventCounts < LpcMaxEventLogs) {
|
||
|
||
PUNICODE_STRING StrReason;
|
||
POBJECT_NAME_INFORMATION ObjectNameInfo;
|
||
NTSTATUS Status;
|
||
ULONG Length;
|
||
PLPC_WORK_CONTEXT AuditItem;
|
||
|
||
if (PortThread->ConnectionPort) {
|
||
|
||
ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, LPCP_PORT_NAME_MAX + sizeof (UNICODE_STRING), 'ScpL');
|
||
if (ObjectNameInfo != NULL) {
|
||
|
||
Status = ObQueryNameString( PortThread->ConnectionPort,
|
||
ObjectNameInfo,
|
||
LPCP_PORT_NAME_MAX,
|
||
&Length
|
||
);
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Audit the event. Use a worker thread to avoid burning
|
||
// up a bunch of cycles since the global mutex is held.
|
||
//
|
||
|
||
StrReason = (PUNICODE_STRING)((ULONG_PTR) ObjectNameInfo + LPCP_PORT_NAME_MAX);
|
||
switch (Reason) {
|
||
case LPCP_VALIDATE_REASON_IMPERSONATION:
|
||
RtlInitUnicodeString( StrReason, L"impersonation" );
|
||
break;
|
||
|
||
case LPCP_VALIDATE_REASON_REPLY:
|
||
RtlInitUnicodeString( StrReason, L"reply" );
|
||
break;
|
||
|
||
case LPCP_VALIDATE_REASON_WRONG_DATA:
|
||
RtlInitUnicodeString( StrReason, L"data access" );
|
||
break;
|
||
}
|
||
|
||
AuditItem = ExAllocatePoolWithTag (NonPagedPool,
|
||
sizeof(LPC_WORK_CONTEXT),
|
||
'wcpL');
|
||
|
||
if (AuditItem != NULL) {
|
||
AuditItem->Buffer = (PVOID) ObjectNameInfo;
|
||
ExInitializeWorkItem (&AuditItem->WorkItem,
|
||
LpcpAuditInvalidUse,
|
||
(PVOID) AuditItem);
|
||
|
||
ExQueueWorkItem (&AuditItem->WorkItem, DelayedWorkQueue);
|
||
LpcpEventCounts += 1;
|
||
}
|
||
else {
|
||
ExFreePool (ObjectNameInfo);
|
||
}
|
||
}
|
||
else {
|
||
ExFreePool (ObjectNameInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if DBG
|
||
if (LpcpStopOnReplyMismatch) {
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
|
||
}
|
||
#endif
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
#if 0
|
||
|
||
VOID
|
||
LpcpAuditInvalidUse (
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the worker routine which logs security items.
|
||
|
||
Arguments:
|
||
|
||
Context - Supplies a pointer to the LPC_WORK_CONTEXT for the audit event.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, PASSIVE_LEVEL.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUNICODE_STRING StrReason;
|
||
POBJECT_NAME_INFORMATION ObjectNameInfo;
|
||
PLPC_WORK_CONTEXT AuditItem;
|
||
|
||
PAGED_CODE();
|
||
|
||
AuditItem = (PLPC_WORK_CONTEXT) Context;
|
||
|
||
ObjectNameInfo = (POBJECT_NAME_INFORMATION) AuditItem->Buffer;
|
||
|
||
StrReason = (PUNICODE_STRING)((ULONG_PTR) ObjectNameInfo + LPCP_PORT_NAME_MAX);
|
||
|
||
SeAuditLPCInvalidUse (StrReason, &ObjectNameInfo->Name);
|
||
|
||
ExFreePool (ObjectNameInfo);
|
||
|
||
ExFreePool (AuditItem);
|
||
}
|
||
#endif
|