f45cd2ae74d9da22c2c787fd4de38f819b2b6687
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /*
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
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 PETHREAD PspReaperList = NULL;
20 WORK_QUEUE_ITEM PspReaperWorkItem;
21 BOOLEAN PspReaping = FALSE;
22 extern LIST_ENTRY PsActiveProcessHead;
23 extern FAST_MUTEX PspActiveProcessMutex;
24
25 /* FUNCTIONS *****************************************************************/
26
27 VOID
28 STDCALL
29 PspReapRoutine(PVOID Context)
30 {
31 KIRQL OldIrql;
32 PETHREAD Thread, NewThread;
33
34 /* Acquire lock */
35 DPRINT("Evil reaper running!!\n");
36 OldIrql = KeAcquireDispatcherDatabaseLock();
37
38 /* Get the first Thread Entry */
39 Thread = PspReaperList;
40 PspReaperList = NULL;
41 DPRINT("PspReaperList: %x\n", Thread);
42
43 /* Check to see if the list is empty */
44 do {
45
46 /* Unlock the Dispatcher */
47 KeReleaseDispatcherDatabaseLock(OldIrql);
48
49 /* Is there a thread on the list? */
50 while (Thread) {
51
52 /* Get the next Thread */
53 DPRINT("Thread: %x\n", Thread);
54 DPRINT("Thread: %x\n", Thread->ReaperLink);
55 NewThread = Thread->ReaperLink;
56
57 /* Remove reference to current thread */
58 ObDereferenceObject(Thread);
59
60 /* Move to next Thread */
61 Thread = NewThread;
62 }
63
64 /* No more linked threads... Reacquire the Lock */
65 OldIrql = KeAcquireDispatcherDatabaseLock();
66
67 /* Now try to get a new thread from the list */
68 Thread = PspReaperList;
69 PspReaperList = NULL;
70 DPRINT("PspReaperList: %x\n", Thread);
71
72 /* Loop again if there is a new thread */
73 } while (Thread);
74
75 PspReaping = FALSE;
76 DPRINT("Done reaping\n");
77 KeReleaseDispatcherDatabaseLock(OldIrql);
78 }
79
80 VOID
81 STDCALL
82 PspKillMostProcesses(VOID)
83 {
84 PLIST_ENTRY current_entry;
85 PEPROCESS current;
86
87 ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess->UniqueProcessId);
88
89 /* Acquire the Active Process Lock */
90 ExAcquireFastMutex(&PspActiveProcessMutex);
91
92 /* Loop all processes on the list */
93 current_entry = PsActiveProcessHead.Flink;
94 while (current_entry != &PsActiveProcessHead)
95 {
96 current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
97 current_entry = current_entry->Flink;
98
99 if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId)
100 {
101 /* Terminate all the Threads in this Process */
102 PspTerminateProcessThreads(current, STATUS_SUCCESS);
103 }
104 }
105
106 /* Release the lock */
107 ExReleaseFastMutex(&PspActiveProcessMutex);
108 }
109
110 VOID
111 STDCALL
112 PspTerminateProcessThreads(PEPROCESS Process,
113 NTSTATUS ExitStatus)
114 {
115 PLIST_ENTRY CurrentEntry;
116 PETHREAD Thread, CurrentThread = PsGetCurrentThread();
117
118 CurrentEntry = Process->ThreadListHead.Flink;
119 while (CurrentEntry != &Process->ThreadListHead) {
120
121 /* Get the Current Thread */
122 Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
123
124 /* Move to the Next Thread */
125 CurrentEntry = CurrentEntry->Flink;
126
127 /* Make sure it's not the one we're in */
128 if (Thread != CurrentThread) {
129
130 /* Make sure it didn't already terminate */
131 if (!Thread->Terminated) {
132
133 Thread->Terminated = TRUE;
134
135 /* Terminate it by APC */
136 PspTerminateThreadByPointer(Thread, ExitStatus);
137 }
138 }
139 }
140 }
141
142 VOID
143 STDCALL
144 PspDeleteProcess(PVOID ObjectBody)
145 {
146 PEPROCESS Process = (PEPROCESS)ObjectBody;
147
148 DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
149
150 /* Remove it from the Active List */
151 ExAcquireFastMutex(&PspActiveProcessMutex);
152 RemoveEntryList(&Process->ActiveProcessLinks);
153 ExReleaseFastMutex(&PspActiveProcessMutex);
154
155 /* Delete the CID Handle */
156 if(Process->UniqueProcessId != NULL) {
157
158 PsDeleteCidHandle(Process->UniqueProcessId, PsProcessType);
159 }
160
161 /* KDB hook */
162 KDB_DELETEPROCESS_HOOK(Process);
163
164 /* Dereference the Token */
165 SeDeassignPrimaryToken(Process);
166
167 /* Release Memory Information */
168 MmReleaseMmInfo(Process);
169
170 /* Delete the W32PROCESS structure if there's one associated */
171 if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
172 }
173
174 VOID
175 STDCALL
176 PspDeleteThread(PVOID ObjectBody)
177 {
178 PETHREAD Thread = (PETHREAD)ObjectBody;
179 PEPROCESS Process = Thread->ThreadsProcess;
180
181 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
182
183 /* Deassociate the Process */
184 Thread->ThreadsProcess = NULL;
185
186 /* Delete the CID Handle */
187 if(Thread->Cid.UniqueThread != NULL) {
188
189 PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
190 }
191
192 /* Free the W32THREAD structure if present */
193 if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
194
195 /* Release the Kernel Stack */
196 MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, FALSE);
197
198 /* Dereference the Process */
199 ObDereferenceObject(Process);
200 }
201
202 /*
203 * FUNCTION: Terminates the current thread
204 * See "Windows Internals" - Chapter 13, Page 50-53
205 */
206 VOID
207 STDCALL
208 PspExitThread(NTSTATUS ExitStatus)
209 {
210 PETHREAD CurrentThread;
211 BOOLEAN Last;
212 PEPROCESS CurrentProcess;
213 PTERMINATION_PORT TerminationPort;
214 PTEB Teb;
215 KIRQL oldIrql;
216
217 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
218
219 /* Get the Current Thread and Process */
220 CurrentThread = PsGetCurrentThread();
221 CurrentProcess = CurrentThread->ThreadsProcess;
222
223 /* Set the Exit Status and Exit Time */
224 CurrentThread->ExitStatus = ExitStatus;
225 KeQuerySystemTime(&CurrentThread->ExitTime);
226
227 /* Can't terminate a thread if it attached another process */
228 if (KeIsAttachedProcess()) {
229
230 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
231 (ULONG) CurrentThread->Tcb.ApcState.Process,
232 (ULONG) CurrentThread->Tcb.ApcStateIndex,
233 (ULONG) CurrentThread);
234 }
235
236 /* Lower to Passive Level */
237 KeLowerIrql(PASSIVE_LEVEL);
238
239 /* Lock the Process before we modify its thread entries */
240 PsLockProcess(CurrentProcess, FALSE);
241
242 /* wake up the thread so we don't deadlock on PsLockProcess */
243 KeForceResumeThread(&CurrentThread->Tcb);
244
245 /* Run Thread Notify Routines before we desintegrate the thread */
246 PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
247
248 /* Remove the thread from the thread list of its process */
249 RemoveEntryList(&CurrentThread->ThreadListEntry);
250 Last = IsListEmpty(&CurrentProcess->ThreadListHead);
251
252 /* Set the last Thread Exit Status */
253 CurrentProcess->LastThreadExitStatus = ExitStatus;
254
255 if (Last) {
256
257 /* Save the Exit Time if not already done by NtTerminateProcess. This
258 happens when the last thread just terminates without explicitly
259 terminating the process. */
260 CurrentProcess->ExitTime = CurrentThread->ExitTime;
261 }
262
263 /* Check if the process has a debug port */
264 if (CurrentProcess->DebugPort) {
265
266 /* Notify the Debug API. TODO */
267 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
268 }
269
270 /* Process the Termination Ports */
271 while ((TerminationPort = CurrentThread->TerminationPort)) {
272
273 DPRINT("TerminationPort: %p\n", TerminationPort);
274
275 /* Get the next one */
276 CurrentThread->TerminationPort = TerminationPort->Next;
277
278 /* Send the LPC Message */
279 LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
280
281 /* Free the Port */
282 ExFreePool(TerminationPort);
283 }
284
285 /* Rundown Win32 Structures */
286 PsTerminateWin32Thread(CurrentThread);
287 if (Last) PsTerminateWin32Process(CurrentProcess);
288
289 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
290 //CmNotifyRunDown(CurrentThread);
291
292 /* Free the TEB */
293 if((Teb = CurrentThread->Tcb.Teb))
294 {
295 /* Clean up the stack first, if requested */
296 if (Teb->FreeStackOnTermination)
297 {
298 ULONG Dummy = 0;
299 ZwFreeVirtualMemory(NtCurrentProcess(),
300 &Teb->DeallocationStack,
301 &Dummy,
302 MEM_RELEASE);
303 }
304
305 DPRINT("Decommit teb at %p\n", Teb);
306 MmDeleteTeb(CurrentProcess, Teb);
307 CurrentThread->Tcb.Teb = NULL;
308 }
309
310 /* The last Thread shuts down the Process */
311 if (Last) PspExitProcess(CurrentProcess);
312
313 /* Unlock the Process */
314 PsUnlockProcess(CurrentProcess);
315
316 /* Cancel I/O for the thread. */
317 IoCancelThreadIo(CurrentThread);
318
319 /* Rundown Timers */
320 ExTimerRundown();
321 KeCancelTimer(&CurrentThread->Tcb.Timer);
322
323 /* If the Processor Control Block's NpxThread points to the current thread
324 * unset it.
325 */
326 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
327 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
328 NULL,
329 (PKPROCESS)CurrentThread);
330 KeLowerIrql(oldIrql);
331
332 /* Rundown Mutexes */
333 KeRundownThread();
334
335 /* Terminate the Thread from the Scheduler */
336 KeTerminateThread(0);
337 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
338 KEBUGCHECK(0);
339 }
340
341 VOID
342 STDCALL
343 PsExitSpecialApc(PKAPC Apc,
344 PKNORMAL_ROUTINE* NormalRoutine,
345 PVOID* NormalContext,
346 PVOID* SystemArgument1,
347 PVOID* SystemArguemnt2)
348 {
349 DPRINT1("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n",
350 PsGetCurrentThread(), PsGetCurrentProcess());
351
352 /* Don't do anything unless we are in User-Mode */
353 if (Apc->SystemArgument2)
354 {
355 NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
356
357 /* Free the APC */
358 ExFreePool(Apc);
359
360 /* Terminate the Thread */
361 PspExitThread(ExitStatus);
362
363 /* we should never reach this point! */
364 KEBUGCHECK(0);
365 }
366 }
367
368 VOID
369 STDCALL
370 PspExitNormalApc(PVOID NormalContext,
371 PVOID SystemArgument1,
372 PVOID SystemArgument2)
373 {
374 PKAPC Apc = (PKAPC)SystemArgument1;
375 PETHREAD Thread = PsGetCurrentThread();
376 NTSTATUS ExitStatus;
377
378 DPRINT1("PspExitNormalApc called: 0x%x (proc: 0x%x)\n",
379 PsGetCurrentThread(), PsGetCurrentProcess());
380
381 /* This should never happen */
382 ASSERT(!SystemArgument2);
383
384 /* If this is a system thread, we can safely kill it from Kernel-Mode */
385 if (PsIsSystemThread(Thread))
386 {
387 /* Get the Exit Status */
388 DPRINT1("Killing System Thread\n");
389 ExitStatus = (NTSTATUS)Apc->NormalContext;
390
391 /* Free the APC */
392 ExFreePool(Apc);
393
394 /* Exit the Thread */
395 PspExitThread(ExitStatus);
396 }
397
398 /* If we're here, this is not a System Thread, so kill it from User-Mode */
399 DPRINT1("Initializing User-Mode APC\n");
400 KeInitializeApc(Apc,
401 &Thread->Tcb,
402 OriginalApcEnvironment,
403 PsExitSpecialApc,
404 NULL,
405 PspExitNormalApc,
406 UserMode,
407 NormalContext);
408
409 /* Now insert the APC with the User-Mode Flag */
410 KeInsertQueueApc(Apc, Apc, (PVOID)UserMode, 2);
411
412 /* Forcefully resume the thread */
413 KeForceResumeThread(&Thread->Tcb);
414 }
415
416 /*
417 * See "Windows Internals" - Chapter 13, Page 49
418 */
419 VOID
420 STDCALL
421 PspTerminateThreadByPointer(PETHREAD Thread,
422 NTSTATUS ExitStatus)
423 {
424 PKAPC Apc;
425
426 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
427 Thread, ExitStatus);
428
429 /* Check if we are already in the right context */
430 if (PsGetCurrentThread() == Thread) {
431
432 /* Directly terminate the thread */
433 PspExitThread(ExitStatus);
434
435 /* we should never reach this point! */
436 KEBUGCHECK(0);
437 }
438
439 /* Allocate the APC */
440 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
441
442 /* Initialize a Kernel Mode APC to Kill the Thread */
443 KeInitializeApc(Apc,
444 &Thread->Tcb,
445 OriginalApcEnvironment,
446 PsExitSpecialApc,
447 NULL,
448 PspExitNormalApc,
449 KernelMode,
450 (PVOID)ExitStatus);
451
452 /* Insert it into the APC Queue */
453 KeInsertQueueApc(Apc,
454 Apc,
455 NULL,
456 2);
457
458 /* Forcefully resume the thread */
459 KeForceResumeThread(&Thread->Tcb);
460 }
461
462 NTSTATUS
463 STDCALL
464 PspExitProcess(PEPROCESS Process)
465 {
466 DPRINT("PspExitProcess 0x%x\n", Process);
467
468 PspRunCreateProcessNotifyRoutines(Process, FALSE);
469
470 /* close all handles associated with our process, this needs to be done
471 when the last thread still runs */
472 ObKillProcess(Process);
473
474 KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
475
476 return(STATUS_SUCCESS);
477 }
478
479 NTSTATUS
480 STDCALL
481 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
482 IN NTSTATUS ExitStatus)
483 {
484 NTSTATUS Status;
485 PEPROCESS Process;
486 PETHREAD CurrentThread;
487 BOOLEAN KillByHandle;
488
489 PAGED_CODE();
490
491 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
492 ProcessHandle, ExitStatus);
493
494 KillByHandle = (ProcessHandle != NULL);
495
496 /* Get the Process Object */
497 Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
498 PROCESS_TERMINATE,
499 PsProcessType,
500 KeGetPreviousMode(),
501 (PVOID*)&Process,
502 NULL);
503 if (!NT_SUCCESS(Status)) {
504
505 DPRINT1("Invalid handle to Process\n");
506 return(Status);
507 }
508
509 CurrentThread = PsGetCurrentThread();
510
511 PsLockProcess(Process, FALSE);
512
513 if(Process->ExitTime.QuadPart != 0)
514 {
515 PsUnlockProcess(Process);
516 ObDereferenceObject(Process);
517 return STATUS_PROCESS_IS_TERMINATING;
518 }
519
520 /* Terminate all the Process's Threads */
521 PspTerminateProcessThreads(Process, ExitStatus);
522
523 /* only kill the calling thread if it either passed a process handle or
524 NtCurrentProcess() */
525 if (KillByHandle) {
526
527 /* set the exit time as we're about to release the process lock before
528 we kill ourselves to prevent threads outside of our process trying
529 to kill us */
530 KeQuerySystemTime(&Process->ExitTime);
531
532 /* Only master thread remains... kill it off */
533 if (CurrentThread->ThreadsProcess == Process) {
534
535 /* mark our thread as terminating so attempts to terminate it, when
536 unlocking the process, fail */
537 CurrentThread->Terminated = TRUE;
538
539 PsUnlockProcess(Process);
540
541 /* we can safely dereference the process because the current thread
542 holds a reference to it until it gets reaped */
543 ObDereferenceObject(Process);
544
545 /* now the other threads get a chance to terminate, we don't wait but
546 just kill ourselves right now. The process will be run down when the
547 last thread terminates */
548
549 PspExitThread(ExitStatus);
550
551 /* we should never reach this point! */
552 KEBUGCHECK(0);
553 }
554 }
555
556 /* unlock and dereference the process so the threads can kill themselves */
557 PsUnlockProcess(Process);
558 ObDereferenceObject(Process);
559
560 return(STATUS_SUCCESS);
561 }
562
563 NTSTATUS
564 STDCALL
565 NtTerminateThread(IN HANDLE ThreadHandle,
566 IN NTSTATUS ExitStatus)
567 {
568 PETHREAD Thread;
569 NTSTATUS Status;
570
571 PAGED_CODE();
572
573 /* Handle the special NULL case */
574 if (!ThreadHandle)
575 {
576 /* Check if we're the only thread left */
577 if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
578 {
579 /* This is invalid */
580 DPRINT1("Can't terminate self\n");
581 return STATUS_CANT_TERMINATE_SELF;
582 }
583 else
584 {
585 /* Use current handle */
586 ThreadHandle = NtCurrentThread();
587 }
588 }
589
590 /* Get the Thread Object */
591 Status = ObReferenceObjectByHandle(ThreadHandle,
592 THREAD_TERMINATE,
593 PsThreadType,
594 KeGetPreviousMode(),
595 (PVOID*)&Thread,
596 NULL);
597 if (!NT_SUCCESS(Status)) {
598
599 DPRINT1("Could not reference thread object\n");
600 return(Status);
601 }
602
603 /* Make sure this is not a system thread */
604 if (PsIsSystemThread(Thread)) {
605
606 DPRINT1("Trying to Terminate a system thread!\n");
607 ObDereferenceObject(Thread);
608 return STATUS_INVALID_PARAMETER;
609 }
610
611 /* Check to see if we're running in the same thread */
612 if (Thread != PsGetCurrentThread()) {
613
614 /* we need to lock the process to make sure it's not already terminating */
615 PsLockProcess(Thread->ThreadsProcess, FALSE);
616
617 /* This isn't our thread, terminate it if not already done */
618 if (!Thread->Terminated) {
619
620 Thread->Terminated = TRUE;
621
622 /* Terminate it */
623 PspTerminateThreadByPointer(Thread, ExitStatus);
624 }
625
626 PsUnlockProcess(Thread->ThreadsProcess);
627
628 /* Dereference the Thread and return */
629 ObDereferenceObject(Thread);
630
631 } else {
632
633 Thread->Terminated = TRUE;
634
635 /* it's safe to dereference thread, there's at least the keep-alive
636 reference which will be removed by the thread reaper causing the
637 thread to be finally destroyed */
638 ObDereferenceObject(Thread);
639
640 /* Terminate him, he's ours */
641 PspExitThread(ExitStatus);
642
643 /* We do never reach this point */
644 KEBUGCHECK(0);
645 }
646
647 return(STATUS_SUCCESS);
648 }
649
650 /*
651 * @implemented
652 */
653 NTSTATUS
654 STDCALL
655 PsTerminateSystemThread(NTSTATUS ExitStatus)
656 {
657 PETHREAD Thread = PsGetCurrentThread();
658
659 /* Make sure this is a system thread */
660 if (!PsIsSystemThread(Thread)) {
661
662 DPRINT1("Trying to Terminate a non-system thread!\n");
663 return STATUS_INVALID_PARAMETER;
664 }
665
666 /* Terminate it for real */
667 PspExitThread(ExitStatus);
668
669 /* we should never reach this point! */
670 KEBUGCHECK(0);
671
672 return(STATUS_SUCCESS);
673 }
674
675 NTSTATUS
676 STDCALL
677 NtRegisterThreadTerminatePort(HANDLE PortHandle)
678 {
679 NTSTATUS Status;
680 PTERMINATION_PORT TerminationPort;
681 PVOID TerminationLpcPort;
682 PETHREAD Thread;
683
684 PAGED_CODE();
685
686 /* Get the Port */
687 Status = ObReferenceObjectByHandle(PortHandle,
688 PORT_ALL_ACCESS,
689 LpcPortObjectType,
690 KeGetPreviousMode(),
691 &TerminationLpcPort,
692 NULL);
693 if (!NT_SUCCESS(Status)) {
694
695 DPRINT1("Failed to reference Port\n");
696 return(Status);
697 }
698
699 /* Allocate the Port and make sure it suceeded */
700 if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
701 sizeof(PTERMINATION_PORT),
702 TAG('P', 's', 'T', '=')))) {
703
704 /* Associate the Port */
705 Thread = PsGetCurrentThread();
706 TerminationPort->Port = TerminationLpcPort;
707 DPRINT("TerminationPort: %p\n", TerminationPort);
708 TerminationPort->Next = Thread->TerminationPort;
709 Thread->TerminationPort = TerminationPort;
710 DPRINT("TerminationPort: %p\n", Thread->TerminationPort);
711
712 /* Return success */
713 return(STATUS_SUCCESS);
714
715 } else {
716
717 /* Dereference and Fail */
718 ObDereferenceObject(TerminationPort);
719 return(STATUS_INSUFFICIENT_RESOURCES);
720 }
721 }