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