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