Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/lpc/send.c * PURPOSE: Local Procedure Call: Sending (Requests) * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>#define NDEBUG#include <debug.h>
/* PUBLIC FUNCTIONS **********************************************************/
/* * @implemented */NTSTATUSNTAPILpcRequestPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcMessage){ PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL; ULONG MessageType; PLPCP_MESSAGE Message; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread();
PAGED_CODE();
LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage);
/* Check if this is a non-datagram message */ if (LpcMessage->u2.s2.Type) { /* Get the message type */ MessageType = LpcpGetMessageType(LpcMessage);
/* Validate it */ if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED)) { /* Fail */ return STATUS_INVALID_PARAMETER; }
/* Mark this as a kernel-mode message only if we really came from it */ if ((PreviousMode == KernelMode) && (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) { /* We did, this is a kernel mode message */ MessageType |= LPC_KERNELMODE_MESSAGE; } } else { /* This is a datagram */ MessageType = LPC_DATAGRAM; }
/* Can't have data information on this type of call */ if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
/* Validate the message length */ if (((ULONG)LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcMessage->u1.s1.TotalLength <= (ULONG)LpcMessage->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; }
/* Allocate a new message */ Message = LpcpAllocateFromPortZone(); if (!Message) return STATUS_NO_MEMORY;
/* Clear the context */ Message->RepliedToThread = NULL; Message->PortContext = NULL;
/* Copy the message */ LpcpMoveMessage(&Message->Request, LpcMessage, LpcMessage + 1, MessageType, &Thread->Cid);
/* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Check if this is anything but a connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* The queue port is the connected port */ QueuePort = Port->ConnectedPort; if (QueuePort) { /* Check if this is a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Then copy the context */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Any other kind of port, use the connection port */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } }
/* If we have a connection port, reference it */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } } else { /* For connection ports, use the port itself */ QueuePort = PortObject; }
/* Make sure we have a port */ if (QueuePort) { /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0;
/* No Message ID for the thread */ Thread->LpcReplyMessageId = 0;
/* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
/* Release the lock and the semaphore */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
/* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); }
KeLeaveCriticalRegion();
/* We're done */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); return STATUS_SUCCESS; }
/* If we got here, then free the message and fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return STATUS_PORT_DISCONNECTED;}
/** @implemented*/NTSTATUSNTAPILpcRequestWaitReplyPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcRequest, OUT PPORT_MESSAGE LpcReply){ NTSTATUS Status = STATUS_SUCCESS; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)PortObject; PLPCP_PORT_OBJECT QueuePort, ReplyPort, ConnectionPort = NULL; USHORT MessageType; PLPCP_MESSAGE Message; BOOLEAN Callback = FALSE; PKSEMAPHORE Semaphore;
PAGED_CODE();
LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Messages: %p/%p. Type: %lx\n", Port, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest));
/* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
/* Check if this is an LPC Request */ MessageType = LpcpGetMessageType(LpcRequest); switch (MessageType) { /* No type, assume LPC request */ case 0: MessageType = LPC_REQUEST; break;
/* LPC request callback */ case LPC_REQUEST: Callback = TRUE; break;
/* Anything else, nothing to do */ case LPC_CLIENT_DIED: case LPC_PORT_CLOSED: case LPC_EXCEPTION: case LPC_DEBUG_EVENT: case LPC_ERROR_EVENT: break;
/* Invalid message type */ default: return STATUS_INVALID_PARAMETER; }
/* Set the request type */ LpcRequest->u2.s2.Type = MessageType;
/* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; }
/* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ return STATUS_NO_MEMORY; }
/* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } else { /* No callback, just copy the message */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, 0, &Thread->Cid);
/* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Right now clear the port context */ Message->PortContext = NULL;
/* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; }
/* This will be the rundown port */ ReplyPort = QueuePort;
/* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); return STATUS_PORT_DISCONNECTED; } }
/* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; }
/* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port;
/* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0;
/* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL;
/* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port);
/* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore;
/* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } }
/* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion();
/* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
/* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0;
/* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); }
/* Release the lock */ KeReleaseGuardedMutex(&LpcpLock);
/* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1);
/* Move the message */ LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL);
/* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Check if we replied to a thread */ if (Message->RepliedToThread) { /* Dereference */ ObDereferenceObject(Message->RepliedToThread); Message->RepliedToThread = NULL; }
/* Free the message */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } } else { /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 0); }
/* All done */ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %d\n", Port, Status);
/* Dereference the connection port */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status;}
/* * @implemented */NTSTATUSNTAPINtRequestPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest){ NTSTATUS Status; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); PORT_MESSAGE CapturedLpcRequest; PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL; ULONG MessageType; PLPCP_MESSAGE Message;
PAGED_CODE();
/* Check if the call comes from user mode */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Probe and capture the LpcRequest */ ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG)); CapturedLpcRequest = *(volatile PORT_MESSAGE*)LpcRequest; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { /* Access the LpcRequest directly */ CapturedLpcRequest = *LpcRequest; }
LPCTRACE(LPC_SEND_DEBUG, "Handle: %p. Message: %p. Type: %lx\n", PortHandle, LpcRequest, LpcpGetMessageType(&CapturedLpcRequest));
/* Get the message type */ MessageType = CapturedLpcRequest.u2.s2.Type | LPC_DATAGRAM;
/* Can't have data information on this type of call */ if (CapturedLpcRequest.u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER;
/* Validate the length */ if (((ULONG)CapturedLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)CapturedLpcRequest.u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; }
/* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status;
/* Validate the message length */ if (((ULONG)CapturedLpcRequest.u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)CapturedLpcRequest.u1.s1.TotalLength <= (ULONG)CapturedLpcRequest.u1.s1.DataLength)) { /* Fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; }
/* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ ObDereferenceObject(Port); return STATUS_NO_MEMORY; }
/* No callback, just copy the message */ _SEH2_TRY { /* Copy it */ LpcpMoveMessage(&Message->Request, &CapturedLpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Cleanup and return the exception code */ LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END;
/* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Right now clear the port context */ Message->PortContext = NULL;
/* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; }
/* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } }
/* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = Port; }
/* Reference QueuePort if we have it */ if (QueuePort && ObReferenceObjectSafe(QueuePort)) { /* Set sender's port */ Message->SenderPort = Port;
/* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0;
/* No Message ID for the thread */ Thread->LpcReplyMessageId = 0;
/* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry);
/* Release the lock and the semaphore */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(QueuePort->MsgQueue.Semaphore);
/* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); }
KeLeaveCriticalRegion();
/* Dereference objects */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(QueuePort); ObDereferenceObject(Port); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); return STATUS_SUCCESS; }
Status = STATUS_PORT_DISCONNECTED;
/* All done with a failure*/ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %d\n", Port, Status);
/* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
ObDereferenceObject(Port); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status;}
NTSTATUSNTAPILpcpVerifyMessageDataInfo( _In_ PPORT_MESSAGE Message, _Out_ PULONG NumberOfDataEntries){ PLPCP_DATA_INFO DataInfo; PUCHAR EndOfEntries;
/* Check if we have no data info at all */ if (Message->u2.s2.DataInfoOffset == 0) { *NumberOfDataEntries = 0; return STATUS_SUCCESS; }
/* Make sure the data info structure is within the message */ if (((ULONG)Message->u1.s1.TotalLength < sizeof(PORT_MESSAGE) + sizeof(LPCP_DATA_INFO)) || ((ULONG)Message->u2.s2.DataInfoOffset < sizeof(PORT_MESSAGE)) || ((ULONG)Message->u2.s2.DataInfoOffset > ((ULONG)Message->u1.s1.TotalLength - sizeof(LPCP_DATA_INFO)))) { return STATUS_INVALID_PARAMETER; }
/* Get a pointer to the data info */ DataInfo = LpcpGetDataInfoFromMessage(Message);
/* Make sure the full data info with all entries is within the message */ EndOfEntries = (PUCHAR)&DataInfo->Entries[DataInfo->NumberOfEntries]; if ((EndOfEntries > ((PUCHAR)Message + (ULONG)Message->u1.s1.TotalLength)) || (EndOfEntries < (PUCHAR)Message)) { return STATUS_INVALID_PARAMETER; }
*NumberOfDataEntries = DataInfo->NumberOfEntries; return STATUS_SUCCESS;}
/* * @implemented */NTSTATUSNTAPINtRequestWaitReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest, IN OUT PPORT_MESSAGE LpcReply){ NTSTATUS Status; PORT_MESSAGE CapturedLpcRequest; ULONG NumberOfDataEntries; PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; PLPCP_MESSAGE Message; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback; PKSEMAPHORE Semaphore; ULONG MessageType; PLPCP_DATA_INFO DataInfo;
PAGED_CODE();
/* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING;
/* Check for user mode access */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Probe and capture the LpcRequest */ ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG)); CapturedLpcRequest = *(volatile PORT_MESSAGE*)LpcRequest;
/* Probe the reply message for write */ ProbeForWrite(LpcReply, sizeof(*LpcReply), sizeof(ULONG));
/* Make sure the data entries in the request message are valid */ Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries); if (!NT_SUCCESS(Status)) { DPRINT1("LpcpVerifyMessageDataInfo failed\n"); _SEH2_YIELD(return Status); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Got exception\n"); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { CapturedLpcRequest = *LpcRequest; Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries); if (!NT_SUCCESS(Status)) { DPRINT1("LpcpVerifyMessageDataInfo failed\n"); return Status; } }
LPCTRACE(LPC_SEND_DEBUG, "Handle: %p. Messages: %p/%p. Type: %lx\n", PortHandle, LpcRequest, LpcReply, LpcpGetMessageType(&CapturedLpcRequest));
/* This flag is undocumented. Remove it before continuing */ CapturedLpcRequest.u2.s2.Type &= ~0x4000;
/* Check if this is an LPC Request */ if (LpcpGetMessageType(&CapturedLpcRequest) == LPC_REQUEST) { /* Then it's a callback */ Callback = TRUE; } else if (LpcpGetMessageType(&CapturedLpcRequest)) { /* This is a not kernel-mode message */ DPRINT1("Not a kernel-mode message!\n"); return STATUS_INVALID_PARAMETER; } else { /* This is a kernel-mode message without a callback */ CapturedLpcRequest.u2.s2.Type |= LPC_REQUEST; Callback = FALSE; }
/* Get the message type */ MessageType = CapturedLpcRequest.u2.s2.Type;
/* Due to the above probe, we know that TotalLength is positive */ ASSERT(CapturedLpcRequest.u1.s1.TotalLength >= 0);
/* Validate the length */ if ((((ULONG)(USHORT)CapturedLpcRequest.u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)CapturedLpcRequest.u1.s1.TotalLength)) { /* Fail */ DPRINT1("Invalid message length: %u, %u\n", CapturedLpcRequest.u1.s1.DataLength, CapturedLpcRequest.u1.s1.TotalLength); return STATUS_INVALID_PARAMETER; }
/* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status;
/* Validate the message length */ if (((ULONG)CapturedLpcRequest.u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)CapturedLpcRequest.u1.s1.TotalLength <= (ULONG)CapturedLpcRequest.u1.s1.DataLength)) { /* Fail */ DPRINT1("Invalid message length: %u, %u\n", CapturedLpcRequest.u1.s1.DataLength, CapturedLpcRequest.u1.s1.TotalLength); ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; }
/* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ DPRINT1("Failed to allocate a message!\n"); ObDereferenceObject(Port); return STATUS_NO_MEMORY; }
/* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); } else { /* No callback, just copy the message */ _SEH2_TRY { /* Check if we have data info entries */ if (LpcRequest->u2.s2.DataInfoOffset != 0) { /* Get the data info and check if the number of entries matches what we expect */ DataInfo = LpcpGetDataInfoFromMessage(LpcRequest); if (DataInfo->NumberOfEntries != NumberOfDataEntries) { LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); DPRINT1("NumberOfEntries has changed: %u, %u\n", DataInfo->NumberOfEntries, NumberOfDataEntries); _SEH2_YIELD(return STATUS_INVALID_PARAMETER); } }
/* Copy it */ LpcpMoveMessage(&Message->Request, &CapturedLpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Cleanup and return the exception code */ DPRINT1("Got exception!\n"); LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END;
/* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Right now clear the port context */ Message->PortContext = NULL;
/* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ DPRINT1("No connected port\n"); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; }
/* This will be the rundown port */ ReplyPort = QueuePort;
/* Check if this is a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context */ Message->PortContext = QueuePort->PortContext; }
if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ DPRINT1("No connection port\n"); LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } }
/* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; }
/* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port;
/* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0;
/* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL;
/* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port);
/* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore;
/* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } }
/* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion();
/* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode);
/* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock);
/* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0;
/* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); }
/* Release the lock */ KeReleaseGuardedMutex(&LpcpLock);
/* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1);
/* Move the message */ _SEH2_TRY { LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Got exception!\n"); Status = _SEH2_GetExceptionCode(); } _SEH2_END;
/* Check if this is an LPC request with data information */ if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) && (Message->Request.u2.s2.DataInfoOffset)) { /* Save the data information */ LpcpSaveDataInfoMessage(Port, Message, 0); } else { /* Otherwise, just free it */ LpcpFreeToPortZone(Message, 0); } } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } } else { /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 0); }
/* All done */ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %d\n", Port, Status); ObDereferenceObject(Port); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status;}
/* EOF */