xref: /reactos/ntoskrnl/lpc/close.c (revision 1734f297)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/lpc/close.c
5  * PURPOSE:         Local Procedure Call: Rundown, Cleanup, Deletion
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 LpcExitThread(IN PETHREAD Thread)
20 {
21     PLPCP_MESSAGE Message;
22     ASSERT(Thread == PsGetCurrentThread());
23 
24     /* Acquire the lock */
25     KeAcquireGuardedMutex(&LpcpLock);
26 
27     /* Make sure that the Reply Chain is empty */
28     if (!IsListEmpty(&Thread->LpcReplyChain))
29     {
30         /* It's not, remove the entry */
31         RemoveEntryList(&Thread->LpcReplyChain);
32     }
33 
34     /* Set the thread in exit mode */
35     Thread->LpcExitThreadCalled = TRUE;
36     Thread->LpcReplyMessageId = 0;
37 
38     /* Check if there's a reply message */
39     Message = LpcpGetMessageFromThread(Thread);
40     if (Message)
41     {
42         /* FIXME: TODO */
43         ASSERT(FALSE);
44     }
45 
46     /* Release the lock */
47     KeReleaseGuardedMutex(&LpcpLock);
48 }
49 
50 VOID
51 NTAPI
52 LpcpFreeToPortZone(IN PLPCP_MESSAGE Message,
53                    IN ULONG LockFlags)
54 {
55     PLPCP_CONNECTION_MESSAGE ConnectMessage;
56     PLPCP_PORT_OBJECT ClientPort = NULL;
57     PETHREAD Thread = NULL;
58     BOOLEAN LockHeld = (LockFlags & LPCP_LOCK_HELD);
59     BOOLEAN ReleaseLock = (LockFlags & LPCP_LOCK_RELEASE);
60 
61     PAGED_CODE();
62 
63     LPCTRACE(LPC_CLOSE_DEBUG, "Message: %p. LockFlags: %lx\n", Message, LockFlags);
64 
65     /* Acquire the lock if not already */
66     if (!LockHeld) KeAcquireGuardedMutex(&LpcpLock);
67 
68     /* Check if the queue list is empty */
69     if (!IsListEmpty(&Message->Entry))
70     {
71         /* Remove and re-initialize */
72         RemoveEntryList(&Message->Entry);
73         InitializeListHead(&Message->Entry);
74     }
75 
76     /* Check if we've already replied */
77     if (Message->RepliedToThread)
78     {
79         /* Set thread to dereference and clean up */
80         Thread = Message->RepliedToThread;
81         Message->RepliedToThread = NULL;
82     }
83 
84     /* Check if this is a connection request */
85     if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST)
86     {
87         /* Get the connection message */
88         ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
89 
90         /* Clear the client port */
91         ClientPort = ConnectMessage->ClientPort;
92         if (ClientPort) ConnectMessage->ClientPort = NULL;
93     }
94 
95     /* Release the lock */
96     KeReleaseGuardedMutex(&LpcpLock);
97 
98     /* Check if we had anything to dereference */
99     if (Thread) ObDereferenceObject(Thread);
100     if (ClientPort) ObDereferenceObject(ClientPort);
101 
102     /* Free the entry */
103     ExFreeToPagedLookasideList(&LpcpMessagesLookaside, Message);
104 
105     /* Reacquire the lock if needed */
106     if ((LockHeld) && !(ReleaseLock)) KeAcquireGuardedMutex(&LpcpLock);
107 }
108 
109 VOID
110 NTAPI
111 LpcpDestroyPortQueue(IN PLPCP_PORT_OBJECT Port,
112                      IN BOOLEAN Destroy)
113 {
114     PLIST_ENTRY ListHead, NextEntry;
115     PETHREAD Thread;
116     PLPCP_MESSAGE Message;
117     PLPCP_PORT_OBJECT ConnectionPort = NULL;
118     PLPCP_CONNECTION_MESSAGE ConnectMessage;
119     PLPCP_NONPAGED_PORT_QUEUE MessageQueue;
120 
121     PAGED_CODE();
122     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
123 
124     /* Hold the lock */
125     KeAcquireGuardedMutex(&LpcpLock);
126 
127     /* Check if we have a connected port */
128     if (((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_UNCONNECTED_PORT) &&
129         (Port->ConnectedPort))
130     {
131         /* Disconnect it */
132         Port->ConnectedPort->ConnectedPort = NULL;
133         ConnectionPort = Port->ConnectedPort->ConnectionPort;
134         if (ConnectionPort)
135         {
136             /* Clear connection port */
137             Port->ConnectedPort->ConnectionPort = NULL;
138         }
139     }
140 
141     /* Check if this is a connection port */
142     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT)
143     {
144         /* Delete the name */
145         Port->Flags |= LPCP_NAME_DELETED;
146     }
147 
148     /* Walk all the threads waiting and signal them */
149     ListHead = &Port->LpcReplyChainHead;
150     NextEntry = ListHead->Flink;
151     while ((NextEntry) && (NextEntry != ListHead))
152     {
153         /* Get the Thread */
154         Thread = CONTAINING_RECORD(NextEntry, ETHREAD, LpcReplyChain);
155 
156         /* Make sure we're not in exit */
157         if (Thread->LpcExitThreadCalled) break;
158 
159         /* Move to the next entry */
160         NextEntry = NextEntry->Flink;
161 
162         /* Remove and reinitialize the List */
163         RemoveEntryList(&Thread->LpcReplyChain);
164         InitializeListHead(&Thread->LpcReplyChain);
165 
166         /* Check if someone is waiting */
167         if (!KeReadStateSemaphore(&Thread->LpcReplySemaphore))
168         {
169             /* Get the message */
170             Message = LpcpGetMessageFromThread(Thread);
171             if (Message)
172             {
173                 /* Check if it's a connection request */
174                 if (Message->Request.u2.s2.Type == LPC_CONNECTION_REQUEST)
175                 {
176                     /* Get the connection message */
177                     ConnectMessage = (PLPCP_CONNECTION_MESSAGE)(Message + 1);
178 
179                     /* Check if it had a section */
180                     if (ConnectMessage->SectionToMap)
181                     {
182                         /* Dereference it */
183                         ObDereferenceObject(ConnectMessage->SectionToMap);
184                     }
185                 }
186 
187                 /* Clear the reply message */
188                 Thread->LpcReplyMessage = NULL;
189 
190                 /* And remove the message from the port zone */
191                 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD);
192                 NextEntry = Port->LpcReplyChainHead.Flink;
193             }
194 
195             /* Release the semaphore and reset message id count */
196             Thread->LpcReplyMessageId = 0;
197             KeReleaseSemaphore(&Thread->LpcReplySemaphore, 0, 1, FALSE);
198         }
199     }
200 
201     /* Reinitialize the list head */
202     InitializeListHead(&Port->LpcReplyChainHead);
203 
204     /* Loop queued messages */
205     while ((Port->MsgQueue.ReceiveHead.Flink) &&
206            !(IsListEmpty(&Port->MsgQueue.ReceiveHead)))
207     {
208         /* Get the message */
209         Message = CONTAINING_RECORD(Port->MsgQueue.ReceiveHead.Flink,
210                                     LPCP_MESSAGE,
211                                     Entry);
212 
213         /* Free and reinitialize it's list head */
214         RemoveEntryList(&Message->Entry);
215         InitializeListHead(&Message->Entry);
216 
217         /* Remove it from the port zone */
218         LpcpFreeToPortZone(Message, LPCP_LOCK_HELD);
219     }
220 
221     /* Release the lock */
222     KeReleaseGuardedMutex(&LpcpLock);
223 
224     /* Dereference the connection port */
225     if (ConnectionPort) ObDereferenceObject(ConnectionPort);
226 
227     /* Check if we have to free the port entirely */
228     if (Destroy)
229     {
230         /* Check if the semaphore exists */
231         if (Port->MsgQueue.Semaphore)
232         {
233             /* Use the semaphore to find the port queue and free it */
234             MessageQueue = CONTAINING_RECORD(Port->MsgQueue.Semaphore,
235                                              LPCP_NONPAGED_PORT_QUEUE,
236                                              Semaphore);
237             ExFreePoolWithTag(MessageQueue, 'troP');
238         }
239     }
240 }
241 
242 VOID
243 NTAPI
244 LpcpClosePort(IN PEPROCESS Process OPTIONAL,
245               IN PVOID Object,
246               IN ACCESS_MASK GrantedAccess,
247               IN ULONG ProcessHandleCount,
248               IN ULONG SystemHandleCount)
249 {
250     PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)Object;
251 
252     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
253 
254     /* Only Server-side Connection Ports need clean up*/
255     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT)
256     {
257         /* Check the handle count */
258         switch (SystemHandleCount)
259         {
260             /* No handles left */
261             case 0:
262 
263                 /* Destroy the port queue */
264                 LpcpDestroyPortQueue(Port, TRUE);
265                 break;
266 
267             /* Last handle remaining */
268             case 1:
269 
270                 /* Reset the queue only */
271                 LpcpDestroyPortQueue(Port, FALSE);
272 
273             /* More handles remain, do nothing */
274             default:
275                 break;
276         }
277     }
278 }
279 
280 VOID
281 NTAPI
282 LpcpFreePortClientSecurity(IN PLPCP_PORT_OBJECT Port)
283 {
284     /* Check if this is a client port */
285     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
286     {
287         /* Check if security is static */
288         if (!(Port->Flags & LPCP_SECURITY_DYNAMIC))
289         {
290             /* Check if we have a token */
291             if (Port->StaticSecurity.ClientToken)
292             {
293                 /* Free security */
294                 SeDeleteClientSecurity(&Port->StaticSecurity);
295             }
296         }
297     }
298 }
299 
300 VOID
301 NTAPI
302 LpcpDeletePort(IN PVOID ObjectBody)
303 {
304     LARGE_INTEGER Timeout;
305     PETHREAD Thread;
306     PLPCP_PORT_OBJECT Port = (PLPCP_PORT_OBJECT)ObjectBody;
307     PLPCP_PORT_OBJECT ConnectionPort;
308     PLPCP_MESSAGE Message;
309     PLIST_ENTRY ListHead, NextEntry;
310     HANDLE Pid;
311     CLIENT_DIED_MSG ClientDiedMsg;
312 
313     PAGED_CODE();
314     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p. Flags: %lx\n", Port, Port->Flags);
315 
316     Timeout.QuadPart = -1000000;
317 
318     /* Check if this is a communication port */
319     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT)
320     {
321         /* Acquire the lock */
322         KeAcquireGuardedMutex(&LpcpLock);
323 
324         /* Get the thread */
325         Thread = Port->ClientThread;
326         if (Thread)
327         {
328             /* Clear it */
329             Port->ClientThread = NULL;
330 
331             /* Release the lock and dereference */
332             KeReleaseGuardedMutex(&LpcpLock);
333             ObDereferenceObject(Thread);
334         }
335         else
336         {
337             /* Release the lock */
338             KeReleaseGuardedMutex(&LpcpLock);
339         }
340     }
341 
342     /* Check if this is a client-side port */
343     if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT)
344     {
345         /* Setup the client died message */
346         ClientDiedMsg.h.u1.s1.TotalLength = sizeof(ClientDiedMsg);
347         ClientDiedMsg.h.u1.s1.DataLength = sizeof(ClientDiedMsg.CreateTime);
348         ClientDiedMsg.h.u2.ZeroInit = 0;
349         ClientDiedMsg.h.u2.s2.Type = LPC_PORT_CLOSED;
350         ClientDiedMsg.CreateTime = PsGetCurrentProcess()->CreateTime;
351 
352         /* Send it */
353         for (;;)
354         {
355             /* Send the message */
356             if (LpcRequestPort(Port, &ClientDiedMsg.h) != STATUS_NO_MEMORY)
357                 break;
358 
359             /* Wait until trying again */
360             KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
361         }
362     }
363 
364     /* Destroy the port queue */
365     LpcpDestroyPortQueue(Port, TRUE);
366 
367     /* Check if we had views */
368     if ((Port->ClientSectionBase) || (Port->ServerSectionBase))
369     {
370         /* Check if we had a client view */
371         if (Port->ClientSectionBase)
372         {
373             /* Unmap it */
374             MmUnmapViewOfSection(Port->MappingProcess,
375                                  Port->ClientSectionBase);
376         }
377 
378         /* Check for a server view */
379         if (Port->ServerSectionBase)
380         {
381             /* Unmap it */
382             MmUnmapViewOfSection(Port->MappingProcess,
383                                  Port->ServerSectionBase);
384         }
385 
386         /* Dereference the mapping process */
387         ObDereferenceObject(Port->MappingProcess);
388         Port->MappingProcess = NULL;
389     }
390 
391     /* Acquire the lock */
392     KeAcquireGuardedMutex(&LpcpLock);
393 
394     /* Get the connection port */
395     ConnectionPort = Port->ConnectionPort;
396     if (ConnectionPort)
397     {
398         /* Get the PID */
399         Pid = PsGetCurrentProcessId();
400 
401         /* Loop the data lists */
402         ListHead = &ConnectionPort->LpcDataInfoChainHead;
403         NextEntry = ListHead->Flink;
404         while (NextEntry != ListHead)
405         {
406             /* Get the message */
407             Message = CONTAINING_RECORD(NextEntry, LPCP_MESSAGE, Entry);
408             NextEntry = NextEntry->Flink;
409 
410             /* Check if this is the connection port */
411             if (Port == ConnectionPort)
412             {
413                 /* Free queued messages */
414                 RemoveEntryList(&Message->Entry);
415                 InitializeListHead(&Message->Entry);
416                 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD);
417 
418                 /* Restart at the head */
419                 NextEntry = ListHead->Flink;
420             }
421             else if ((Message->Request.ClientId.UniqueProcess == Pid) &&
422                      ((Message->SenderPort == Port) ||
423                       (Message->SenderPort == Port->ConnectedPort) ||
424                       (Message->SenderPort == ConnectionPort)))
425             {
426                 /* Remove it */
427                 RemoveEntryList(&Message->Entry);
428                 InitializeListHead(&Message->Entry);
429                 LpcpFreeToPortZone(Message, LPCP_LOCK_HELD);
430 
431                 /* Restart at the head */
432                 NextEntry = ListHead->Flink;
433             }
434         }
435 
436         /* Release the lock */
437         KeReleaseGuardedMutex(&LpcpLock);
438 
439         /* Dereference the object unless it's the same port */
440         if (ConnectionPort != Port) ObDereferenceObject(ConnectionPort);
441 
442         /* Check if this is a connection port with a server process */
443         if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CONNECTION_PORT) &&
444             (ConnectionPort->ServerProcess))
445         {
446             /* Dereference the server process */
447             ObDereferenceObject(ConnectionPort->ServerProcess);
448             ConnectionPort->ServerProcess = NULL;
449         }
450     }
451     else
452     {
453         /* Release the lock */
454         KeReleaseGuardedMutex(&LpcpLock);
455     }
456 
457     /* Free client security */
458     LpcpFreePortClientSecurity(Port);
459     LPCTRACE(LPC_CLOSE_DEBUG, "Port: %p deleted\n", Port);
460 }
461 
462 /* EOF */
463