2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/kill.c
5 * PURPOSE: Thread Termination and Reaping
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS *******************************************************************/
19 PETHREAD PspReaperList
= NULL
;
20 WORK_QUEUE_ITEM PspReaperWorkItem
;
21 BOOLEAN PspReaping
= FALSE
;
22 extern LIST_ENTRY PsActiveProcessHead
;
23 extern FAST_MUTEX PspActiveProcessMutex
;
25 /* FUNCTIONS *****************************************************************/
29 PspReapRoutine(PVOID Context
)
32 PETHREAD Thread
, NewThread
;
35 DPRINT("Evil reaper running!!\n");
36 OldIrql
= KeAcquireDispatcherDatabaseLock();
38 /* Get the first Thread Entry */
39 Thread
= PspReaperList
;
41 DPRINT("PspReaperList: %x\n", Thread
);
43 /* Check to see if the list is empty */
46 /* Unlock the Dispatcher */
47 KeReleaseDispatcherDatabaseLock(OldIrql
);
49 /* Is there a thread on the list? */
52 /* Get the next Thread */
53 DPRINT("Thread: %x\n", Thread
);
54 DPRINT("Thread: %x\n", Thread
->ReaperLink
);
55 NewThread
= Thread
->ReaperLink
;
57 /* Remove reference to current thread */
58 ObDereferenceObject(Thread
);
60 /* Move to next Thread */
64 /* No more linked threads... Reacquire the Lock */
65 OldIrql
= KeAcquireDispatcherDatabaseLock();
67 /* Now try to get a new thread from the list */
68 Thread
= PspReaperList
;
70 DPRINT("PspReaperList: %x\n", Thread
);
72 /* Loop again if there is a new thread */
76 DPRINT("Done reaping\n");
77 KeReleaseDispatcherDatabaseLock(OldIrql
);
82 PspKillMostProcesses(VOID
)
84 PLIST_ENTRY current_entry
;
87 /* Acquire the Active Process Lock */
88 ExAcquireFastMutex(&PspActiveProcessMutex
);
90 /* Loop all processes on the list */
91 current_entry
= PsActiveProcessHead
.Flink
;
92 while (current_entry
!= &PsActiveProcessHead
)
94 current
= CONTAINING_RECORD(current_entry
, EPROCESS
, ActiveProcessLinks
);
95 current_entry
= current_entry
->Flink
;
97 if (current
->UniqueProcessId
!= PsInitialSystemProcess
->UniqueProcessId
&&
98 current
->UniqueProcessId
!= PsGetCurrentProcessId())
100 /* Terminate all the Threads in this Process */
101 PspTerminateProcessThreads(current
, STATUS_SUCCESS
);
105 /* Release the lock */
106 ExReleaseFastMutex(&PspActiveProcessMutex
);
111 PspTerminateProcessThreads(PEPROCESS Process
,
114 PLIST_ENTRY CurrentEntry
;
115 PETHREAD Thread
, CurrentThread
= PsGetCurrentThread();
117 CurrentEntry
= Process
->ThreadListHead
.Flink
;
118 while (CurrentEntry
!= &Process
->ThreadListHead
) {
120 /* Get the Current Thread */
121 Thread
= CONTAINING_RECORD(CurrentEntry
, ETHREAD
, ThreadListEntry
);
123 /* Move to the Next Thread */
124 CurrentEntry
= CurrentEntry
->Flink
;
126 /* Make sure it's not the one we're in */
127 if (Thread
!= CurrentThread
) {
129 /* Make sure it didn't already terminate */
130 if (!Thread
->Terminated
) {
132 Thread
->Terminated
= TRUE
;
134 /* Terminate it by APC */
135 PspTerminateThreadByPointer(Thread
, ExitStatus
);
143 PspDeleteProcess(PVOID ObjectBody
)
145 PEPROCESS Process
= (PEPROCESS
)ObjectBody
;
147 DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody
);
149 /* Remove it from the Active List */
150 ExAcquireFastMutex(&PspActiveProcessMutex
);
151 RemoveEntryList(&Process
->ActiveProcessLinks
);
152 ExReleaseFastMutex(&PspActiveProcessMutex
);
154 /* Delete the CID Handle */
155 if(Process
->UniqueProcessId
!= NULL
) {
157 PsDeleteCidHandle(Process
->UniqueProcessId
, PsProcessType
);
161 KDB_DELETEPROCESS_HOOK(Process
);
163 /* Dereference the Token */
164 SeDeassignPrimaryToken(Process
);
166 /* Release Memory Information */
167 MmReleaseMmInfo(Process
);
169 /* Delete the W32PROCESS structure if there's one associated */
170 if(Process
->Win32Process
!= NULL
) ExFreePool(Process
->Win32Process
);
175 PspDeleteThread(PVOID ObjectBody
)
177 PETHREAD Thread
= (PETHREAD
)ObjectBody
;
178 PEPROCESS Process
= Thread
->ThreadsProcess
;
180 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody
, Thread
->ThreadsProcess
);
182 /* Deassociate the Process */
183 Thread
->ThreadsProcess
= NULL
;
185 /* Delete the CID Handle */
186 if(Thread
->Cid
.UniqueThread
!= NULL
) {
188 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
191 /* Free the W32THREAD structure if present */
192 if(Thread
->Tcb
.Win32Thread
!= NULL
) ExFreePool (Thread
->Tcb
.Win32Thread
);
194 /* Release the Kernel Stack */
195 MmDeleteKernelStack((PVOID
)Thread
->Tcb
.StackLimit
, FALSE
);
197 /* Dereference the Process */
198 ObDereferenceObject(Process
);
202 * FUNCTION: Terminates the current thread
203 * See "Windows Internals" - Chapter 13, Page 50-53
207 PspExitThread(NTSTATUS ExitStatus
)
209 PETHREAD CurrentThread
;
211 PEPROCESS CurrentProcess
;
212 PTERMINATION_PORT TerminationPort
;
215 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus
, PsGetCurrentThread());
217 /* Get the Current Thread and Process */
218 CurrentThread
= PsGetCurrentThread();
219 CurrentProcess
= CurrentThread
->ThreadsProcess
;
221 /* Set the Exit Status and Exit Time */
222 CurrentThread
->ExitStatus
= ExitStatus
;
223 KeQuerySystemTime(&CurrentThread
->ExitTime
);
225 /* Can't terminate a thread if it attached another process */
226 if (KeIsAttachedProcess()) {
228 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
, (ULONG
) CurrentProcess
,
229 (ULONG
) CurrentThread
->Tcb
.ApcState
.Process
,
230 (ULONG
) CurrentThread
->Tcb
.ApcStateIndex
,
231 (ULONG
) CurrentThread
);
234 /* Lower to Passive Level */
235 KeLowerIrql(PASSIVE_LEVEL
);
237 /* Lock the Process before we modify its thread entries */
238 PsLockProcess(CurrentProcess
, FALSE
);
240 /* wake up the thread so we don't deadlock on PsLockProcess */
241 KeForceResumeThread(&CurrentThread
->Tcb
);
243 /* Run Thread Notify Routines before we desintegrate the thread */
244 PspRunCreateThreadNotifyRoutines(CurrentThread
, FALSE
);
246 /* Remove the thread from the thread list of its process */
247 RemoveEntryList(&CurrentThread
->ThreadListEntry
);
248 Last
= IsListEmpty(&CurrentProcess
->ThreadListHead
);
250 /* Set the last Thread Exit Status */
251 CurrentProcess
->LastThreadExitStatus
= ExitStatus
;
255 /* Save the Exit Time if not already done by NtTerminateProcess. This
256 happens when the last thread just terminates without explicitly
257 terminating the process. */
258 CurrentProcess
->ExitTime
= CurrentThread
->ExitTime
;
261 /* Check if the process has a debug port */
262 if (CurrentProcess
->DebugPort
) {
264 /* Notify the Debug API. TODO */
265 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
268 /* Process the Termination Ports */
269 while ((TerminationPort
= CurrentThread
->TerminationPort
)) {
271 DPRINT("TerminationPort: %p\n", TerminationPort
);
273 /* Get the next one */
274 CurrentThread
->TerminationPort
= TerminationPort
->Next
;
276 /* Send the LPC Message */
277 LpcSendTerminationPort(TerminationPort
->Port
, CurrentThread
->CreateTime
);
280 ExFreePool(TerminationPort
);
283 /* Rundown Win32 Structures */
284 PsTerminateWin32Thread(CurrentThread
);
285 if (Last
) PsTerminateWin32Process(CurrentProcess
);
287 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
288 //CmNotifyRunDown(CurrentThread);
291 if((Teb
= CurrentThread
->Tcb
.Teb
))
293 /* Clean up the stack first, if requested */
294 if (Teb
->FreeStackOnTermination
)
297 ZwFreeVirtualMemory(NtCurrentProcess(),
298 &Teb
->DeallocationStack
,
303 DPRINT("Decommit teb at %p\n", Teb
);
304 MmDeleteTeb(CurrentProcess
, Teb
);
305 CurrentThread
->Tcb
.Teb
= NULL
;
308 /* The last Thread shuts down the Process */
309 if (Last
) PspExitProcess(CurrentProcess
);
311 /* Unlock the Process */
312 PsUnlockProcess(CurrentProcess
);
314 /* Cancel I/O for the thread. */
315 IoCancelThreadIo(CurrentThread
);
319 KeCancelTimer(&CurrentThread
->Tcb
.Timer
);
321 /* If the Processor Control Block's NpxThread points to the current thread
324 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread
,
326 (PKPROCESS
)CurrentThread
);
328 /* Rundown Mutexes */
331 /* Terminate the Thread from the Scheduler */
332 KeTerminateThread(0);
333 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread
, PsGetCurrentThread());
339 PsExitSpecialApc(PKAPC Apc
,
340 PKNORMAL_ROUTINE
* NormalRoutine
,
341 PVOID
* NormalContext
,
342 PVOID
* SystemArgument1
,
343 PVOID
* SystemArguemnt2
)
345 NTSTATUS ExitStatus
= (NTSTATUS
)Apc
->NormalContext
;
347 DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n", PsGetCurrentThread(), PsGetCurrentProcess());
352 /* Terminate the Thread */
353 PspExitThread(ExitStatus
);
355 /* we should never reach this point! */
361 PspExitNormalApc(PVOID NormalContext
,
362 PVOID SystemArgument1
,
363 PVOID SystemArgument2
)
365 /* Not fully supported yet... must work out some issues that
366 * I don't understand yet -- Alex
369 PspExitThread((NTSTATUS
)NormalContext
);
371 /* we should never reach this point! */
376 * See "Windows Internals" - Chapter 13, Page 49
380 PspTerminateThreadByPointer(PETHREAD Thread
,
385 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
388 /* Check if we are already in the right context */
389 if (PsGetCurrentThread() == Thread
) {
391 /* Directly terminate the thread */
392 PspExitThread(ExitStatus
);
394 /* we should never reach this point! */
398 /* Allocate the APC */
399 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG_TERMINATE_APC
);
401 /* Initialize a Kernel Mode APC to Kill the Thread */
404 OriginalApcEnvironment
,
411 /* Insert it into the APC Queue */
412 KeInsertQueueApc(Apc
,
417 /* Forcefully resume the thread */
418 KeForceResumeThread(&Thread
->Tcb
);
423 PspExitProcess(PEPROCESS Process
)
425 DPRINT("PspExitProcess 0x%x\n", Process
);
427 PspRunCreateProcessNotifyRoutines(Process
, FALSE
);
429 /* close all handles associated with our process, this needs to be done
430 when the last thread still runs */
431 ObKillProcess(Process
);
433 KeSetProcess(&Process
->Pcb
, IO_NO_INCREMENT
);
435 return(STATUS_SUCCESS
);
440 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL
,
441 IN NTSTATUS ExitStatus
)
445 PETHREAD CurrentThread
;
446 BOOLEAN KillByHandle
;
450 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
451 ProcessHandle
, ExitStatus
);
453 KillByHandle
= (ProcessHandle
!= NULL
);
455 /* Get the Process Object */
456 Status
= ObReferenceObjectByHandle((KillByHandle
? ProcessHandle
: NtCurrentProcess()),
462 if (!NT_SUCCESS(Status
)) {
464 DPRINT1("Invalid handle to Process\n");
468 CurrentThread
= PsGetCurrentThread();
470 PsLockProcess(Process
, FALSE
);
472 if(Process
->ExitTime
.QuadPart
!= 0)
474 PsUnlockProcess(Process
);
475 ObDereferenceObject(Process
);
476 return STATUS_PROCESS_IS_TERMINATING
;
479 /* Terminate all the Process's Threads */
480 PspTerminateProcessThreads(Process
, ExitStatus
);
482 /* only kill the calling thread if it either passed a process handle or
483 NtCurrentProcess() */
486 /* set the exit time as we're about to release the process lock before
487 we kill ourselves to prevent threads outside of our process trying
489 KeQuerySystemTime(&Process
->ExitTime
);
491 /* Only master thread remains... kill it off */
492 if (CurrentThread
->ThreadsProcess
== Process
) {
494 /* mark our thread as terminating so attempts to terminate it, when
495 unlocking the process, fail */
496 CurrentThread
->Terminated
= TRUE
;
498 PsUnlockProcess(Process
);
500 /* we can safely dereference the process because the current thread
501 holds a reference to it until it gets reaped */
502 ObDereferenceObject(Process
);
504 /* now the other threads get a chance to terminate, we don't wait but
505 just kill ourselves right now. The process will be run down when the
506 last thread terminates */
508 PspExitThread(ExitStatus
);
510 /* we should never reach this point! */
515 /* unlock and dereference the process so the threads can kill themselves */
516 PsUnlockProcess(Process
);
517 ObDereferenceObject(Process
);
519 return(STATUS_SUCCESS
);
524 NtTerminateThread(IN HANDLE ThreadHandle
,
525 IN NTSTATUS ExitStatus
)
532 /* Handle the special NULL case */
535 /* Check if we're the only thread left */
536 if (IsListEmpty(&PsGetCurrentProcess()->Pcb
.ThreadListHead
))
538 /* This is invalid */
539 DPRINT1("Can't terminate self\n");
540 return STATUS_CANT_TERMINATE_SELF
;
544 /* Use current handle */
545 ThreadHandle
= NtCurrentThread();
549 /* Get the Thread Object */
550 Status
= ObReferenceObjectByHandle(ThreadHandle
,
556 if (!NT_SUCCESS(Status
)) {
558 DPRINT1("Could not reference thread object\n");
562 /* Make sure this is not a system thread */
563 if (PsIsSystemThread(Thread
)) {
565 DPRINT1("Trying to Terminate a system thread!\n");
566 ObDereferenceObject(Thread
);
567 return STATUS_INVALID_PARAMETER
;
570 /* Check to see if we're running in the same thread */
571 if (Thread
!= PsGetCurrentThread()) {
573 /* we need to lock the process to make sure it's not already terminating */
574 PsLockProcess(Thread
->ThreadsProcess
, FALSE
);
576 /* This isn't our thread, terminate it if not already done */
577 if (!Thread
->Terminated
) {
579 Thread
->Terminated
= TRUE
;
582 PspTerminateThreadByPointer(Thread
, ExitStatus
);
585 PsUnlockProcess(Thread
->ThreadsProcess
);
587 /* Dereference the Thread and return */
588 ObDereferenceObject(Thread
);
592 Thread
->Terminated
= TRUE
;
594 /* it's safe to dereference thread, there's at least the keep-alive
595 reference which will be removed by the thread reaper causing the
596 thread to be finally destroyed */
597 ObDereferenceObject(Thread
);
599 /* Terminate him, he's ours */
600 PspExitThread(ExitStatus
);
602 /* We do never reach this point */
606 return(STATUS_SUCCESS
);
614 PsTerminateSystemThread(NTSTATUS ExitStatus
)
616 PETHREAD Thread
= PsGetCurrentThread();
618 /* Make sure this is a system thread */
619 if (!PsIsSystemThread(Thread
)) {
621 DPRINT1("Trying to Terminate a non-system thread!\n");
622 return STATUS_INVALID_PARAMETER
;
625 /* Terminate it for real */
626 PspExitThread(ExitStatus
);
628 /* we should never reach this point! */
631 return(STATUS_SUCCESS
);
636 NtRegisterThreadTerminatePort(HANDLE PortHandle
)
639 PTERMINATION_PORT TerminationPort
;
640 PVOID TerminationLpcPort
;
646 Status
= ObReferenceObjectByHandle(PortHandle
,
652 if (!NT_SUCCESS(Status
)) {
654 DPRINT1("Failed to reference Port\n");
658 /* Allocate the Port and make sure it suceeded */
659 if((TerminationPort
= ExAllocatePoolWithTag(NonPagedPool
,
660 sizeof(PTERMINATION_PORT
),
661 TAG('P', 's', 'T', '=')))) {
663 /* Associate the Port */
664 Thread
= PsGetCurrentThread();
665 TerminationPort
->Port
= TerminationLpcPort
;
666 DPRINT("TerminationPort: %p\n", TerminationPort
);
667 TerminationPort
->Next
= Thread
->TerminationPort
;
668 Thread
->TerminationPort
= TerminationPort
;
669 DPRINT("TerminationPort: %p\n", Thread
->TerminationPort
);
672 return(STATUS_SUCCESS
);
676 /* Dereference and Fail */
677 ObDereferenceObject(TerminationPort
);
678 return(STATUS_INSUFFICIENT_RESOURCES
);