xref: /reactos/ntoskrnl/ps/kill.c (revision 62919904)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ps/kill.c
5  * PURPOSE:         Process Manager: Process and Thread Termination
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Filip Navara (xnavara@reactos.org)
8  *                  Thomas Weidenmueller (w3seek@reactos.org
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* GLOBALS *******************************************************************/
18 
19 LIST_ENTRY PspReaperListHead = { NULL, NULL };
20 WORK_QUEUE_ITEM PspReaperWorkItem;
21 LARGE_INTEGER ShortTime = {{-10 * 100 * 1000, -1}};
22 
23 /* PRIVATE FUNCTIONS *********************************************************/
24 
25 VOID
26 NTAPI
27 PspCatchCriticalBreak(IN PCHAR Message,
28                       IN PVOID ProcessOrThread,
29                       IN PCHAR ImageName)
30 {
31     CHAR Action[2];
32     BOOLEAN Handled = FALSE;
33     PAGED_CODE();
34 
35     /* Check if a debugger is enabled */
36     if (KdDebuggerEnabled)
37     {
38         /* Print out the message */
39         DbgPrint(Message, ProcessOrThread, ImageName);
40         do
41         {
42             /* If a debugger isn't present, don't prompt */
43             if (KdDebuggerNotPresent) break;
44 
45             /* A debuger is active, prompt for action */
46             DbgPrompt("Break, or Ignore (bi)?", Action, sizeof(Action));
47             switch (Action[0])
48             {
49                 /* Break */
50                 case 'B': case 'b':
51 
52                     /* Do a breakpoint */
53                     DbgBreakPoint();
54 
55                 /* Ignore */
56                 case 'I': case 'i':
57 
58                     /* Handle it */
59                     Handled = TRUE;
60 
61                 /* Unrecognized */
62                 default:
63                     break;
64             }
65         } while (!Handled);
66     }
67 
68     /* Did we ultimately handle this? */
69     if (!Handled)
70     {
71         /* We didn't, bugcheck */
72         KeBugCheckEx(CRITICAL_OBJECT_TERMINATION,
73                      ((PKPROCESS)ProcessOrThread)->Header.Type,
74                      (ULONG_PTR)ProcessOrThread,
75                      (ULONG_PTR)ImageName,
76                      (ULONG_PTR)Message);
77     }
78 }
79 
80 NTSTATUS
81 NTAPI
82 PspTerminateProcess(IN PEPROCESS Process,
83                     IN NTSTATUS ExitStatus)
84 {
85     PETHREAD Thread;
86     NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE;
87     PAGED_CODE();
88     PSTRACE(PS_KILL_DEBUG,
89             "Process: %p ExitStatus: %d\n", Process, ExitStatus);
90     PSREFTRACE(Process);
91 
92     /* Check if this is a Critical Process */
93     if (Process->BreakOnTermination)
94     {
95         /* Break to debugger */
96         PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
97                               Process,
98                               Process->ImageFileName);
99     }
100 
101     /* Set the delete flag */
102     InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT);
103 
104     /* Get the first thread */
105     Thread = PsGetNextProcessThread(Process, NULL);
106     while (Thread)
107     {
108         /* Kill it */
109         PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
110         Thread = PsGetNextProcessThread(Process, Thread);
111 
112         /* We had at least one thread, so termination is OK */
113         Status = STATUS_SUCCESS;
114     }
115 
116     /* Check if there was nothing to terminate or if we have a debug port */
117     if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort))
118     {
119         /* Clear the handle table anyway */
120         ObClearProcessHandleTable(Process);
121     }
122 
123     /* Return status */
124     return Status;
125 }
126 
127 NTSTATUS
128 NTAPI
129 PsTerminateProcess(IN PEPROCESS Process,
130                    IN NTSTATUS ExitStatus)
131 {
132     /* Call the internal API */
133     return PspTerminateProcess(Process, ExitStatus);
134 }
135 
136 VOID
137 NTAPI
138 PspShutdownProcessManager(VOID)
139 {
140     PEPROCESS Process = NULL;
141 
142     /* Loop every process */
143     Process = PsGetNextProcess(Process);
144     while (Process)
145     {
146         /* Make sure this isn't the idle or initial process */
147         if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
148         {
149             /* Kill it */
150             PspTerminateProcess(Process, STATUS_SYSTEM_SHUTDOWN);
151         }
152 
153         /* Get the next process */
154         Process = PsGetNextProcess(Process);
155     }
156 }
157 
158 VOID
159 NTAPI
160 PspExitApcRundown(IN PKAPC Apc)
161 {
162     PAGED_CODE();
163 
164     /* Free the APC */
165     ExFreePool(Apc);
166 }
167 
168 VOID
169 NTAPI
170 PspReapRoutine(IN PVOID Context)
171 {
172     PSINGLE_LIST_ENTRY NextEntry;
173     PETHREAD Thread;
174     PSTRACE(PS_KILL_DEBUG, "Context: %p\n", Context);
175 
176     /* Start main loop */
177     do
178     {
179         /* Write magic value and return the next entry to process */
180         NextEntry = InterlockedExchangePointer((PVOID*)&PspReaperListHead.Flink,
181                                                (PVOID)1);
182         ASSERT((NextEntry != NULL) && (NextEntry != (PVOID)1));
183 
184         /* Start inner loop */
185         do
186         {
187             /* Get the first Thread Entry */
188             Thread = CONTAINING_RECORD(NextEntry, ETHREAD, ReaperLink);
189 
190             /* Delete this entry's kernel stack */
191             MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
192                                 Thread->Tcb.LargeStack);
193             Thread->Tcb.InitialStack = NULL;
194 
195             /* Move to the next entry */
196             NextEntry = NextEntry->Next;
197 
198             /* Dereference this thread */
199             ObDereferenceObject(Thread);
200         } while ((NextEntry != NULL) && (NextEntry != (PVOID)1));
201 
202         /* Remove magic value, keep looping if it got changed */
203     } while (InterlockedCompareExchangePointer((PVOID*)&PspReaperListHead.Flink,
204                                                NULL,
205                                                (PVOID)1) != (PVOID)1);
206 }
207 
208 #if DBG
209 VOID
210 NTAPI
211 PspCheckProcessList(VOID)
212 {
213     PLIST_ENTRY Entry;
214 
215     KeAcquireGuardedMutex(&PspActiveProcessMutex);
216     DbgPrint("# checking PsActiveProcessHead @ %p\n", &PsActiveProcessHead);
217     for (Entry = PsActiveProcessHead.Flink;
218          Entry != &PsActiveProcessHead;
219          Entry = Entry->Flink)
220     {
221         PEPROCESS Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
222         POBJECT_HEADER Header;
223         PVOID Info, HeaderLocation;
224 
225         /* Get the header and assume this is what we'll free */
226         Header = OBJECT_TO_OBJECT_HEADER(Process);
227         HeaderLocation = Header;
228 
229         /* To find the header, walk backwards from how we allocated */
230         if ((Info = OBJECT_HEADER_TO_CREATOR_INFO(Header)))
231         {
232             HeaderLocation = Info;
233         }
234         if ((Info = OBJECT_HEADER_TO_NAME_INFO(Header)))
235         {
236             HeaderLocation = Info;
237         }
238         if ((Info = OBJECT_HEADER_TO_HANDLE_INFO(Header)))
239         {
240             HeaderLocation = Info;
241         }
242         if ((Info = OBJECT_HEADER_TO_QUOTA_INFO(Header)))
243         {
244             HeaderLocation = Info;
245         }
246 
247         ExpCheckPoolAllocation(HeaderLocation, NonPagedPool, 'corP');
248     }
249 
250     KeReleaseGuardedMutex(&PspActiveProcessMutex);
251 }
252 #endif
253 
254 VOID
255 NTAPI
256 PspDeleteProcess(IN PVOID ObjectBody)
257 {
258     PEPROCESS Process = (PEPROCESS)ObjectBody;
259     KAPC_STATE ApcState;
260     PAGED_CODE();
261     PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
262     PSREFTRACE(Process);
263 
264     /* Check if it has an Active Process Link */
265     if (Process->ActiveProcessLinks.Flink)
266     {
267         /* Remove it from the Active List */
268         KeAcquireGuardedMutex(&PspActiveProcessMutex);
269         RemoveEntryList(&Process->ActiveProcessLinks);
270         Process->ActiveProcessLinks.Flink = NULL;
271         Process->ActiveProcessLinks.Blink = NULL;
272         KeReleaseGuardedMutex(&PspActiveProcessMutex);
273     }
274 
275     /* Check for Auditing information */
276     if (Process->SeAuditProcessCreationInfo.ImageFileName)
277     {
278         /* Free it */
279         ExFreePoolWithTag(Process->SeAuditProcessCreationInfo.ImageFileName,
280                           TAG_SEPA);
281         Process->SeAuditProcessCreationInfo.ImageFileName = NULL;
282     }
283 
284     /* Check if we have a job */
285     if (Process->Job)
286     {
287         /* Remove the process from the job */
288         PspRemoveProcessFromJob(Process, Process->Job);
289 
290         /* Dereference it */
291         ObDereferenceObject(Process->Job);
292         Process->Job = NULL;
293     }
294 
295     /* Increase the stack count */
296     Process->Pcb.StackCount++;
297 
298     /* Check if we have a debug port */
299     if (Process->DebugPort)
300     {
301         /* Deference the Debug Port */
302         ObDereferenceObject(Process->DebugPort);
303         Process->DebugPort = NULL;
304     }
305 
306     /* Check if we have an exception port */
307     if (Process->ExceptionPort)
308     {
309         /* Deference the Exception Port */
310         ObDereferenceObject(Process->ExceptionPort);
311         Process->ExceptionPort = NULL;
312     }
313 
314     /* Check if we have a section object */
315     if (Process->SectionObject)
316     {
317         /* Deference the Section Object */
318         ObDereferenceObject(Process->SectionObject);
319         Process->SectionObject = NULL;
320     }
321 
322 #if defined(_X86_)
323     /* Clean Ldt and Vdm objects */
324     PspDeleteLdt(Process);
325     PspDeleteVdmObjects(Process);
326 #endif
327 
328     /* Delete the Object Table */
329     if (Process->ObjectTable)
330     {
331         /* Attach to the process */
332         KeStackAttachProcess(&Process->Pcb, &ApcState);
333 
334         /* Kill the Object Info */
335         ObKillProcess(Process);
336 
337         /* Detach */
338         KeUnstackDetachProcess(&ApcState);
339     }
340 
341     /* Check if we have an address space, and clean it */
342     if (Process->HasAddressSpace)
343     {
344         /* Attach to the process */
345         KeStackAttachProcess(&Process->Pcb, &ApcState);
346 
347         /* Clean the Address Space */
348         PspExitProcess(FALSE, Process);
349 
350         /* Detach */
351         KeUnstackDetachProcess(&ApcState);
352 
353         /* Completely delete the Address Space */
354         MmDeleteProcessAddressSpace(Process);
355     }
356 
357     /* See if we have a PID */
358     if (Process->UniqueProcessId)
359     {
360         /* Delete the PID */
361         if (!(ExDestroyHandle(PspCidTable, Process->UniqueProcessId, NULL)))
362         {
363             /* Something wrong happened, bugcheck */
364             KeBugCheck(CID_HANDLE_DELETION);
365         }
366     }
367 
368     /* Cleanup security information */
369     PspDeleteProcessSecurity(Process);
370 
371     /* Check if we have kept information on the Working Set */
372     if (Process->WorkingSetWatch)
373     {
374         /* Free it */
375         ExFreePool(Process->WorkingSetWatch);
376 
377         /* And return the quota it was taking up */
378         PsReturnProcessNonPagedPoolQuota(Process, 0x2000);
379     }
380 
381     /* Dereference the Device Map */
382     ObDereferenceDeviceMap(Process);
383 
384     /* Destroy the Quota Block */
385     PspDestroyQuotaBlock(Process);
386 }
387 
388 VOID
389 NTAPI
390 PspDeleteThread(IN PVOID ObjectBody)
391 {
392     PETHREAD Thread = (PETHREAD)ObjectBody;
393     PEPROCESS Process = Thread->ThreadsProcess;
394     PAGED_CODE();
395     PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
396     PSREFTRACE(Thread);
397     ASSERT(Thread->Tcb.Win32Thread == NULL);
398 
399     /* Check if we have a stack */
400     if (Thread->Tcb.InitialStack)
401     {
402         /* Release it */
403         MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
404                             Thread->Tcb.LargeStack);
405     }
406 
407     /* Check if we have a CID Handle */
408     if (Thread->Cid.UniqueThread)
409     {
410         /* Delete the CID Handle */
411         if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL)))
412         {
413             /* Something wrong happened, bugcheck */
414             KeBugCheck(CID_HANDLE_DELETION);
415         }
416     }
417 
418     /* Cleanup impersionation information */
419     PspDeleteThreadSecurity(Thread);
420 
421     /* Make sure the thread was inserted, before continuing */
422     if (!Process) return;
423 
424     /* Check if the thread list is valid */
425     if (Thread->ThreadListEntry.Flink)
426     {
427         /* Lock the thread's process */
428         KeEnterCriticalRegion();
429         ExAcquirePushLockExclusive(&Process->ProcessLock);
430 
431         /* Remove us from the list */
432         RemoveEntryList(&Thread->ThreadListEntry);
433 
434         /* Release the lock */
435         ExReleasePushLockExclusive(&Process->ProcessLock);
436         KeLeaveCriticalRegion();
437     }
438 
439     /* Dereference the Process */
440     ObDereferenceObject(Process);
441 }
442 
443 /*
444  * FUNCTION: Terminates the current thread
445  * See "Windows Internals" - Chapter 13, Page 50-53
446  */
447 VOID
448 NTAPI
449 PspExitThread(IN NTSTATUS ExitStatus)
450 {
451     CLIENT_DIED_MSG TerminationMsg;
452     NTSTATUS Status;
453     PTEB Teb;
454     PEPROCESS CurrentProcess;
455     PETHREAD Thread, OtherThread, PreviousThread = NULL;
456     PVOID DeallocationStack;
457     SIZE_T Dummy;
458     BOOLEAN Last = FALSE;
459     PTERMINATION_PORT TerminationPort, NextPort;
460     PLIST_ENTRY FirstEntry, CurrentEntry;
461     PKAPC Apc;
462     PTOKEN PrimaryToken;
463     PAGED_CODE();
464     PSTRACE(PS_KILL_DEBUG, "ExitStatus: %d\n", ExitStatus);
465 
466     /* Get the Current Thread and Process */
467     Thread = PsGetCurrentThread();
468     CurrentProcess = Thread->ThreadsProcess;
469     ASSERT((Thread) == PsGetCurrentThread());
470 
471     /* Can't terminate a thread if it attached another process */
472     if (KeIsAttachedProcess())
473     {
474         /* Bugcheck */
475         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
476                      (ULONG_PTR)CurrentProcess,
477                      (ULONG_PTR)Thread->Tcb.ApcState.Process,
478                      (ULONG_PTR)Thread->Tcb.ApcStateIndex,
479                      (ULONG_PTR)Thread);
480     }
481 
482     /* Lower to Passive Level */
483     KeLowerIrql(PASSIVE_LEVEL);
484 
485     /* Can't be a worker thread */
486     if (Thread->ActiveExWorker)
487     {
488         /* Bugcheck */
489         KeBugCheckEx(ACTIVE_EX_WORKER_THREAD_TERMINATION,
490                      (ULONG_PTR)Thread,
491                      0,
492                      0,
493                      0);
494     }
495 
496     /* Can't have pending APCs */
497     if (Thread->Tcb.CombinedApcDisable != 0)
498     {
499         /* Bugcheck */
500         KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
501                      0,
502                      Thread->Tcb.CombinedApcDisable,
503                      0,
504                      1);
505     }
506 
507     /* Lock the thread */
508     ExWaitForRundownProtectionRelease(&Thread->RundownProtect);
509 
510     /* Cleanup the power state */
511     PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState);
512 
513     /* Call the WMI Callback for Threads */
514     //WmiTraceThread(Thread, NULL, FALSE);
515 
516     /* Run Thread Notify Routines before we desintegrate the thread */
517     PspRunCreateThreadNotifyRoutines(Thread, FALSE);
518 
519     /* Lock the Process before we modify its thread entries */
520     KeEnterCriticalRegion();
521     ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
522 
523     /* Decrease the active thread count, and check if it's 0 */
524     if (!(--CurrentProcess->ActiveThreads))
525     {
526         /* Set the delete flag */
527         InterlockedOr((PLONG)&CurrentProcess->Flags, PSF_PROCESS_DELETE_BIT);
528 
529         /* Remember we are last */
530         Last = TRUE;
531 
532         /* Check if this termination is due to the thread dying */
533         if (ExitStatus == STATUS_THREAD_IS_TERMINATING)
534         {
535             /* Check if the last thread was pending */
536             if (CurrentProcess->ExitStatus == STATUS_PENDING)
537             {
538                 /* Use the last exit status */
539                 CurrentProcess->ExitStatus = CurrentProcess->
540                                              LastThreadExitStatus;
541             }
542         }
543         else
544         {
545             /* Just a normal exit, write the code */
546             CurrentProcess->ExitStatus = ExitStatus;
547         }
548 
549         /* Loop all the current threads */
550         FirstEntry = &CurrentProcess->ThreadListHead;
551         CurrentEntry = FirstEntry->Flink;
552         while (FirstEntry != CurrentEntry)
553         {
554             /* Get the thread on the list */
555             OtherThread = CONTAINING_RECORD(CurrentEntry,
556                                             ETHREAD,
557                                             ThreadListEntry);
558 
559             /* Check if it's a thread that's still alive */
560             if ((OtherThread != Thread) &&
561                 !(KeReadStateThread(&OtherThread->Tcb)) &&
562                 (ObReferenceObjectSafe(OtherThread)))
563             {
564                 /* It's a live thread and we referenced it, unlock process */
565                 ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
566                 KeLeaveCriticalRegion();
567 
568                 /* Wait on the thread */
569                 KeWaitForSingleObject(OtherThread,
570                                       Executive,
571                                       KernelMode,
572                                       FALSE,
573                                       NULL);
574 
575                 /* Check if we had a previous thread to dereference */
576                 if (PreviousThread) ObDereferenceObject(PreviousThread);
577 
578                 /* Remember the thread and re-lock the process */
579                 PreviousThread = OtherThread;
580                 KeEnterCriticalRegion();
581                 ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
582             }
583 
584             /* Go to the next thread */
585             CurrentEntry = CurrentEntry->Flink;
586         }
587     }
588     else if (ExitStatus != STATUS_THREAD_IS_TERMINATING)
589     {
590         /* Write down the exit status of the last thread to get killed */
591         CurrentProcess->LastThreadExitStatus = ExitStatus;
592     }
593 
594     /* Unlock the Process */
595     ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
596     KeLeaveCriticalRegion();
597 
598     /* Check if we had a previous thread to dereference */
599     if (PreviousThread) ObDereferenceObject(PreviousThread);
600 
601     /* Check if the process has a debug port and if this is a user thread */
602     if ((CurrentProcess->DebugPort) && !(Thread->SystemThread))
603     {
604         /* Notify the Debug API. */
605         Last ? DbgkExitProcess(CurrentProcess->ExitStatus) :
606                DbgkExitThread(ExitStatus);
607     }
608 
609     /* Check if this is a Critical Thread */
610     if ((KdDebuggerEnabled) && (Thread->BreakOnTermination))
611     {
612         /* Break to debugger */
613         PspCatchCriticalBreak("Critical thread 0x%p (in %s) exited\n",
614                               Thread,
615                               CurrentProcess->ImageFileName);
616     }
617 
618     /* Check if it's the last thread and this is a Critical Process */
619     if ((Last) && (CurrentProcess->BreakOnTermination))
620     {
621         /* Check if a debugger is here to handle this */
622         if (KdDebuggerEnabled)
623         {
624             /* Break to debugger */
625             PspCatchCriticalBreak("Critical  process 0x%p (in %s) exited\n",
626                                   CurrentProcess,
627                                   CurrentProcess->ImageFileName);
628         }
629         else
630         {
631             /* Bugcheck, we can't allow this */
632             KeBugCheckEx(CRITICAL_PROCESS_DIED,
633                          (ULONG_PTR)CurrentProcess,
634                          0,
635                          0,
636                          0);
637         }
638     }
639 
640     /* Sanity check */
641     ASSERT(Thread->Tcb.CombinedApcDisable == 0);
642 
643     /* Process the Termination Ports */
644     TerminationPort = Thread->TerminationPort;
645     if (TerminationPort)
646     {
647         /* Setup the message header */
648         TerminationMsg.h.u2.ZeroInit = 0;
649         TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
650         TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
651         TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
652                                             sizeof(PORT_MESSAGE);
653 
654         /* Loop each port */
655         do
656         {
657             /* Save the Create Time */
658             TerminationMsg.CreateTime = Thread->CreateTime;
659 
660             /* Loop trying to send message */
661             while (TRUE)
662             {
663                 /* Send the LPC Message */
664                 Status = LpcRequestPort(TerminationPort->Port,
665                                         &TerminationMsg.h);
666                 if ((Status == STATUS_NO_MEMORY) ||
667                     (Status == STATUS_INSUFFICIENT_RESOURCES))
668                 {
669                     /* Wait a bit and try again */
670                     KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
671                     continue;
672                 }
673                 break;
674             }
675 
676             /* Dereference this LPC Port */
677             ObDereferenceObject(TerminationPort->Port);
678 
679             /* Move to the next one */
680             NextPort = TerminationPort->Next;
681 
682             /* Free the Termination Port Object */
683             ExFreePoolWithTag(TerminationPort, '=TsP');
684 
685             /* Keep looping as long as there is a port */
686             TerminationPort = NextPort;
687         } while (TerminationPort);
688     }
689     else if (((ExitStatus == STATUS_THREAD_IS_TERMINATING) &&
690               (Thread->DeadThread)) ||
691              !(Thread->DeadThread))
692     {
693         /*
694          * This case is special and deserves some extra comments. What
695          * basically happens here is that this thread doesn't have a termination
696          * port, which means that it died before being fully created. Since we
697          * still have to notify an LPC Server, we'll use the exception port,
698          * which we know exists. However, we need to know how far the thread
699          * actually got created. We have three possibilities:
700          *
701          *  - NtCreateThread returned an error really early: DeadThread is set.
702          *  - NtCreateThread managed to create the thread: DeadThread is off.
703          *  - NtCreateThread was creating the thread (with DeadThread set,
704          *    but the thread got killed prematurely: STATUS_THREAD_IS_TERMINATING
705          *    is our exit code.)
706          *
707          * For the 2 & 3rd scenarios, the thread has been created far enough to
708          * warrant notification to the LPC Server.
709          */
710 
711         /* Setup the message header */
712         TerminationMsg.h.u2.ZeroInit = 0;
713         TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
714         TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
715         TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
716                                             sizeof(PORT_MESSAGE);
717 
718         /* Make sure the process has an exception port */
719         if (CurrentProcess->ExceptionPort)
720         {
721             /* Save the Create Time */
722             TerminationMsg.CreateTime = Thread->CreateTime;
723 
724             /* Loop trying to send message */
725             while (TRUE)
726             {
727                 /* Send the LPC Message */
728                 Status = LpcRequestPort(CurrentProcess->ExceptionPort,
729                                         &TerminationMsg.h);
730                 if ((Status == STATUS_NO_MEMORY) ||
731                     (Status == STATUS_INSUFFICIENT_RESOURCES))
732                 {
733                     /* Wait a bit and try again */
734                     KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
735                     continue;
736                 }
737                 break;
738             }
739         }
740     }
741 
742     /* Rundown Win32 Thread if there is one */
743     if (Thread->Tcb.Win32Thread) PspW32ThreadCallout(Thread,
744                                                      PsW32ThreadCalloutExit);
745 
746     /* If we are the last thread and have a W32 Process */
747     if ((Last) && (CurrentProcess->Win32Process))
748     {
749         /* Run it down too */
750         PspW32ProcessCallout(CurrentProcess, FALSE);
751     }
752 
753     /* Make sure Stack Swap is enabled */
754     if (!Thread->Tcb.EnableStackSwap)
755     {
756         /* Stack swap really shouldn't be disabled during exit! */
757         KeBugCheckEx(KERNEL_STACK_LOCKED_AT_EXIT, 0, 0, 0, 0);
758     }
759 
760     /* Cancel I/O for the thread. */
761     IoCancelThreadIo(Thread);
762 
763     /* Rundown Timers */
764     ExTimerRundown();
765 
766     /* FIXME: Rundown Registry Notifications (NtChangeNotify)
767     CmNotifyRunDown(Thread); */
768 
769     /* Rundown Mutexes */
770     KeRundownThread();
771 
772     /* Check if we have a TEB */
773     Teb = Thread->Tcb.Teb;
774     if (Teb)
775     {
776         /* Check if the thread is still alive */
777         if (!Thread->DeadThread)
778         {
779             /* Check if we need to free its stack */
780             if (Teb->FreeStackOnTermination)
781             {
782                 /* Set the TEB's Deallocation Stack as the Base Address */
783                 Dummy = 0;
784                 DeallocationStack = Teb->DeallocationStack;
785 
786                 /* Free the Thread's Stack */
787                 ZwFreeVirtualMemory(NtCurrentProcess(),
788                                     &DeallocationStack,
789                                     &Dummy,
790                                     MEM_RELEASE);
791             }
792 
793             /* Free the debug handle */
794             if (Teb->DbgSsReserved[1]) ObCloseHandle(Teb->DbgSsReserved[1],
795                                                      UserMode);
796         }
797 
798         /* Decommit the TEB */
799         MmDeleteTeb(CurrentProcess, Teb);
800         Thread->Tcb.Teb = NULL;
801     }
802 
803     /* Free LPC Data */
804     LpcExitThread(Thread);
805 
806     /* Save the exit status and exit time */
807     Thread->ExitStatus = ExitStatus;
808     KeQuerySystemTime(&Thread->ExitTime);
809 
810     /* Sanity check */
811     ASSERT(Thread->Tcb.CombinedApcDisable == 0);
812 
813     /* Check if this is the final thread or not */
814     if (Last)
815     {
816         /* Set the process exit time */
817         CurrentProcess->ExitTime = Thread->ExitTime;
818 
819         /* Exit the process */
820         PspExitProcess(TRUE, CurrentProcess);
821 
822         /* Get the process token and check if we need to audit */
823         PrimaryToken = PsReferencePrimaryToken(CurrentProcess);
824         if (SeDetailedAuditingWithToken(PrimaryToken))
825         {
826             /* Audit the exit */
827             SeAuditProcessExit(CurrentProcess);
828         }
829 
830         /* Dereference the process token */
831         ObFastDereferenceObject(&CurrentProcess->Token, PrimaryToken);
832 
833         /* Check if this is a VDM Process and rundown the VDM DPCs if so */
834         if (CurrentProcess->VdmObjects) { /* VdmRundownDpcs(CurrentProcess); */ }
835 
836         /* Kill the process in the Object Manager */
837         ObKillProcess(CurrentProcess);
838 
839         /* Check if we have a section object */
840         if (CurrentProcess->SectionObject)
841         {
842             /* Dereference and clear the Section Object */
843             ObDereferenceObject(CurrentProcess->SectionObject);
844             CurrentProcess->SectionObject = NULL;
845         }
846 
847         /* Check if the process is part of a job */
848         if (CurrentProcess->Job)
849         {
850             /* Remove the process from the job */
851             PspExitProcessFromJob(CurrentProcess->Job, CurrentProcess);
852         }
853     }
854 
855     /* Disable APCs */
856     KeEnterCriticalRegion();
857 
858     /* Disable APC queueing, force a resumption */
859     Thread->Tcb.ApcQueueable = FALSE;
860     KeForceResumeThread(&Thread->Tcb);
861 
862     /* Re-enable APCs */
863     KeLeaveCriticalRegion();
864 
865     /* Flush the User APCs */
866     FirstEntry = KeFlushQueueApc(&Thread->Tcb, UserMode);
867     if (FirstEntry)
868     {
869         /* Start with the first entry */
870         CurrentEntry = FirstEntry;
871         do
872         {
873            /* Get the APC */
874            Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
875 
876            /* Move to the next one */
877            CurrentEntry = CurrentEntry->Flink;
878 
879            /* Rundown the APC or de-allocate it */
880            if (Apc->RundownRoutine)
881            {
882               /* Call its own routine */
883               Apc->RundownRoutine(Apc);
884            }
885            else
886            {
887               /* Do it ourselves */
888               ExFreePool(Apc);
889            }
890         }
891         while (CurrentEntry != FirstEntry);
892     }
893 
894     /* Clean address space if this was the last thread */
895     if (Last) MmCleanProcessAddressSpace(CurrentProcess);
896 
897     /* Call the Lego routine */
898     if (Thread->Tcb.LegoData) PspRunLegoRoutine(&Thread->Tcb);
899 
900     /* Flush the APC queue, which should be empty */
901     FirstEntry = KeFlushQueueApc(&Thread->Tcb, KernelMode);
902     if ((FirstEntry) || (Thread->Tcb.CombinedApcDisable != 0))
903     {
904         /* Bugcheck time */
905         KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
906                      (ULONG_PTR)FirstEntry,
907                      Thread->Tcb.CombinedApcDisable,
908                      KeGetCurrentIrql(),
909                      0);
910     }
911 
912     /* Signal the process if this was the last thread */
913     if (Last) KeSetProcess(&CurrentProcess->Pcb, 0, FALSE);
914 
915     /* Terminate the Thread from the Scheduler */
916     KeTerminateThread(0);
917 }
918 
919 VOID
920 NTAPI
921 PsExitSpecialApc(IN PKAPC Apc,
922                  IN OUT PKNORMAL_ROUTINE* NormalRoutine,
923                  IN OUT PVOID* NormalContext,
924                  IN OUT PVOID* SystemArgument1,
925                  IN OUT PVOID* SystemArgument2)
926 {
927     NTSTATUS Status;
928     PAGED_CODE();
929     PSTRACE(PS_KILL_DEBUG,
930             "Apc: %p SystemArgument2: %p \n", Apc, SystemArgument2);
931 
932     /* Don't do anything unless we are in User-Mode */
933     if (Apc->SystemArgument2)
934     {
935         /* Free the APC */
936         Status = PtrToUlong(Apc->NormalContext);
937         PspExitApcRundown(Apc);
938 
939         /* Terminate the Thread */
940         PspExitThread(Status);
941     }
942 }
943 
944 VOID
945 NTAPI
946 PspExitNormalApc(IN PVOID NormalContext,
947                  IN PVOID SystemArgument1,
948                  IN PVOID SystemArgument2)
949 {
950     PKAPC Apc = (PKAPC)SystemArgument1;
951     PETHREAD Thread = PsGetCurrentThread();
952     PAGED_CODE();
953     PSTRACE(PS_KILL_DEBUG, "SystemArgument2: %p \n", SystemArgument2);
954 
955     /* This should never happen */
956     ASSERT(!(((ULONG_PTR)SystemArgument2) & 1));
957 
958     /* If we're here, this is not a System Thread, so kill it from User-Mode */
959     KeInitializeApc(Apc,
960                     &Thread->Tcb,
961                     OriginalApcEnvironment,
962                     PsExitSpecialApc,
963                     PspExitApcRundown,
964                     PspExitNormalApc,
965                     UserMode,
966                     NormalContext);
967 
968     /* Now insert the APC with the User-Mode Flag */
969     if (!(KeInsertQueueApc(Apc,
970                            Apc,
971                            (PVOID)((ULONG_PTR)SystemArgument2 | 1),
972                            2)))
973     {
974         /* Failed to insert, free the APC */
975         PspExitApcRundown(Apc);
976     }
977 
978     /* Set the APC Pending flag */
979     Thread->Tcb.ApcState.UserApcPending = TRUE;
980 }
981 
982 /*
983  * See "Windows Internals" - Chapter 13, Page 49
984  */
985 NTSTATUS
986 NTAPI
987 PspTerminateThreadByPointer(IN PETHREAD Thread,
988                             IN NTSTATUS ExitStatus,
989                             IN BOOLEAN bSelf)
990 {
991     PKAPC Apc;
992     NTSTATUS Status = STATUS_SUCCESS;
993     ULONG Flags;
994     PAGED_CODE();
995     PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %d\n", Thread, ExitStatus);
996     PSREFTRACE(Thread);
997 
998     /* Check if this is a Critical Thread, and Bugcheck */
999     if (Thread->BreakOnTermination)
1000     {
1001         /* Break to debugger */
1002         PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n",
1003                               Thread,
1004                               Thread->ThreadsProcess->ImageFileName);
1005     }
1006 
1007     /* Check if we are already inside the thread */
1008     if ((bSelf) || (PsGetCurrentThread() == Thread))
1009     {
1010         /* This should only happen at passive */
1011         ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
1012 
1013         /* Mark it as terminated */
1014         PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT);
1015 
1016         /* Directly terminate the thread */
1017         PspExitThread(ExitStatus);
1018     }
1019 
1020     /* This shouldn't be a system thread */
1021     if (Thread->SystemThread) return STATUS_ACCESS_DENIED;
1022 
1023     /* Allocate the APC */
1024     Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
1025     if (!Apc) return STATUS_INSUFFICIENT_RESOURCES;
1026 
1027     /* Set the Terminated Flag */
1028     Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT;
1029 
1030     /* Set it, and check if it was already set while we were running */
1031     if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) &
1032           CT_TERMINATED_BIT))
1033     {
1034         /* Initialize a Kernel Mode APC to Kill the Thread */
1035         KeInitializeApc(Apc,
1036                         &Thread->Tcb,
1037                         OriginalApcEnvironment,
1038                         PsExitSpecialApc,
1039                         PspExitApcRundown,
1040                         PspExitNormalApc,
1041                         KernelMode,
1042                         UlongToPtr(ExitStatus));
1043 
1044         /* Insert it into the APC Queue */
1045         if (!KeInsertQueueApc(Apc, Apc, NULL, 2))
1046         {
1047             /* The APC was already in the queue, fail */
1048             Status = STATUS_UNSUCCESSFUL;
1049         }
1050         else
1051         {
1052             /* Forcefully resume the thread and return */
1053             KeForceResumeThread(&Thread->Tcb);
1054             return Status;
1055         }
1056     }
1057 
1058     /* We failed, free the APC */
1059     ExFreePoolWithTag(Apc, TAG_TERMINATE_APC);
1060 
1061     /* Return Status */
1062     return Status;
1063 }
1064 
1065 BOOLEAN
1066 NTAPI
1067 PspIsProcessExiting(IN PEPROCESS Process)
1068 {
1069     return Process->Flags & PSF_PROCESS_EXITING_BIT;
1070 }
1071 
1072 VOID
1073 NTAPI
1074 PspExitProcess(IN BOOLEAN LastThread,
1075                IN PEPROCESS Process)
1076 {
1077     ULONG Actual;
1078     PAGED_CODE();
1079     PSTRACE(PS_KILL_DEBUG,
1080             "LastThread: %u Process: %p\n", LastThread, Process);
1081     PSREFTRACE(Process);
1082 
1083     /* Set Process Exit flag */
1084     InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_EXITING_BIT);
1085 
1086     /* Check if we are the last thread */
1087     if (LastThread)
1088     {
1089         /* Notify the WMI Process Callback */
1090         //WmiTraceProcess(Process, FALSE);
1091 
1092         /* Run the Notification Routines */
1093         PspRunCreateProcessNotifyRoutines(Process, FALSE);
1094     }
1095 
1096     /* Cleanup the power state */
1097     PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState);
1098 
1099     /* Clear the security port */
1100     if (!Process->SecurityPort)
1101     {
1102         /* So we don't double-dereference */
1103         Process->SecurityPort = (PVOID)1;
1104     }
1105     else if (Process->SecurityPort != (PVOID)1)
1106     {
1107         /* Dereference it */
1108         ObDereferenceObject(Process->SecurityPort);
1109         Process->SecurityPort = (PVOID)1;
1110     }
1111 
1112     /* Check if we are the last thread */
1113     if (LastThread)
1114     {
1115         /* Check if we have to set the Timer Resolution */
1116         if (Process->SetTimerResolution)
1117         {
1118             /* Set it to default */
1119             ZwSetTimerResolution(KeMaximumIncrement, 0, &Actual);
1120         }
1121 
1122         /* Check if we are part of a Job that has a completion port */
1123         if ((Process->Job) && (Process->Job->CompletionPort))
1124         {
1125             /* FIXME: Check job status code and do I/O completion if needed */
1126         }
1127 
1128         /* FIXME: Notify the Prefetcher */
1129     }
1130     else
1131     {
1132         /* Clear process' address space here */
1133         MmCleanProcessAddressSpace(Process);
1134     }
1135 }
1136 
1137 /* PUBLIC FUNCTIONS **********************************************************/
1138 
1139 /*
1140  * @implemented
1141  */
1142 NTSTATUS
1143 NTAPI
1144 PsTerminateSystemThread(IN NTSTATUS ExitStatus)
1145 {
1146     PETHREAD Thread = PsGetCurrentThread();
1147 
1148     /* Make sure this is a system thread */
1149     if (!Thread->SystemThread) return STATUS_INVALID_PARAMETER;
1150 
1151     /* Terminate it for real */
1152     return PspTerminateThreadByPointer(Thread, ExitStatus, TRUE);
1153 }
1154 
1155 /*
1156  * @implemented
1157  */
1158 NTSTATUS
1159 NTAPI
1160 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
1161                    IN NTSTATUS ExitStatus)
1162 {
1163     NTSTATUS Status;
1164     PEPROCESS Process, CurrentProcess = PsGetCurrentProcess();
1165     PETHREAD Thread, CurrentThread = PsGetCurrentThread();
1166     BOOLEAN KillByHandle;
1167     PAGED_CODE();
1168     PSTRACE(PS_KILL_DEBUG,
1169             "ProcessHandle: %p ExitStatus: %d\n", ProcessHandle, ExitStatus);
1170 
1171     /* Were we passed a process handle? */
1172     if (ProcessHandle)
1173     {
1174         /* Yes we were, use it */
1175         KillByHandle = TRUE;
1176     }
1177     else
1178     {
1179         /* We weren't... we assume this is suicide */
1180         KillByHandle = FALSE;
1181         ProcessHandle = NtCurrentProcess();
1182     }
1183 
1184     /* Get the Process Object */
1185     Status = ObReferenceObjectByHandle(ProcessHandle,
1186                                        PROCESS_TERMINATE,
1187                                        PsProcessType,
1188                                        KeGetPreviousMode(),
1189                                        (PVOID*)&Process,
1190                                        NULL);
1191     if (!NT_SUCCESS(Status)) return(Status);
1192 
1193     /* Check if this is a Critical Process, and Bugcheck */
1194     if (Process->BreakOnTermination)
1195     {
1196         /* Break to debugger */
1197         PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
1198                               Process,
1199                               Process->ImageFileName);
1200     }
1201 
1202     /* Lock the Process */
1203     if (!ExAcquireRundownProtection(&Process->RundownProtect))
1204     {
1205         /* Failed to lock, fail */
1206         ObDereferenceObject(Process);
1207         return STATUS_PROCESS_IS_TERMINATING;
1208     }
1209 
1210     /* Set the delete flag, unless the process is comitting suicide */
1211     if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT);
1212 
1213     /* Get the first thread */
1214     Status = STATUS_NOTHING_TO_TERMINATE;
1215     Thread = PsGetNextProcessThread(Process, NULL);
1216     if (Thread)
1217     {
1218         /* We know we have at least a thread */
1219         Status = STATUS_SUCCESS;
1220 
1221         /* Loop and kill the others */
1222         do
1223         {
1224             /* Ensure it's not ours*/
1225             if (Thread != CurrentThread)
1226             {
1227                 /* Kill it */
1228                 PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
1229             }
1230 
1231             /* Move to the next thread */
1232             Thread = PsGetNextProcessThread(Process, Thread);
1233         } while (Thread);
1234     }
1235 
1236     /* Unlock the process */
1237     ExReleaseRundownProtection(&Process->RundownProtect);
1238 
1239     /* Check if we are killing ourselves */
1240     if (Process == CurrentProcess)
1241     {
1242         /* Also make sure the caller gave us our handle */
1243         if (KillByHandle)
1244         {
1245             /* Dereference the process */
1246             ObDereferenceObject(Process);
1247 
1248             /* Terminate ourselves */
1249             PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE);
1250         }
1251     }
1252     else if (ExitStatus == DBG_TERMINATE_PROCESS)
1253     {
1254         /* Disable debugging on this process */
1255         DbgkClearProcessDebugObject(Process, NULL);
1256     }
1257 
1258     /* Check if there was nothing to terminate, or if we have a Debug Port */
1259     if ((Status == STATUS_NOTHING_TO_TERMINATE) ||
1260         ((Process->DebugPort) && (KillByHandle)))
1261     {
1262         /* Clear the handle table */
1263         ObClearProcessHandleTable(Process);
1264 
1265         /* Return status now */
1266         Status = STATUS_SUCCESS;
1267     }
1268 
1269     /* Decrease the reference count we added */
1270     ObDereferenceObject(Process);
1271 
1272     /* Return status */
1273     return Status;
1274 }
1275 
1276 NTSTATUS
1277 NTAPI
1278 NtTerminateThread(IN HANDLE ThreadHandle,
1279                   IN NTSTATUS ExitStatus)
1280 {
1281     PETHREAD Thread;
1282     PETHREAD CurrentThread = PsGetCurrentThread();
1283     NTSTATUS Status;
1284     PAGED_CODE();
1285     PSTRACE(PS_KILL_DEBUG,
1286             "ThreadHandle: %p ExitStatus: %d\n", ThreadHandle, ExitStatus);
1287 
1288     /* Handle the special NULL case */
1289     if (!ThreadHandle)
1290     {
1291         /* Check if we're the only thread left */
1292         if (PsGetCurrentProcess()->ActiveThreads == 1)
1293         {
1294             /* This is invalid */
1295             return STATUS_CANT_TERMINATE_SELF;
1296         }
1297 
1298         /* Terminate us directly */
1299         goto TerminateSelf;
1300     }
1301     else if (ThreadHandle == NtCurrentThread())
1302     {
1303 TerminateSelf:
1304         /* Terminate this thread */
1305         return PspTerminateThreadByPointer(CurrentThread,
1306                                            ExitStatus,
1307                                            TRUE);
1308     }
1309 
1310     /* We are terminating another thread, get the Thread Object */
1311     Status = ObReferenceObjectByHandle(ThreadHandle,
1312                                        THREAD_TERMINATE,
1313                                        PsThreadType,
1314                                        KeGetPreviousMode(),
1315                                        (PVOID*)&Thread,
1316                                        NULL);
1317     if (!NT_SUCCESS(Status)) return Status;
1318 
1319     /* Check to see if we're running in the same thread */
1320     if (Thread != CurrentThread)
1321     {
1322         /* Terminate it */
1323         Status = PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
1324 
1325         /* Dereference the Thread and return */
1326         ObDereferenceObject(Thread);
1327     }
1328     else
1329     {
1330         /* Dereference the thread and terminate ourselves */
1331         ObDereferenceObject(Thread);
1332         goto TerminateSelf;
1333     }
1334 
1335     /* Return status */
1336     return Status;
1337 }
1338 
1339 NTSTATUS
1340 NTAPI
1341 NtRegisterThreadTerminatePort(IN HANDLE PortHandle)
1342 {
1343     NTSTATUS Status;
1344     PTERMINATION_PORT TerminationPort;
1345     PVOID TerminationLpcPort;
1346     PETHREAD Thread;
1347     PAGED_CODE();
1348     PSTRACE(PS_KILL_DEBUG, "PortHandle: %p\n", PortHandle);
1349 
1350     /* Get the Port */
1351     Status = ObReferenceObjectByHandle(PortHandle,
1352                                        PORT_ALL_ACCESS,
1353                                        LpcPortObjectType,
1354                                        KeGetPreviousMode(),
1355                                        &TerminationLpcPort,
1356                                        NULL);
1357     if (!NT_SUCCESS(Status)) return(Status);
1358 
1359     /* Allocate the Port and make sure it suceeded */
1360     TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
1361                                             sizeof(TERMINATION_PORT),
1362                                             '=TsP');
1363     if(TerminationPort)
1364     {
1365         /* Associate the Port */
1366         Thread = PsGetCurrentThread();
1367         TerminationPort->Port = TerminationLpcPort;
1368         TerminationPort->Next = Thread->TerminationPort;
1369         Thread->TerminationPort = TerminationPort;
1370 
1371         /* Return success */
1372         return STATUS_SUCCESS;
1373     }
1374 
1375     /* Dereference and Fail */
1376     ObDereferenceObject(TerminationLpcPort);
1377     return STATUS_INSUFFICIENT_RESOURCES;
1378 }
1379