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