xref: /reactos/ntoskrnl/lpc/reply.c (revision 1734f297)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/lpc/reply.c
5  * PURPOSE:         Local Procedure Call: Receive (Replies)
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* PRIVATE FUNCTIONS *********************************************************/
16 
17 VOID
18 NTAPI
19 LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
20                         IN ULONG MessageId,
21                         IN ULONG CallbackId,
22                         IN CLIENT_ID ClientId)
23 {
24     PLPCP_MESSAGE Message;
25     PLIST_ENTRY ListHead, NextEntry;
26 
27     /* Check if the port we want is the connection port */
28     if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
29     {
30         /* Use it */
31         Port = Port->ConnectionPort;
32         if (!Port) return;
33     }
34 
35     /* Loop the list */
36     ListHead = &Port->LpcDataInfoChainHead;
37     NextEntry = ListHead->Flink;
38     while (ListHead != NextEntry)
39     {
40         /* Get the message */
41         Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
42 
43         /* Make sure it matches */
44         if ((Message->Request.MessageId == MessageId) &&
45             (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread) &&
46             (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess))
47         {
48             /* Unlink and free it */
49             RemoveEntryList(&Message->Entry);
50             InitializeListHead(&Message->Entry);
51             LpcpFreeToPortZone(Message, LPCP_LOCK_HELD);
52             break;
53         }
54 
55         /* Go to the next entry */
56         NextEntry = NextEntry->Flink;
57     }
58 }
59 
60 VOID
61 NTAPI
62 LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port,
63                         IN PLPCP_MESSAGE Message,
64                         IN ULONG LockFlags)
65 {
66     BOOLEAN LockHeld = (LockFlags & LPCP_LOCK_HELD);
67 
68     PAGED_CODE();
69 
70     /* Acquire the lock */
71     if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
72 
73     /* Check if the port we want is the connection port */
74     if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
75     {
76         /* Use it */
77         Port = Port->ConnectionPort;
78         if (!Port)
79         {
80             /* Release the lock and return */
81             if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
82             return;
83         }
84     }
85 
86     /* Link the message */
87     InsertTailList(&Port->LpcDataInfoChainHead, &Message->Entry);
88 
89     /* Release the lock */
90     if (!LockHeld) KeReleaseGuardedMutex(&LpcpLock);
91 }
92 
93 PLPCP_MESSAGE
94 NTAPI
95 LpcpFindDataInfoMessage(
96     IN PLPCP_PORT_OBJECT Port,
97     IN ULONG MessageId,
98     IN LPC_CLIENT_ID ClientId)
99 {
100     PLPCP_MESSAGE Message;
101     PLIST_ENTRY ListEntry;
102 
103     PAGED_CODE();
104 
105     /* Check if the port we want is the connection port */
106     if ((Port->Flags & LPCP_PORT_TYPE_MASK) > LPCP_UNCONNECTED_PORT)
107     {
108         /* Use it */
109         Port = Port->ConnectionPort;
110         if (!Port)
111         {
112             /* Return NULL */
113             return NULL;
114         }
115     }
116 
117     /* Loop all entries in the list */
118     for (ListEntry = Port->LpcDataInfoChainHead.Flink;
119          ListEntry != &Port->LpcDataInfoChainHead;
120          ListEntry = ListEntry->Flink)
121     {
122         Message = CONTAINING_RECORD(ListEntry, LPCP_MESSAGE, Entry);
123 
124         /* Check if this is the desired message */
125         if ((Message->Request.MessageId == MessageId) &&
126             (Message->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) &&
127             (Message->Request.ClientId.UniqueThread == ClientId.UniqueThread))
128         {
129             /* It is, return it */
130             return Message;
131         }
132     }
133 
134     return NULL;
135 }
136 
137 VOID
138 NTAPI
139 LpcpMoveMessage(IN PPORT_MESSAGE Destination,
140                 IN PPORT_MESSAGE Origin,
141                 IN PVOID Data,
142                 IN ULONG MessageType,
143                 IN PCLIENT_ID ClientId)
144 {
145     LPCTRACE((LPC_REPLY_DEBUG | LPC_SEND_DEBUG),
146              "Destination/Origin: %p/%p. Data: %p. Length: %lx\n",
147              Destination,
148              Origin,
149              Data,
150              Origin->u1.Length);
151 
152     /* Set the Message size */
153     Destination->u1.Length = Origin->u1.Length;
154 
155     /* Set the Message Type */
156     Destination->u2.s2.Type = !MessageType ?
157                               Origin->u2.s2.Type : MessageType & 0xFFFF;
158 
159     /* Check if we have a Client ID */
160     if (ClientId)
161     {
162         /* Set the Client ID */
163         Destination->ClientId.UniqueProcess = ClientId->UniqueProcess;
164         Destination->ClientId.UniqueThread = ClientId->UniqueThread;
165     }
166     else
167     {
168         /* Otherwise, copy it */
169         Destination->ClientId.UniqueProcess = Origin->ClientId.UniqueProcess;
170         Destination->ClientId.UniqueThread = Origin->ClientId.UniqueThread;
171     }
172 
173     /* Copy the MessageId and ClientViewSize */
174     Destination->MessageId = Origin->MessageId;
175     Destination->ClientViewSize = Origin->ClientViewSize;
176 
177     /* Copy the Message Data */
178     RtlCopyMemory(Destination + 1,
179                   Data,
180                   ALIGN_UP_BY(Destination->u1.s1.DataLength, sizeof(ULONG)));
181 }
182 
183 /* PUBLIC FUNCTIONS **********************************************************/
184 
185 /*
186  * @implemented
187  */
188 NTSTATUS
189 NTAPI
190 NtReplyPort(IN HANDLE PortHandle,
191             IN PPORT_MESSAGE ReplyMessage)
192 {
193     NTSTATUS Status;
194     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
195     PORT_MESSAGE CapturedReplyMessage;
196     PLPCP_PORT_OBJECT Port;
197     PLPCP_MESSAGE Message;
198     PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
199 
200     PAGED_CODE();
201     LPCTRACE(LPC_REPLY_DEBUG,
202              "Handle: %p. Message: %p.\n",
203              PortHandle,
204              ReplyMessage);
205 
206     /* Check if the call comes from user mode */
207     if (PreviousMode != KernelMode)
208     {
209         _SEH2_TRY
210         {
211             ProbeForRead(ReplyMessage, sizeof(*ReplyMessage), sizeof(ULONG));
212             CapturedReplyMessage = *(volatile PORT_MESSAGE*)ReplyMessage;
213         }
214         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
215         {
216             _SEH2_YIELD(return _SEH2_GetExceptionCode());
217         }
218         _SEH2_END;
219     }
220     else
221     {
222         CapturedReplyMessage = *ReplyMessage;
223     }
224 
225     /* Validate its length */
226     if (((ULONG)CapturedReplyMessage.u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
227          (ULONG)CapturedReplyMessage.u1.s1.TotalLength)
228     {
229         /* Fail */
230         return STATUS_INVALID_PARAMETER;
231     }
232 
233     /* Make sure it has a valid ID */
234     if (!CapturedReplyMessage.MessageId) return STATUS_INVALID_PARAMETER;
235 
236     /* Get the Port object */
237     Status = ObReferenceObjectByHandle(PortHandle,
238                                        0,
239                                        LpcPortObjectType,
240                                        PreviousMode,
241                                        (PVOID*)&Port,
242                                        NULL);
243     if (!NT_SUCCESS(Status)) return Status;
244 
245     /* Validate its length in respect to the port object */
246     if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > Port->MaxMessageLength) ||
247         ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <=
248          (ULONG)CapturedReplyMessage.u1.s1.DataLength))
249     {
250         /* Too large, fail */
251         ObDereferenceObject(Port);
252         return STATUS_PORT_MESSAGE_TOO_LONG;
253     }
254 
255     /* Get the ETHREAD corresponding to it */
256     Status = PsLookupProcessThreadByCid(&CapturedReplyMessage.ClientId,
257                                         NULL,
258                                         &WakeupThread);
259     if (!NT_SUCCESS(Status))
260     {
261         /* No thread found, fail */
262         ObDereferenceObject(Port);
263         return Status;
264     }
265 
266     /* Allocate a message from the port zone */
267     Message = LpcpAllocateFromPortZone();
268     if (!Message)
269     {
270         /* Fail if we couldn't allocate a message */
271         ObDereferenceObject(WakeupThread);
272         ObDereferenceObject(Port);
273         return STATUS_NO_MEMORY;
274     }
275 
276     /* Keep the lock acquired */
277     KeAcquireGuardedMutex(&LpcpLock);
278 
279     /* Make sure this is the reply the thread is waiting for */
280     if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) ||
281         ((LpcpGetMessageFromThread(WakeupThread)) &&
282         (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)-> Request)
283             != LPC_REQUEST)))
284     {
285         /* It isn't, fail */
286         LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
287         ObDereferenceObject(WakeupThread);
288         ObDereferenceObject(Port);
289         return STATUS_REPLY_MESSAGE_MISMATCH;
290     }
291 
292     /* Copy the message */
293     _SEH2_TRY
294     {
295         LpcpMoveMessage(&Message->Request,
296                         &CapturedReplyMessage,
297                         ReplyMessage + 1,
298                         LPC_REPLY,
299                         NULL);
300     }
301     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
302     {
303         /* Cleanup and return the exception code */
304         LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
305         ObDereferenceObject(WakeupThread);
306         ObDereferenceObject(Port);
307         _SEH2_YIELD(return _SEH2_GetExceptionCode());
308     }
309     _SEH2_END;
310 
311     /* Reference the thread while we use it */
312     ObReferenceObject(WakeupThread);
313     Message->RepliedToThread = WakeupThread;
314 
315     /* Set this as the reply message */
316     WakeupThread->LpcReplyMessageId = 0;
317     WakeupThread->LpcReplyMessage = (PVOID)Message;
318 
319     /* Check if we have messages on the reply chain */
320     if (!(WakeupThread->LpcExitThreadCalled) &&
321         !(IsListEmpty(&WakeupThread->LpcReplyChain)))
322     {
323         /* Remove us from it and reinitialize it */
324         RemoveEntryList(&WakeupThread->LpcReplyChain);
325         InitializeListHead(&WakeupThread->LpcReplyChain);
326     }
327 
328     /* Check if this is the message the thread had received */
329     if ((Thread->LpcReceivedMsgIdValid) &&
330         (Thread->LpcReceivedMessageId == CapturedReplyMessage.MessageId))
331     {
332         /* Clear this data */
333         Thread->LpcReceivedMessageId = 0;
334         Thread->LpcReceivedMsgIdValid = FALSE;
335     }
336 
337     /* Free any data information */
338     LpcpFreeDataInfoMessage(Port,
339                             CapturedReplyMessage.MessageId,
340                             CapturedReplyMessage.CallbackId,
341                             CapturedReplyMessage.ClientId);
342 
343     /* Release the lock and release the LPC semaphore to wake up waiters */
344     KeReleaseGuardedMutex(&LpcpLock);
345     LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
346 
347     /* Now we can let go of the thread */
348     ObDereferenceObject(WakeupThread);
349 
350     /* Dereference port object */
351     ObDereferenceObject(Port);
352     return Status;
353 }
354 
355 /*
356  * @implemented
357  */
358 NTSTATUS
359 NTAPI
360 NtReplyWaitReceivePortEx(IN HANDLE PortHandle,
361                          OUT PVOID *PortContext OPTIONAL,
362                          IN PPORT_MESSAGE ReplyMessage OPTIONAL,
363                          OUT PPORT_MESSAGE ReceiveMessage,
364                          IN PLARGE_INTEGER Timeout OPTIONAL)
365 {
366     NTSTATUS Status;
367     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(), WaitMode = PreviousMode;
368     PORT_MESSAGE CapturedReplyMessage;
369     LARGE_INTEGER CapturedTimeout;
370     PLPCP_PORT_OBJECT Port, ReceivePort, ConnectionPort = NULL;
371     PLPCP_MESSAGE Message;
372     PETHREAD Thread = PsGetCurrentThread(), WakeupThread;
373     PLPCP_CONNECTION_MESSAGE ConnectMessage;
374     ULONG ConnectionInfoLength;
375 
376     PAGED_CODE();
377     LPCTRACE(LPC_REPLY_DEBUG,
378              "Handle: %p. Messages: %p/%p. Context: %p\n",
379              PortHandle,
380              ReplyMessage,
381              ReceiveMessage,
382              PortContext);
383 
384     /* Check if the call comes from user mode */
385     if (PreviousMode != KernelMode)
386     {
387         _SEH2_TRY
388         {
389             if (PortContext != NULL)
390                 ProbeForWritePointer(PortContext);
391 
392             if (ReplyMessage != NULL)
393             {
394                 ProbeForRead(ReplyMessage, sizeof(*ReplyMessage), sizeof(ULONG));
395                 CapturedReplyMessage = *(volatile PORT_MESSAGE*)ReplyMessage;
396             }
397 
398             if (Timeout != NULL)
399             {
400                 ProbeForReadLargeInteger(Timeout);
401                 CapturedTimeout = *(volatile LARGE_INTEGER*)Timeout;
402                 Timeout = &CapturedTimeout;
403             }
404         }
405         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
406         {
407             _SEH2_YIELD(return _SEH2_GetExceptionCode());
408         }
409         _SEH2_END;
410     }
411     else
412     {
413         /* If this is a system thread, then let it page out its stack */
414         if (Thread->SystemThread) WaitMode = UserMode;
415 
416         if (ReplyMessage != NULL)
417             CapturedReplyMessage = *ReplyMessage;
418     }
419 
420     /* Check if caller has a reply message */
421     if (ReplyMessage)
422     {
423         /* Validate its length */
424         if (((ULONG)CapturedReplyMessage.u1.s1.DataLength + sizeof(PORT_MESSAGE)) >
425              (ULONG)CapturedReplyMessage.u1.s1.TotalLength)
426         {
427             /* Fail */
428             return STATUS_INVALID_PARAMETER;
429         }
430 
431         /* Make sure it has a valid ID */
432         if (!CapturedReplyMessage.MessageId) return STATUS_INVALID_PARAMETER;
433     }
434 
435     /* Get the Port object */
436     Status = ObReferenceObjectByHandle(PortHandle,
437                                        0,
438                                        LpcPortObjectType,
439                                        PreviousMode,
440                                        (PVOID*)&Port,
441                                        NULL);
442     if (!NT_SUCCESS(Status)) return Status;
443 
444     /* Check if the caller has a reply message */
445     if (ReplyMessage)
446     {
447         /* Validate its length in respect to the port object */
448         if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > Port->MaxMessageLength) ||
449             ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <=
450              (ULONG)CapturedReplyMessage.u1.s1.DataLength))
451         {
452             /* Too large, fail */
453             ObDereferenceObject(Port);
454             return STATUS_PORT_MESSAGE_TOO_LONG;
455         }
456     }
457 
458     /* Check if this is anything but a client port */
459     if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CLIENT_PORT)
460     {
461         /* Check if this is the connection port */
462         if (Port->ConnectionPort == Port)
463         {
464             /* Use this port */
465             ConnectionPort = ReceivePort = Port;
466             ObReferenceObject(ConnectionPort);
467         }
468         else
469         {
470             /* Acquire the lock */
471             KeAcquireGuardedMutex(&LpcpLock);
472 
473             /* Get the port */
474             ConnectionPort = ReceivePort = Port->ConnectionPort;
475             if (!ConnectionPort)
476             {
477                 /* Fail */
478                 KeReleaseGuardedMutex(&LpcpLock);
479                 ObDereferenceObject(Port);
480                 return STATUS_PORT_DISCONNECTED;
481             }
482 
483             /* Release lock and reference */
484             ObReferenceObject(ConnectionPort);
485             KeReleaseGuardedMutex(&LpcpLock);
486         }
487     }
488     else
489     {
490         /* Otherwise, use the port itself */
491         ReceivePort = Port;
492     }
493 
494     /* Check if the caller gave a reply message */
495     if (ReplyMessage)
496     {
497         /* Get the ETHREAD corresponding to it */
498         Status = PsLookupProcessThreadByCid(&CapturedReplyMessage.ClientId,
499                                             NULL,
500                                             &WakeupThread);
501         if (!NT_SUCCESS(Status))
502         {
503             /* No thread found, fail */
504             ObDereferenceObject(Port);
505             if (ConnectionPort) ObDereferenceObject(ConnectionPort);
506             return Status;
507         }
508 
509         /* Allocate a message from the port zone */
510         Message = LpcpAllocateFromPortZone();
511         if (!Message)
512         {
513             /* Fail if we couldn't allocate a message */
514             if (ConnectionPort) ObDereferenceObject(ConnectionPort);
515             ObDereferenceObject(WakeupThread);
516             ObDereferenceObject(Port);
517             return STATUS_NO_MEMORY;
518         }
519 
520         /* Keep the lock acquired */
521         KeAcquireGuardedMutex(&LpcpLock);
522 
523         /* Make sure this is the reply the thread is waiting for */
524         if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId) ||
525             ((LpcpGetMessageFromThread(WakeupThread)) &&
526              (LpcpGetMessageType(&LpcpGetMessageFromThread(WakeupThread)->Request)
527                 != LPC_REQUEST)))
528         {
529             /* It isn't, fail */
530             LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
531             if (ConnectionPort) ObDereferenceObject(ConnectionPort);
532             ObDereferenceObject(WakeupThread);
533             ObDereferenceObject(Port);
534             return STATUS_REPLY_MESSAGE_MISMATCH;
535         }
536 
537         /* Copy the message */
538         _SEH2_TRY
539         {
540             LpcpMoveMessage(&Message->Request,
541                             &CapturedReplyMessage,
542                             ReplyMessage + 1,
543                             LPC_REPLY,
544                             NULL);
545         }
546         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
547         {
548             /* Cleanup and return the exception code */
549             LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
550             if (ConnectionPort) ObDereferenceObject(ConnectionPort);
551             ObDereferenceObject(WakeupThread);
552             ObDereferenceObject(Port);
553             _SEH2_YIELD(return _SEH2_GetExceptionCode());
554         }
555         _SEH2_END;
556 
557         /* Reference the thread while we use it */
558         ObReferenceObject(WakeupThread);
559         Message->RepliedToThread = WakeupThread;
560 
561         /* Set this as the reply message */
562         WakeupThread->LpcReplyMessageId = 0;
563         WakeupThread->LpcReplyMessage = (PVOID)Message;
564 
565         /* Check if we have messages on the reply chain */
566         if (!(WakeupThread->LpcExitThreadCalled) &&
567             !(IsListEmpty(&WakeupThread->LpcReplyChain)))
568         {
569             /* Remove us from it and reinitialize it */
570             RemoveEntryList(&WakeupThread->LpcReplyChain);
571             InitializeListHead(&WakeupThread->LpcReplyChain);
572         }
573 
574         /* Check if this is the message the thread had received */
575         if ((Thread->LpcReceivedMsgIdValid) &&
576             (Thread->LpcReceivedMessageId == CapturedReplyMessage.MessageId))
577         {
578             /* Clear this data */
579             Thread->LpcReceivedMessageId = 0;
580             Thread->LpcReceivedMsgIdValid = FALSE;
581         }
582 
583         /* Free any data information */
584         LpcpFreeDataInfoMessage(Port,
585                                 CapturedReplyMessage.MessageId,
586                                 CapturedReplyMessage.CallbackId,
587                                 CapturedReplyMessage.ClientId);
588 
589         /* Release the lock and release the LPC semaphore to wake up waiters */
590         KeReleaseGuardedMutex(&LpcpLock);
591         LpcpCompleteWait(&WakeupThread->LpcReplySemaphore);
592 
593         /* Now we can let go of the thread */
594         ObDereferenceObject(WakeupThread);
595     }
596 
597     /* Now wait for someone to reply to us */
598     LpcpReceiveWait(ReceivePort->MsgQueue.Semaphore, WaitMode);
599     if (Status != STATUS_SUCCESS) goto Cleanup;
600 
601     /* Wait done, get the LPC lock */
602     KeAcquireGuardedMutex(&LpcpLock);
603 
604     /* Check if we've received nothing */
605     if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
606     {
607         /* Check if this was a waitable port and wake it */
608         if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
609         {
610             /* Reset its event */
611             KeClearEvent(&ReceivePort->WaitEvent);
612         }
613 
614         /* Release the lock and fail */
615         KeReleaseGuardedMutex(&LpcpLock);
616         if (ConnectionPort) ObDereferenceObject(ConnectionPort);
617         ObDereferenceObject(Port);
618         return STATUS_UNSUCCESSFUL;
619     }
620 
621     /* Get the message on the queue */
622     Message = CONTAINING_RECORD(RemoveHeadList(&ReceivePort->MsgQueue.ReceiveHead),
623                                 LPCP_MESSAGE,
624                                 Entry);
625 
626     /* Check if the queue is empty now */
627     if (IsListEmpty(&ReceivePort->MsgQueue.ReceiveHead))
628     {
629         /* Check if this was a waitable port */
630         if (ReceivePort->Flags & LPCP_WAITABLE_PORT)
631         {
632             /* Reset its event */
633             KeClearEvent(&ReceivePort->WaitEvent);
634         }
635     }
636 
637     /* Re-initialize the message's list entry */
638     InitializeListHead(&Message->Entry);
639 
640     /* Set this as the received message */
641     Thread->LpcReceivedMessageId = Message->Request.MessageId;
642     Thread->LpcReceivedMsgIdValid = TRUE;
643 
644     _SEH2_TRY
645     {
646         /* Check if this was a connection request */
647         if (LpcpGetMessageType(&Message->Request) == LPC_CONNECTION_REQUEST)
648         {
649             /* Get the connection message */
650             ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
651             LPCTRACE(LPC_REPLY_DEBUG,
652                      "Request Messages: %p/%p\n",
653                      Message,
654                      ConnectMessage);
655 
656             /* Get its length */
657             ConnectionInfoLength = Message->Request.u1.s1.DataLength -
658                                    sizeof(LPCP_CONNECTION_MESSAGE);
659 
660             /* Return it as the receive message */
661             *ReceiveMessage = Message->Request;
662 
663             /* Clear our stack variable so the message doesn't get freed */
664             Message = NULL;
665 
666             /* Setup the receive message */
667             ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof(PORT_MESSAGE) +
668                                                          ConnectionInfoLength);
669             ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
670             RtlCopyMemory(ReceiveMessage + 1,
671                           ConnectMessage + 1,
672                           ConnectionInfoLength);
673 
674             /* Clear the port context if the caller requested one */
675             if (PortContext) *PortContext = NULL;
676         }
677         else if (LpcpGetMessageType(&Message->Request) != LPC_REPLY)
678         {
679             /* Otherwise, this is a new message or event */
680             LPCTRACE(LPC_REPLY_DEBUG,
681                      "Non-Reply Messages: %p/%p\n",
682                      &Message->Request,
683                      (&Message->Request) + 1);
684 
685             /* Copy it */
686             LpcpMoveMessage(ReceiveMessage,
687                             &Message->Request,
688                             (&Message->Request) + 1,
689                             0,
690                             NULL);
691 
692             /* Return its context */
693             if (PortContext) *PortContext = Message->PortContext;
694 
695             /* And check if it has data information */
696             if (Message->Request.u2.s2.DataInfoOffset)
697             {
698                 /* It does, save it, and don't free the message below */
699                 LpcpSaveDataInfoMessage(Port, Message, LPCP_LOCK_HELD);
700                 Message = NULL;
701             }
702         }
703         else
704         {
705             /* This is a reply message, should never happen! */
706             ASSERT(FALSE);
707         }
708     }
709     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
710     {
711         Status = _SEH2_GetExceptionCode();
712     }
713     _SEH2_END;
714 
715     /* Check if we have a message pointer here */
716     if (Message)
717     {
718         /* Free it and release the lock */
719         LpcpFreeToPortZone(Message, LPCP_LOCK_HELD | LPCP_LOCK_RELEASE);
720     }
721     else
722     {
723         /* Just release the lock */
724         KeReleaseGuardedMutex(&LpcpLock);
725     }
726 
727 Cleanup:
728     /* All done, dereference the port and return the status */
729     LPCTRACE(LPC_REPLY_DEBUG,
730              "Port: %p. Status: %d\n",
731              Port,
732              Status);
733     if (ConnectionPort) ObDereferenceObject(ConnectionPort);
734     ObDereferenceObject(Port);
735     return Status;
736 }
737 
738 /*
739  * @implemented
740  */
741 NTSTATUS
742 NTAPI
743 NtReplyWaitReceivePort(IN HANDLE PortHandle,
744                        OUT PVOID *PortContext OPTIONAL,
745                        IN PPORT_MESSAGE ReplyMessage OPTIONAL,
746                        OUT PPORT_MESSAGE ReceiveMessage)
747 {
748     /* Call the newer API */
749     return NtReplyWaitReceivePortEx(PortHandle,
750                                     PortContext,
751                                     ReplyMessage,
752                                     ReceiveMessage,
753                                     NULL);
754 }
755 
756 /*
757  * @unimplemented
758  */
759 NTSTATUS
760 NTAPI
761 NtReplyWaitReplyPort(IN HANDLE PortHandle,
762                      IN PPORT_MESSAGE ReplyMessage)
763 {
764     UNIMPLEMENTED;
765     return STATUS_NOT_IMPLEMENTED;
766 }
767 
768 NTSTATUS
769 NTAPI
770 LpcpCopyRequestData(
771     IN BOOLEAN Write,
772     IN HANDLE PortHandle,
773     IN PPORT_MESSAGE Message,
774     IN ULONG Index,
775     IN PVOID Buffer,
776     IN ULONG BufferLength,
777     OUT PULONG ReturnLength)
778 {
779     NTSTATUS Status;
780     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
781     PORT_MESSAGE CapturedMessage;
782     PLPCP_PORT_OBJECT Port = NULL;
783     PETHREAD ClientThread = NULL;
784     SIZE_T LocalReturnLength;
785     PLPCP_MESSAGE InfoMessage;
786     PLPCP_DATA_INFO DataInfo;
787     PVOID DataInfoBaseAddress;
788 
789     PAGED_CODE();
790 
791     /* Check if the call comes from user mode */
792     if (PreviousMode != KernelMode)
793     {
794         _SEH2_TRY
795         {
796             ProbeForRead(Message, sizeof(*Message), sizeof(PVOID));
797             CapturedMessage = *(volatile PORT_MESSAGE*)Message;
798         }
799         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
800         {
801             _SEH2_YIELD(return _SEH2_GetExceptionCode());
802         }
803         _SEH2_END;
804     }
805     else
806     {
807         CapturedMessage = *Message;
808     }
809 
810     /* Make sure there is any data to copy */
811     if (CapturedMessage.u2.s2.DataInfoOffset == 0)
812     {
813         return STATUS_INVALID_PARAMETER;
814     }
815 
816     /* Reference the port handle */
817     Status = ObReferenceObjectByHandle(PortHandle,
818                                        PORT_ALL_ACCESS,
819                                        LpcPortObjectType,
820                                        PreviousMode,
821                                        (PVOID*)&Port,
822                                        NULL);
823     if (!NT_SUCCESS(Status))
824     {
825         DPRINT1("Failed to reference port handle: 0x%ls\n", Status);
826         return Status;
827     }
828 
829     /* Look up the client thread */
830     Status = PsLookupProcessThreadByCid(&CapturedMessage.ClientId,
831                                         NULL,
832                                         &ClientThread);
833     if (!NT_SUCCESS(Status))
834     {
835         DPRINT1("Failed to lookup client thread for [0x%lx:0x%lx]: 0x%ls\n",
836                 CapturedMessage.ClientId.UniqueProcess,
837                 CapturedMessage.ClientId.UniqueThread, Status);
838         goto Cleanup;
839     }
840 
841     /* Acquire the global LPC lock */
842     KeAcquireGuardedMutex(&LpcpLock);
843 
844     /* Check for message id mismatch */
845     if ((ClientThread->LpcReplyMessageId != CapturedMessage.MessageId) ||
846         (CapturedMessage.MessageId == 0))
847     {
848         DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n",
849                 ClientThread->LpcReplyMessageId, CapturedMessage.MessageId);
850         Status = STATUS_REPLY_MESSAGE_MISMATCH;
851         goto CleanupWithLock;
852     }
853 
854     /* Validate the port */
855     if (!LpcpValidateClientPort(ClientThread, Port))
856     {
857         DPRINT1("LpcpValidateClientPort failed\n");
858         Status = STATUS_REPLY_MESSAGE_MISMATCH;
859         goto CleanupWithLock;
860     }
861 
862     /* Find the message with the data */
863     InfoMessage = LpcpFindDataInfoMessage(Port,
864                                           CapturedMessage.MessageId,
865                                           CapturedMessage.ClientId);
866     if (InfoMessage == NULL)
867     {
868         DPRINT1("LpcpFindDataInfoMessage failed\n");
869         Status = STATUS_INVALID_PARAMETER;
870         goto CleanupWithLock;
871     }
872 
873     /* Get the data info */
874     DataInfo = LpcpGetDataInfoFromMessage(&InfoMessage->Request);
875 
876     /* Check if the index is within bounds */
877     if (Index >= DataInfo->NumberOfEntries)
878     {
879         DPRINT1("Message data index %lu out of bounds (%lu in msg)\n",
880                 Index, DataInfo->NumberOfEntries);
881         Status = STATUS_INVALID_PARAMETER;
882         goto CleanupWithLock;
883     }
884 
885     /* Check if the caller wants to read/write more data than expected */
886     if (BufferLength > DataInfo->Entries[Index].DataLength)
887     {
888         DPRINT1("Trying to read more data (%lu) than available (%lu)\n",
889                 BufferLength, DataInfo->Entries[Index].DataLength);
890         Status = STATUS_INVALID_PARAMETER;
891         goto CleanupWithLock;
892     }
893 
894     /* Get the data pointer */
895     DataInfoBaseAddress = DataInfo->Entries[Index].BaseAddress;
896 
897     /* Release the lock */
898     KeReleaseGuardedMutex(&LpcpLock);
899 
900     if (Write)
901     {
902         /* Copy data from the caller to the message sender */
903         Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
904                                      Buffer,
905                                      ClientThread->ThreadsProcess,
906                                      DataInfoBaseAddress,
907                                      BufferLength,
908                                      PreviousMode,
909                                      &LocalReturnLength);
910     }
911     else
912     {
913         /* Copy data from the message sender to the caller */
914         Status = MmCopyVirtualMemory(ClientThread->ThreadsProcess,
915                                      DataInfoBaseAddress,
916                                      PsGetCurrentProcess(),
917                                      Buffer,
918                                      BufferLength,
919                                      PreviousMode,
920                                      &LocalReturnLength);
921     }
922 
923     if (!NT_SUCCESS(Status))
924     {
925         DPRINT1("MmCopyVirtualMemory failed: 0x%ls\n", Status);
926         goto Cleanup;
927     }
928 
929     /* Check if the caller asked to return the copied length */
930     if (ReturnLength != NULL)
931     {
932         _SEH2_TRY
933         {
934             *ReturnLength = LocalReturnLength;
935         }
936         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
937         {
938             /* Ignore */
939             DPRINT1("Exception writing ReturnLength, ignoring\n");
940         }
941         _SEH2_END;
942     }
943 
944 Cleanup:
945 
946     if (ClientThread != NULL)
947         ObDereferenceObject(ClientThread);
948 
949     ObDereferenceObject(Port);
950 
951     return Status;
952 
953 CleanupWithLock:
954 
955     /* Release the lock */
956     KeReleaseGuardedMutex(&LpcpLock);
957     goto Cleanup;
958 }
959 
960 /*
961  * @implemented
962  */
963 NTSTATUS
964 NTAPI
965 NtReadRequestData(IN HANDLE PortHandle,
966                   IN PPORT_MESSAGE Message,
967                   IN ULONG Index,
968                   IN PVOID Buffer,
969                   IN ULONG BufferLength,
970                   OUT PULONG ReturnLength)
971 {
972     /* Call the internal function */
973     return LpcpCopyRequestData(FALSE,
974                                PortHandle,
975                                Message,
976                                Index,
977                                Buffer,
978                                BufferLength,
979                                ReturnLength);
980 }
981 
982 /*
983  * @implemented
984  */
985 NTSTATUS
986 NTAPI
987 NtWriteRequestData(IN HANDLE PortHandle,
988                    IN PPORT_MESSAGE Message,
989                    IN ULONG Index,
990                    IN PVOID Buffer,
991                    IN ULONG BufferLength,
992                    OUT PULONG ReturnLength)
993 {
994     /* Call the internal function */
995     return LpcpCopyRequestData(TRUE,
996                                PortHandle,
997                                Message,
998                                Index,
999                                Buffer,
1000                                BufferLength,
1001                                ReturnLength);
1002 }
1003 
1004 /* EOF */
1005