1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/complete.c
5 * PURPOSE: Local Procedure Call: Connection Completion
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
LpcpPrepareToWakeClient(IN PETHREAD Thread)19 LpcpPrepareToWakeClient(IN PETHREAD Thread)
20 {
21 PAGED_CODE();
22
23 /* Make sure the thread isn't dying and it has a valid chain */
24 if (!(Thread->LpcExitThreadCalled) &&
25 !(IsListEmpty(&Thread->LpcReplyChain)))
26 {
27 /* Remove it from the list and reinitialize it */
28 RemoveEntryList(&Thread->LpcReplyChain);
29 InitializeListHead(&Thread->LpcReplyChain);
30 }
31 }
32
33 /* PUBLIC FUNCTIONS **********************************************************/
34
35 /*
36 * @implemented
37 */
38 NTSTATUS
39 NTAPI
NtAcceptConnectPort(OUT PHANDLE PortHandle,IN PVOID PortContext OPTIONAL,IN PPORT_MESSAGE ReplyMessage,IN BOOLEAN AcceptConnection,IN OUT PPORT_VIEW ServerView OPTIONAL,OUT PREMOTE_PORT_VIEW ClientView OPTIONAL)40 NtAcceptConnectPort(OUT PHANDLE PortHandle,
41 IN PVOID PortContext OPTIONAL,
42 IN PPORT_MESSAGE ReplyMessage,
43 IN BOOLEAN AcceptConnection,
44 IN OUT PPORT_VIEW ServerView OPTIONAL,
45 OUT PREMOTE_PORT_VIEW ClientView OPTIONAL)
46 {
47 NTSTATUS Status;
48 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
49 PORT_VIEW CapturedServerView;
50 PORT_MESSAGE CapturedReplyMessage;
51 ULONG ConnectionInfoLength;
52 PLPCP_PORT_OBJECT ConnectionPort, ServerPort, ClientPort;
53 PLPCP_CONNECTION_MESSAGE ConnectMessage;
54 PLPCP_MESSAGE Message;
55 PVOID ClientSectionToMap = NULL;
56 HANDLE Handle;
57 PEPROCESS ClientProcess;
58 PETHREAD ClientThread;
59 LARGE_INTEGER SectionOffset;
60
61 PAGED_CODE();
62
63 LPCTRACE(LPC_COMPLETE_DEBUG,
64 "Context: %p. Message: %p. Accept: %lx. Views: %p/%p\n",
65 PortContext,
66 ReplyMessage,
67 AcceptConnection,
68 ClientView,
69 ServerView);
70
71 /* Check if the call comes from user mode */
72 if (PreviousMode != KernelMode)
73 {
74 _SEH2_TRY
75 {
76 /* Probe the PortHandle */
77 ProbeForWriteHandle(PortHandle);
78
79 /* Probe the basic ReplyMessage structure */
80 ProbeForRead(ReplyMessage, sizeof(*ReplyMessage), sizeof(ULONG));
81 CapturedReplyMessage = *(volatile PORT_MESSAGE*)ReplyMessage;
82 ConnectionInfoLength = CapturedReplyMessage.u1.s1.DataLength;
83
84 /* Probe the connection info */
85 ProbeForRead(ReplyMessage + 1, ConnectionInfoLength, 1);
86
87 /* The following parameters are optional */
88
89 /* Capture the server view */
90 if (ServerView)
91 {
92 ProbeForWrite(ServerView, sizeof(*ServerView), sizeof(ULONG));
93 CapturedServerView = *(volatile PORT_VIEW*)ServerView;
94
95 /* Validate the size of the server view */
96 if (CapturedServerView.Length != sizeof(CapturedServerView))
97 {
98 /* Invalid size */
99 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
100 }
101 }
102
103 /* Capture the client view */
104 if (ClientView)
105 {
106 ProbeForWrite(ClientView, sizeof(*ClientView), sizeof(ULONG));
107
108 /* Validate the size of the client view */
109 if (((volatile REMOTE_PORT_VIEW*)ClientView)->Length != sizeof(*ClientView))
110 {
111 /* Invalid size */
112 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
113 }
114 }
115 }
116 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
117 {
118 /* There was an exception, return the exception code */
119 _SEH2_YIELD(return _SEH2_GetExceptionCode());
120 }
121 _SEH2_END;
122 }
123 else
124 {
125 CapturedReplyMessage = *ReplyMessage;
126 ConnectionInfoLength = CapturedReplyMessage.u1.s1.DataLength;
127
128 /* Capture the server view */
129 if (ServerView)
130 {
131 /* Validate the size of the server view */
132 if (ServerView->Length != sizeof(*ServerView))
133 {
134 /* Invalid size */
135 return STATUS_INVALID_PARAMETER;
136 }
137 CapturedServerView = *ServerView;
138 }
139
140 /* Capture the client view */
141 if (ClientView)
142 {
143 /* Validate the size of the client view */
144 if (ClientView->Length != sizeof(*ClientView))
145 {
146 /* Invalid size */
147 return STATUS_INVALID_PARAMETER;
148 }
149 }
150 }
151
152 /* Get the client process and thread */
153 Status = PsLookupProcessThreadByCid(&CapturedReplyMessage.ClientId,
154 &ClientProcess,
155 &ClientThread);
156 if (!NT_SUCCESS(Status)) return Status;
157
158 /* Acquire the LPC Lock */
159 KeAcquireGuardedMutex(&LpcpLock);
160
161 /* Make sure that the client wants a reply, and this is the right one */
162 if (!(LpcpGetMessageFromThread(ClientThread)) ||
163 !(CapturedReplyMessage.MessageId) ||
164 (ClientThread->LpcReplyMessageId != CapturedReplyMessage.MessageId))
165 {
166 /* Not the reply asked for, or no reply wanted, fail */
167 KeReleaseGuardedMutex(&LpcpLock);
168 ObDereferenceObject(ClientProcess);
169 ObDereferenceObject(ClientThread);
170 return STATUS_REPLY_MESSAGE_MISMATCH;
171 }
172
173 /* Now get the message and connection message */
174 Message = LpcpGetMessageFromThread(ClientThread);
175 ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
176
177 /* Get the client and connection port as well */
178 ClientPort = ConnectMessage->ClientPort;
179 ConnectionPort = ClientPort->ConnectionPort;
180
181 /* Make sure that the reply is being sent to the proper server process */
182 if (ConnectionPort->ServerProcess != PsGetCurrentProcess())
183 {
184 /* It's not, so fail */
185 KeReleaseGuardedMutex(&LpcpLock);
186 ObDereferenceObject(ClientProcess);
187 ObDereferenceObject(ClientThread);
188 return STATUS_REPLY_MESSAGE_MISMATCH;
189 }
190
191 /* At this point, don't let other accept attempts happen */
192 ClientThread->LpcReplyMessage = NULL;
193 ClientThread->LpcReplyMessageId = 0;
194
195 /* Clear the client port for now as well, then release the lock */
196 ConnectMessage->ClientPort = NULL;
197 KeReleaseGuardedMutex(&LpcpLock);
198
199 /* Check the connection information length */
200 if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength)
201 {
202 /* Normalize it since it's too large */
203 ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
204 }
205
206 /* Set the sizes of our reply message */
207 Message->Request.u1.s1.DataLength = (CSHORT)ConnectionInfoLength +
208 sizeof(LPCP_CONNECTION_MESSAGE);
209 Message->Request.u1.s1.TotalLength = sizeof(LPCP_MESSAGE) +
210 Message->Request.u1.s1.DataLength;
211
212 /* Setup the reply message */
213 Message->Request.u2.s2.Type = LPC_REPLY;
214 Message->Request.u2.s2.DataInfoOffset = 0;
215 Message->Request.ClientId = CapturedReplyMessage.ClientId;
216 Message->Request.MessageId = CapturedReplyMessage.MessageId;
217 Message->Request.ClientViewSize = 0;
218
219 _SEH2_TRY
220 {
221 RtlCopyMemory(ConnectMessage + 1, ReplyMessage + 1, ConnectionInfoLength);
222 }
223 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
224 {
225 Status = _SEH2_GetExceptionCode();
226 _SEH2_YIELD(goto Cleanup);
227 }
228 _SEH2_END;
229
230 /* At this point, if the caller refused the connection, go to cleanup */
231 if (!AcceptConnection)
232 {
233 DPRINT1("LPC connection was refused\n");
234 goto Cleanup;
235 }
236
237 /* Otherwise, create the actual port */
238 Status = ObCreateObject(PreviousMode,
239 LpcPortObjectType,
240 NULL,
241 PreviousMode,
242 NULL,
243 sizeof(LPCP_PORT_OBJECT),
244 0,
245 0,
246 (PVOID*)&ServerPort);
247 if (!NT_SUCCESS(Status)) goto Cleanup;
248
249 /* Set it up */
250 RtlZeroMemory(ServerPort, sizeof(LPCP_PORT_OBJECT));
251 ServerPort->PortContext = PortContext;
252 ServerPort->Flags = LPCP_COMMUNICATION_PORT;
253 ServerPort->MaxMessageLength = ConnectionPort->MaxMessageLength;
254 InitializeListHead(&ServerPort->LpcReplyChainHead);
255 InitializeListHead(&ServerPort->LpcDataInfoChainHead);
256
257 /* Reference the connection port until we're fully setup */
258 ObReferenceObject(ConnectionPort);
259
260 /* Link the ports together */
261 ServerPort->ConnectionPort = ConnectionPort;
262 ServerPort->ConnectedPort = ClientPort;
263 ClientPort->ConnectedPort = ServerPort;
264
265 /* Also set the creator CID */
266 ServerPort->Creator = PsGetCurrentThread()->Cid;
267 ClientPort->Creator = Message->Request.ClientId;
268
269 /* Get the section associated and then clear it, while inside the lock */
270 KeAcquireGuardedMutex(&LpcpLock);
271 ClientSectionToMap = ConnectMessage->SectionToMap;
272 ConnectMessage->SectionToMap = NULL;
273 KeReleaseGuardedMutex(&LpcpLock);
274
275 /* Now check if there's a client section */
276 if (ClientSectionToMap)
277 {
278 /* Setup the offset */
279 SectionOffset.QuadPart = ConnectMessage->ClientView.SectionOffset;
280
281 /* Map the section */
282 Status = MmMapViewOfSection(ClientSectionToMap,
283 PsGetCurrentProcess(),
284 &ServerPort->ClientSectionBase,
285 0,
286 0,
287 &SectionOffset,
288 &ConnectMessage->ClientView.ViewSize,
289 ViewUnmap,
290 0,
291 PAGE_READWRITE);
292
293 /* Update the offset and check for mapping status */
294 ConnectMessage->ClientView.SectionOffset = SectionOffset.LowPart;
295 if (NT_SUCCESS(Status))
296 {
297 /* Set the view base */
298 ConnectMessage->ClientView.ViewRemoteBase = ServerPort->
299 ClientSectionBase;
300
301 /* Save and reference the mapping process */
302 ServerPort->MappingProcess = PsGetCurrentProcess();
303 ObReferenceObject(ServerPort->MappingProcess);
304 }
305 else
306 {
307 /* Otherwise, quit */
308 ObDereferenceObject(ServerPort);
309 DPRINT1("Client section mapping failed: %lx\n", Status);
310 LPCTRACE(LPC_COMPLETE_DEBUG,
311 "View base, offset, size: %p %lx %p\n",
312 ServerPort->ClientSectionBase,
313 ConnectMessage->ClientView.ViewSize,
314 SectionOffset);
315 goto Cleanup;
316 }
317 }
318
319 /* Check if there's a server section */
320 if (ServerView)
321 {
322 /* FIXME: TODO */
323 UNREFERENCED_PARAMETER(CapturedServerView);
324 ASSERT(FALSE);
325 }
326
327 /* Reference the server port until it's fully inserted */
328 ObReferenceObject(ServerPort);
329
330 /* Insert the server port in the namespace */
331 Status = ObInsertObject(ServerPort,
332 NULL,
333 PORT_ALL_ACCESS,
334 0,
335 NULL,
336 &Handle);
337 if (!NT_SUCCESS(Status))
338 {
339 /* We failed, remove the extra reference and cleanup */
340 ObDereferenceObject(ServerPort);
341 goto Cleanup;
342 }
343
344 /* Enter SEH to write back the results */
345 _SEH2_TRY
346 {
347 /* Check if the caller gave a client view */
348 if (ClientView)
349 {
350 /* Fill it out */
351 ClientView->ViewBase = ConnectMessage->ClientView.ViewRemoteBase;
352 ClientView->ViewSize = ConnectMessage->ClientView.ViewSize;
353 }
354
355 /* Return the handle to user mode */
356 *PortHandle = Handle;
357 }
358 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
359 {
360 /* Cleanup and return the exception code */
361 ObCloseHandle(Handle, PreviousMode);
362 ObDereferenceObject(ServerPort);
363 Status = _SEH2_GetExceptionCode();
364 _SEH2_YIELD(goto Cleanup);
365 }
366 _SEH2_END;
367
368 LPCTRACE(LPC_COMPLETE_DEBUG,
369 "Handle: %p. Messages: %p/%p. Ports: %p/%p/%p\n",
370 Handle,
371 Message,
372 ConnectMessage,
373 ServerPort,
374 ClientPort,
375 ConnectionPort);
376
377 /* If there was no port context, use the handle by default */
378 if (!PortContext) ServerPort->PortContext = Handle;
379 ServerPort->ClientThread = ClientThread;
380
381 /* Set this message as the LPC Reply message while holding the lock */
382 KeAcquireGuardedMutex(&LpcpLock);
383 ClientThread->LpcReplyMessage = Message;
384 KeReleaseGuardedMutex(&LpcpLock);
385
386 /* Clear the thread pointer so it doesn't get cleaned later */
387 ClientThread = NULL;
388
389 /* Remove the extra reference we had added */
390 ObDereferenceObject(ServerPort);
391
392 Cleanup:
393 /* If there was a section, dereference it */
394 if (ClientSectionToMap) ObDereferenceObject(ClientSectionToMap);
395
396 /* Check if we got here while still having a client thread */
397 if (ClientThread)
398 {
399 KeAcquireGuardedMutex(&LpcpLock);
400 ClientThread->LpcReplyMessage = Message;
401 LpcpPrepareToWakeClient(ClientThread);
402 KeReleaseGuardedMutex(&LpcpLock);
403 LpcpCompleteWait(&ClientThread->LpcReplySemaphore);
404 ObDereferenceObject(ClientThread);
405 }
406
407 /* Dereference the client port if we have one, and the process */
408 LPCTRACE(LPC_COMPLETE_DEBUG,
409 "Status: %lx. Thread: %p. Process: [%.16s]\n",
410 Status,
411 ClientThread,
412 ClientProcess->ImageFileName);
413 if (ClientPort) ObDereferenceObject(ClientPort);
414 ObDereferenceObject(ClientProcess);
415 return Status;
416 }
417
418 /*
419 * @implemented
420 */
421 NTSTATUS
422 NTAPI
NtCompleteConnectPort(IN HANDLE PortHandle)423 NtCompleteConnectPort(IN HANDLE PortHandle)
424 {
425 NTSTATUS Status;
426 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
427 PLPCP_PORT_OBJECT Port;
428 PETHREAD Thread;
429
430 PAGED_CODE();
431 LPCTRACE(LPC_COMPLETE_DEBUG, "Handle: %p\n", PortHandle);
432
433 /* Get the Port Object */
434 Status = ObReferenceObjectByHandle(PortHandle,
435 PORT_ALL_ACCESS,
436 LpcPortObjectType,
437 PreviousMode,
438 (PVOID*)&Port,
439 NULL);
440 if (!NT_SUCCESS(Status)) return Status;
441
442 /* Make sure this is a connection port */
443 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
444 {
445 /* It isn't, fail */
446 ObDereferenceObject(Port);
447 return STATUS_INVALID_PORT_HANDLE;
448 }
449
450 /* Acquire the lock */
451 KeAcquireGuardedMutex(&LpcpLock);
452
453 /* Make sure we have a client thread */
454 if (!Port->ClientThread)
455 {
456 /* We don't, fail */
457 KeReleaseGuardedMutex(&LpcpLock);
458 ObDereferenceObject(Port);
459 return STATUS_INVALID_PARAMETER;
460 }
461
462 /* Get the thread */
463 Thread = Port->ClientThread;
464
465 /* Make sure it has a reply message */
466 if (!LpcpGetMessageFromThread(Thread))
467 {
468 /* It doesn't, quit */
469 KeReleaseGuardedMutex(&LpcpLock);
470 ObDereferenceObject(Port);
471 return STATUS_SUCCESS;
472 }
473
474 /* Clear the client thread and wake it up */
475 Port->ClientThread = NULL;
476 LpcpPrepareToWakeClient(Thread);
477
478 /* Release the lock and wait for an answer */
479 KeReleaseGuardedMutex(&LpcpLock);
480 LpcpCompleteWait(&Thread->LpcReplySemaphore);
481
482 /* Dereference the Thread and Port and return */
483 ObDereferenceObject(Port);
484 ObDereferenceObject(Thread);
485 LPCTRACE(LPC_COMPLETE_DEBUG, "Port: %p. Thread: %p\n", Port, Thread);
486 return Status;
487 }
488
489 /* EOF */
490