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