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
LpcpFreeDataInfoMessage(IN PLPCP_PORT_OBJECT Port,IN ULONG MessageId,IN ULONG CallbackId,IN CLIENT_ID ClientId)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
LpcpSaveDataInfoMessage(IN PLPCP_PORT_OBJECT Port,IN PLPCP_MESSAGE Message,IN ULONG LockFlags)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
LpcpFindDataInfoMessage(IN PLPCP_PORT_OBJECT Port,IN ULONG MessageId,IN LPC_CLIENT_ID ClientId)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
LpcpMoveMessage(IN PPORT_MESSAGE Destination,IN PPORT_MESSAGE Origin,IN PVOID Data,IN ULONG MessageType,IN PCLIENT_ID ClientId)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
NtReplyPort(IN HANDLE PortHandle,IN PPORT_MESSAGE ReplyMessage)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
NtReplyWaitReceivePortEx(IN HANDLE PortHandle,OUT PVOID * PortContext OPTIONAL,IN PPORT_MESSAGE ReplyMessage OPTIONAL,OUT PPORT_MESSAGE ReceiveMessage,IN PLARGE_INTEGER Timeout OPTIONAL)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
NtReplyWaitReceivePort(IN HANDLE PortHandle,OUT PVOID * PortContext OPTIONAL,IN PPORT_MESSAGE ReplyMessage OPTIONAL,OUT PPORT_MESSAGE ReceiveMessage)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
NtReplyWaitReplyPort(IN HANDLE PortHandle,IN PPORT_MESSAGE ReplyMessage)761 NtReplyWaitReplyPort(IN HANDLE PortHandle,
762 IN PPORT_MESSAGE ReplyMessage)
763 {
764 UNIMPLEMENTED;
765 return STATUS_NOT_IMPLEMENTED;
766 }
767
768 NTSTATUS
769 NTAPI
LpcpCopyRequestData(IN BOOLEAN Write,IN HANDLE PortHandle,IN PPORT_MESSAGE Message,IN ULONG Index,IN PVOID Buffer,IN ULONG BufferLength,OUT PULONG ReturnLength)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
NtReadRequestData(IN HANDLE PortHandle,IN PPORT_MESSAGE Message,IN ULONG Index,IN PVOID Buffer,IN ULONG BufferLength,OUT PULONG ReturnLength)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
NtWriteRequestData(IN HANDLE PortHandle,IN PPORT_MESSAGE Message,IN ULONG Index,IN PVOID Buffer,IN ULONG BufferLength,OUT PULONG ReturnLength)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