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