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.
 
 
 
 
 
 

762 lines
23 KiB

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