Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1153 lines
37 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
lpcsend.c
Abstract:
Local Inter-Process Communication (LPC) request system services.
Author:
Steve Wood (stevewo) 15-May-1989
Revision History:
--*/
#include "lpcp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,LpcRequestPort)
#pragma alloc_text(PAGE,NtRequestPort)
#pragma alloc_text(PAGE,LpcRequestWaitReplyPort)
#pragma alloc_text(PAGE,NtRequestWaitReplyPort)
#endif
NTSTATUS
LpcRequestPort(
IN PVOID PortAddress,
IN PPORT_MESSAGE RequestMessage
)
{
PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
PLPCP_PORT_OBJECT QueuePort;
ULONG MsgType;
PLPCP_MESSAGE Msg;
PAGED_CODE();
//
// Get previous processor mode and validate parameters
//
if (RequestMessage->u2.s2.Type != 0) {
MsgType = RequestMessage->u2.s2.Type;
if (MsgType < LPC_DATAGRAM ||
MsgType > LPC_CLIENT_DIED
) {
return( STATUS_INVALID_PARAMETER );
}
}
else {
MsgType = LPC_DATAGRAM;
}
if (RequestMessage->u2.s2.DataInfoOffset != 0) {
return( STATUS_INVALID_PARAMETER );
}
//
// Validate the message length
//
if ((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength ||
(ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength
) {
return STATUS_PORT_MESSAGE_TOO_LONG;
}
//
// Allocate a message block
//
ExAcquireFastMutex( &LpcpLock );
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
ExReleaseFastMutex( &LpcpLock );
if (Msg == NULL) {
return( STATUS_NO_MEMORY );
}
//
// Fill in the message block.
//
Msg->RepliedToThread = NULL;
Msg->PortContext = NULL;
LpcpMoveMessage( &Msg->Request,
RequestMessage,
(RequestMessage + 1),
MsgType,
&PsGetCurrentThread()->Cid
);
//
// Acquire the global Lpc mutex that gaurds the LpcReplyMessage
// field of the thread and the request message queue. Stamp the
// request message with a serial number, insert the message at
// the tail of the request message queue
//
// This all needs to be performed with APCs disabled to avoid
// the situation where something gets put on the queue and this
// thread gets suspended before being able to release the semaphore.
//
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe( &LpcpLock );
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
QueuePort = PortObject->ConnectedPort;
if (QueuePort != NULL) {
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
Msg->PortContext = QueuePort->PortContext;
QueuePort = PortObject->ConnectionPort;
}
else
if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
QueuePort = PortObject->ConnectionPort;
}
}
}
else {
QueuePort = PortObject;
}
if (QueuePort != NULL) {
Msg->Request.MessageId = LpcpGenerateMessageId();
Msg->Request.CallbackId = 0;
PsGetCurrentThread()->LpcReplyMessageId = 0;
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
LpcpMessageTypeName[ Msg->Request.u2.s2.Type ],
Msg,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
QueuePort,
LpcpGetCreatorName( QueuePort )
));
//
// Release the mutex, increment the request message queue
// semaphore by one for the newly inserted request message,
// then exit the critical region.
//
ExReleaseFastMutexUnsafe( &LpcpLock );
KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
LPC_RELEASE_WAIT_INCREMENT,
1L,
FALSE
);
KeLeaveCriticalRegion();
return( STATUS_SUCCESS );
}
else {
LpcpFreeToPortZone( Msg, TRUE );
}
ExReleaseFastMutexUnsafe( &LpcpLock );
KeLeaveCriticalRegion();
return STATUS_PORT_DISCONNECTED;
}
NTSTATUS
NtRequestPort(
IN HANDLE PortHandle,
IN PPORT_MESSAGE RequestMessage
)
{
PLPCP_PORT_OBJECT PortObject;
PLPCP_PORT_OBJECT QueuePort;
PORT_MESSAGE CapturedRequestMessage;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PLPCP_MESSAGE Msg;
PAGED_CODE();
//
// Get previous processor mode and validate parameters
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForRead( RequestMessage,
sizeof( *RequestMessage ),
sizeof( ULONG )
);
CapturedRequestMessage = *RequestMessage;
}
except( EXCEPTION_EXECUTE_HANDLER ) {
return( GetExceptionCode() );
}
}
else {
CapturedRequestMessage = *RequestMessage;
}
if (CapturedRequestMessage.u2.s2.Type != 0) {
return( STATUS_INVALID_PARAMETER );
}
if (CapturedRequestMessage.u2.s2.DataInfoOffset != 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)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength ||
(ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength
) {
ObDereferenceObject( PortObject );
return STATUS_PORT_MESSAGE_TOO_LONG;
}
//
// Determine which port to queue the message to and get client
// port context if client sending to server. Also validate
// length of message being sent.
//
ExAcquireFastMutex( &LpcpLock );
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
ExReleaseFastMutex( &LpcpLock );
if (Msg == NULL) {
ObDereferenceObject( PortObject );
return( STATUS_NO_MEMORY );
}
Msg->RepliedToThread = NULL;
Msg->PortContext = NULL;
try {
LpcpMoveMessage( &Msg->Request,
&CapturedRequestMessage,
(RequestMessage + 1),
LPC_DATAGRAM,
&PsGetCurrentThread()->Cid
);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS( Status )) {
LpcpFreeToPortZone( Msg, FALSE );
ObDereferenceObject( PortObject );
return( Status );
}
//
// Acquire the global Lpc mutex that gaurds the LpcReplyMessage
// field of the thread and the request message queue. Stamp the
// request message with a serial number, insert the message at
// the tail of the request message queue and remember the address
// of the message in the LpcReplyMessage field for the current thread.
//
// This all needs to be performed with APCs disabled to avoid
// the situation where something gets put on the queue and this
// thread gets suspended before being able to release the semaphore.
//
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe( &LpcpLock );
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
QueuePort = PortObject->ConnectedPort;
if (QueuePort != NULL) {
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
Msg->PortContext = QueuePort->PortContext;
QueuePort = PortObject->ConnectionPort;
}
else
if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
QueuePort = PortObject->ConnectionPort;
}
}
}
else {
QueuePort = PortObject;
}
if (QueuePort != NULL) {
Msg->Request.MessageId = LpcpGenerateMessageId();
Msg->Request.CallbackId = 0;
PsGetCurrentThread()->LpcReplyMessageId = 0;
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
LpcpMessageTypeName[ Msg->Request.u2.s2.Type ],
Msg,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
QueuePort,
LpcpGetCreatorName( QueuePort )
));
//
// Release the mutex, increment the request message queue
// semaphore by one for the newly inserted request message,
// then exit the critical region.
//
ExReleaseFastMutexUnsafe( &LpcpLock );
KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
LPC_RELEASE_WAIT_INCREMENT,
1L,
FALSE
);
KeLeaveCriticalRegion();
ObDereferenceObject( PortObject );
return( Status );
}
else {
LpcpFreeToPortZone( Msg, TRUE );
}
ExReleaseFastMutexUnsafe( &LpcpLock );
KeLeaveCriticalRegion();
return STATUS_PORT_DISCONNECTED;
}
NTSTATUS
LpcRequestWaitReplyPort(
IN PVOID PortAddress,
IN PPORT_MESSAGE RequestMessage,
OUT PPORT_MESSAGE ReplyMessage
)
{
PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
PLPCP_PORT_OBJECT QueuePort;
PLPCP_PORT_OBJECT RundownPort;
PKSEMAPHORE ReleaseSemaphore;
NTSTATUS Status;
PLPCP_MESSAGE Msg;
PETHREAD CurrentThread;
PETHREAD WakeupThread;
BOOLEAN CallbackRequest;
PAGED_CODE();
CurrentThread = PsGetCurrentThread();
if (CurrentThread->LpcExitThreadCalled) {
return( STATUS_THREAD_IS_TERMINATING );
}
if (RequestMessage->u2.s2.Type == LPC_REQUEST) {
CallbackRequest = TRUE;
}
else {
CallbackRequest = FALSE;
switch (RequestMessage->u2.s2.Type) {
case 0 :
RequestMessage->u2.s2.Type = LPC_REQUEST;
break;
case LPC_CLIENT_DIED :
case LPC_PORT_CLOSED :
case LPC_EXCEPTION :
case LPC_DEBUG_EVENT :
case LPC_ERROR_EVENT :
break;
default :
return (STATUS_INVALID_PARAMETER);
}
}
//
// Validate the message length
//
if ((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength ||
(ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength
) {
return STATUS_PORT_MESSAGE_TOO_LONG;
}
//
// Determine which port to queue the message to and get client
// port context if client sending to server. Also validate
// length of message being sent.
//
ExAcquireFastMutex( &LpcpLock );
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
ExReleaseFastMutex( &LpcpLock );
if (Msg == NULL) {
return( STATUS_NO_MEMORY );
}
if (CallbackRequest) {
//
// Translate the ClientId from the request into a
// thread pointer. This is a referenced pointer to keep the thread
// from evaporating out from under us.
//
Status = PsLookupProcessThreadByCid( &RequestMessage->ClientId,
NULL,
&WakeupThread
);
if (!NT_SUCCESS( Status )) {
LpcpFreeToPortZone( Msg, FALSE );
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 (WakeupThread->LpcReplyMessageId != RequestMessage->MessageId
) {
LpcpFreeToPortZone( Msg, TRUE );
ExReleaseFastMutex( &LpcpLock );
ObDereferenceObject( WakeupThread );
return( STATUS_REPLY_MESSAGE_MISMATCH );
}
QueuePort = NULL;
Msg->PortContext = NULL;
if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
RundownPort = PortObject;
}
else {
RundownPort = PortObject->ConnectedPort;
if (RundownPort == NULL) {
LpcpFreeToPortZone( Msg, TRUE );
ExReleaseFastMutex( &LpcpLock );
ObDereferenceObject( WakeupThread );
return( STATUS_PORT_DISCONNECTED );
}
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
Msg->PortContext = RundownPort->PortContext;
}
}
//
// Allocate and initialize a request message
//
LpcpMoveMessage( &Msg->Request,
RequestMessage,
(RequestMessage + 1),
0,
&CurrentThread->Cid
);
Msg->Request.CallbackId = LpcpGenerateCallbackId();
LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
LpcpMessageTypeName[ Msg->Request.u2.s2.Type ],
Msg,
Msg->Request.MessageId,
Msg->Request.CallbackId,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName
));
Msg->RepliedToThread = WakeupThread;
WakeupThread->LpcReplyMessage = (PVOID)Msg;
//
// Remove the thread from the reply rundown list as we are sending a callback
//
if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
RemoveEntryList( &WakeupThread->LpcReplyChain );
InitializeListHead( &WakeupThread->LpcReplyChain );
}
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
CurrentThread->LpcReplyMessage = NULL;
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
ExReleaseFastMutex( &LpcpLock );
//
// Wake up the thread that is waiting for an answer to its request
// inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
//
ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
}
else {
LpcpMoveMessage( &Msg->Request,
RequestMessage,
(RequestMessage + 1),
0,
&CurrentThread->Cid
);
//
// Acquire the global Lpc mutex that gaurds the LpcReplyMessage
// field of the thread and the request message queue. Stamp the
// request message with a serial number, insert the message at
// the tail of the request message queue and remember the address
// of the message in the LpcReplyMessage field for the current thread.
//
ExAcquireFastMutex( &LpcpLock );
Msg->PortContext = NULL;
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
QueuePort = PortObject->ConnectedPort;
if (QueuePort == NULL) {
LpcpFreeToPortZone( Msg, TRUE );
ExReleaseFastMutex( &LpcpLock );
ObDereferenceObject( PortObject );
return( STATUS_PORT_DISCONNECTED );
}
RundownPort = QueuePort;
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
Msg->PortContext = QueuePort->PortContext;
QueuePort = PortObject->ConnectionPort;
}
else
if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
QueuePort = PortObject->ConnectionPort;
}
}
else {
QueuePort = PortObject;
RundownPort = PortObject;
}
Msg->RepliedToThread = NULL;
Msg->Request.MessageId = LpcpGenerateMessageId();
Msg->Request.CallbackId = 0;
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
CurrentThread->LpcReplyMessage = NULL;
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
LpcpMessageTypeName[ Msg->Request.u2.s2.Type ],
Msg,
Msg->Request.MessageId,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
QueuePort,
LpcpGetCreatorName( QueuePort )
));
ExReleaseFastMutex( &LpcpLock );
//
// Increment the request message queue semaphore by one for
// the newly inserted request message. Release the spin
// lock, while remaining at the dispatcher IRQL. Then wait for the
// reply to this request by waiting on the LpcReplySemaphore
// for the current thread.
//
ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
}
Status = KeReleaseWaitForSemaphore( ReleaseSemaphore,
&CurrentThread->LpcReplySemaphore,
WrLpcReply,
KernelMode
);
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;
}
}
//
// Acquire the LPC mutex. Remove the reply message from the current thread
//
ExAcquireFastMutex( &LpcpLock );
Msg = CurrentThread->LpcReplyMessage;
CurrentThread->LpcReplyMessage = NULL;
CurrentThread->LpcReplyMessageId = 0;
//
// Remove the thread from the reply rundown list in case we did not wakeup due to
// a reply
//
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
RemoveEntryList( &CurrentThread->LpcReplyChain );
InitializeListHead( &CurrentThread->LpcReplyChain );
}
#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
));
}
#endif
ExReleaseFastMutex( &LpcpLock );
//
// If the wait succeeded, copy the reply to the reply buffer.
//
if (Status == STATUS_SUCCESS ) {
if (Msg != NULL) {
LpcpMoveMessage( ReplyMessage,
&Msg->Request,
(&Msg->Request) + 1,
0,
NULL
);
//
// 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 );
}
else {
Status = STATUS_LPC_REPLY_LOST;
}
}
else {
//
// Wait failed, acquire the LPC mutex and free the message.
//
ExAcquireFastMutex( &LpcpLock );
if (Msg != NULL) {
LpcpFreeToPortZone( Msg, TRUE );
}
ExReleaseFastMutex( &LpcpLock );
}
return( Status );
}
NTSTATUS
NtRequestWaitReplyPort(
IN HANDLE PortHandle,
IN PPORT_MESSAGE RequestMessage,
OUT PPORT_MESSAGE ReplyMessage
)
{
PLPCP_PORT_OBJECT PortObject;
PLPCP_PORT_OBJECT QueuePort;
PLPCP_PORT_OBJECT RundownPort;
PORT_MESSAGE CapturedRequestMessage;
PKSEMAPHORE ReleaseSemaphore;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PLPCP_MESSAGE Msg;
PETHREAD CurrentThread;
PETHREAD WakeupThread;
BOOLEAN CallbackRequest;
PAGED_CODE();
CurrentThread = PsGetCurrentThread();
if (CurrentThread->LpcExitThreadCalled) {
return( STATUS_THREAD_IS_TERMINATING );
}
//
// Get previous processor mode and probe output arguments if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForRead( RequestMessage,
sizeof( *RequestMessage ),
sizeof( ULONG )
);
CapturedRequestMessage = *RequestMessage;
ProbeForWrite( ReplyMessage,
sizeof( *ReplyMessage ),
sizeof( ULONG )
);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
return Status;
}
}
else {
CapturedRequestMessage = *RequestMessage;
}
if (CapturedRequestMessage.u2.s2.Type == LPC_REQUEST) {
CallbackRequest = TRUE;
}
else
if (CapturedRequestMessage.u2.s2.Type != 0) {
return( STATUS_INVALID_PARAMETER );
}
else {
CallbackRequest = FALSE;
}
//
// 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)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength ||
(ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength
) {
ObDereferenceObject( PortObject );
return STATUS_PORT_MESSAGE_TOO_LONG;
}
//
// Determine which port to queue the message to and get client
// port context if client sending to server. Also validate
// length of message being sent.
//
ExAcquireFastMutex( &LpcpLock );
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
ExReleaseFastMutex( &LpcpLock );
if (Msg == NULL) {
ObDereferenceObject( PortObject );
return( STATUS_NO_MEMORY );
}
if (CallbackRequest) {
//
// Translate the ClientId from the request into a
// thread pointer. This is a referenced pointer to keep the thread
// from evaporating out from under us.
//
Status = PsLookupProcessThreadByCid( &CapturedRequestMessage.ClientId,
NULL,
&WakeupThread
);
if (!NT_SUCCESS( Status )) {
LpcpFreeToPortZone( Msg, FALSE );
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.
//
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 (WakeupThread->LpcReplyMessageId != CapturedRequestMessage.MessageId
) {
LpcpPrint(( "%s Attempted CallBack Request to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName
));
LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
CapturedRequestMessage.MessageId,
CapturedRequestMessage.ClientId.UniqueProcess,
CapturedRequestMessage.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 );
}
ExReleaseFastMutex( &LpcpLock );
try {
LpcpMoveMessage( &Msg->Request,
&CapturedRequestMessage,
(RequestMessage + 1),
LPC_REQUEST,
&CurrentThread->Cid
);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS( Status )) {
LpcpFreeToPortZone( Msg, FALSE );
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return( Status );
}
ExAcquireFastMutex( &LpcpLock );
QueuePort = NULL;
Msg->PortContext = NULL;
if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
RundownPort = PortObject;
}
else {
RundownPort = PortObject->ConnectedPort;
if (RundownPort == NULL) {
LpcpFreeToPortZone( Msg, TRUE );
ExReleaseFastMutex( &LpcpLock );
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return( STATUS_PORT_DISCONNECTED );
}
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
Msg->PortContext = RundownPort->PortContext;
}
}
Msg->Request.CallbackId = LpcpGenerateCallbackId();
LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
LpcpMessageTypeName[ Msg->Request.u2.s2.Type ],
Msg,
Msg->Request.MessageId,
Msg->Request.CallbackId,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName
));
Msg->RepliedToThread = WakeupThread;
WakeupThread->LpcReplyMessage = (PVOID)Msg;
//
// Remove the thread from the reply rundown list as we are sending a callback
//
if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
RemoveEntryList( &WakeupThread->LpcReplyChain );
InitializeListHead( &WakeupThread->LpcReplyChain );
}
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
CurrentThread->LpcReplyMessage = NULL;
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
ExReleaseFastMutex( &LpcpLock );
//
// Wake up the thread that is waiting for an answer to its request
// inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
//
ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
}
else {
try {
LpcpMoveMessage( &Msg->Request,
&CapturedRequestMessage,
(RequestMessage + 1),
LPC_REQUEST,
&CurrentThread->Cid
);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
LpcpFreeToPortZone( Msg, FALSE );
ObDereferenceObject( PortObject );
return( GetExceptionCode() );
}
ExAcquireFastMutex( &LpcpLock );
Msg->PortContext = NULL;
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
QueuePort = PortObject->ConnectedPort;
if (QueuePort == NULL) {
LpcpFreeToPortZone( Msg, TRUE );
ExReleaseFastMutex( &LpcpLock );
ObDereferenceObject( PortObject );
return( STATUS_PORT_DISCONNECTED );
}
RundownPort = QueuePort;
if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
Msg->PortContext = QueuePort->PortContext;
QueuePort = PortObject->ConnectionPort;
}
else
if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
QueuePort = PortObject->ConnectionPort;
}
}
else {
QueuePort = PortObject;
RundownPort = PortObject;
}
//
// Stamp the request message with a serial number, insert the message
// at the tail of the request message queue
//
Msg->RepliedToThread = NULL;
Msg->Request.MessageId = LpcpGenerateMessageId();
Msg->Request.CallbackId = 0;
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
CurrentThread->LpcReplyMessage = NULL;
InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
LpcpMessageTypeName[ Msg->Request.u2.s2.Type ],
Msg,
Msg->Request.MessageId,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
QueuePort,
LpcpGetCreatorName( QueuePort )
));
ExReleaseFastMutex( &LpcpLock );
//
// Increment the request message queue semaphore by one for
// the newly inserted request message.
//
ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
}
Status = KeReleaseWaitForSemaphore( ReleaseSemaphore,
&CurrentThread->LpcReplySemaphore,
WrLpcReply,
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;
}
}
//
// Acquire the LPC mutex. Remove the reply message from the current thread
//
ExAcquireFastMutex( &LpcpLock );
Msg = CurrentThread->LpcReplyMessage;
CurrentThread->LpcReplyMessage = NULL;
CurrentThread->LpcReplyMessageId = 0;
//
// Remove the thread from the reply rundown list in case we did not wakeup due to
// a reply
//
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
RemoveEntryList( &CurrentThread->LpcReplyChain );
InitializeListHead( &CurrentThread->LpcReplyChain );
}
#if DBG
if (Status == STATUS_SUCCESS && 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 the wait succeeded, copy the reply to the reply buffer.
//
if (Status == STATUS_SUCCESS) {
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.
//
if (Msg->Request.u2.s2.Type == LPC_REQUEST &&
Msg->Request.u2.s2.DataInfoOffset != 0
) {
LpcpSaveDataInfoMessage( PortObject, Msg );
}
else {
LpcpFreeToPortZone( Msg, FALSE );
}
}
else {
Status = STATUS_LPC_REPLY_LOST;
}
}
else {
//
// Wait failed, acquire the LPC mutex and free the message.
//
ExAcquireFastMutex( &LpcpLock );
LpcpTrace(( "%s NtRequestWaitReply wait failed - Status == %lx\n",
PsGetCurrentProcess()->ImageFileName,
Status
));
if (Msg != NULL) {
LpcpFreeToPortZone( Msg, TRUE );
}
ExReleaseFastMutex( &LpcpLock );
}
ObDereferenceObject( PortObject );
return( Status );
}