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