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