Partial patch of larger rosrtl removal patch. This one merely is a structure fix...
[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 /* Acquire the Active Process Lock */
88 ExAcquireFastMutex(&PspActiveProcessMutex);
89
90 /* Loop all processes on the list */
91 current_entry = PsActiveProcessHead.Flink;
92 while (current_entry != &PsActiveProcessHead)
93 {
94 current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
95 current_entry = current_entry->Flink;
96
97 if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId &&
98 current->UniqueProcessId != PsGetCurrentProcessId())
99 {
100 /* Terminate all the Threads in this Process */
101 PspTerminateProcessThreads(current, STATUS_SUCCESS);
102 }
103 }
104
105 /* Release the lock */
106 ExReleaseFastMutex(&PspActiveProcessMutex);
107 }
108
109 VOID
110 STDCALL
111 PspTerminateProcessThreads(PEPROCESS Process,
112 NTSTATUS ExitStatus)
113 {
114 PLIST_ENTRY CurrentEntry;
115 PETHREAD Thread, CurrentThread = PsGetCurrentThread();
116
117 CurrentEntry = Process->ThreadListHead.Flink;
118 while (CurrentEntry != &Process->ThreadListHead) {
119
120 /* Get the Current Thread */
121 Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
122
123 /* Move to the Next Thread */
124 CurrentEntry = CurrentEntry->Flink;
125
126 /* Make sure it's not the one we're in */
127 if (Thread != CurrentThread) {
128
129 /* Make sure it didn't already terminate */
130 if (!Thread->Terminated) {
131
132 Thread->Terminated = TRUE;
133
134 /* Terminate it by APC */
135 PspTerminateThreadByPointer(Thread, ExitStatus);
136 }
137 }
138 }
139 }
140
141 VOID
142 STDCALL
143 PspDeleteProcess(PVOID ObjectBody)
144 {
145 PEPROCESS Process = (PEPROCESS)ObjectBody;
146
147 DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
148
149 /* Remove it from the Active List */
150 ExAcquireFastMutex(&PspActiveProcessMutex);
151 RemoveEntryList(&Process->ActiveProcessLinks);
152 ExReleaseFastMutex(&PspActiveProcessMutex);
153
154 /* Delete the CID Handle */
155 if(Process->UniqueProcessId != NULL) {
156
157 PsDeleteCidHandle(Process->UniqueProcessId, PsProcessType);
158 }
159
160 /* KDB hook */
161 KDB_DELETEPROCESS_HOOK(Process);
162
163 /* Dereference the Token */
164 SeDeassignPrimaryToken(Process);
165
166 /* Release Memory Information */
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 Kernel Stack */
195 MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, FALSE);
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 while ((TerminationPort = CurrentThread->TerminationPort)) {
270
271 DPRINT("TerminationPort: %p\n", TerminationPort);
272
273 /* Get the next one */
274 CurrentThread->TerminationPort = TerminationPort->Next;
275
276 /* Send the LPC Message */
277 LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
278
279 /* Free the Port */
280 ExFreePool(TerminationPort);
281 }
282
283 /* Rundown Win32 Structures */
284 PsTerminateWin32Thread(CurrentThread);
285 if (Last) PsTerminateWin32Process(CurrentProcess);
286
287 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
288 //CmNotifyRunDown(CurrentThread);
289
290 /* Free the TEB */
291 if((Teb = CurrentThread->Tcb.Teb))
292 {
293 /* Clean up the stack first, if requested */
294 if (Teb->FreeStackOnTermination)
295 {
296 ULONG Dummy = 0;
297 ZwFreeVirtualMemory(NtCurrentProcess(),
298 &Teb->DeallocationStack,
299 &Dummy,
300 MEM_RELEASE);
301 }
302
303 DPRINT("Decommit teb at %p\n", Teb);
304 MmDeleteTeb(CurrentProcess, Teb);
305 CurrentThread->Tcb.Teb = NULL;
306 }
307
308 /* The last Thread shuts down the Process */
309 if (Last) PspExitProcess(CurrentProcess);
310
311 /* Unlock the Process */
312 PsUnlockProcess(CurrentProcess);
313
314 /* Cancel I/O for the thread. */
315 IoCancelThreadIo(CurrentThread);
316
317 /* Rundown Timers */
318 ExTimerRundown();
319 KeCancelTimer(&CurrentThread->Tcb.Timer);
320
321 /* If the Processor Control Block's NpxThread points to the current thread
322 * unset it.
323 */
324 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
325 NULL,
326 (PKPROCESS)CurrentThread);
327
328 /* Rundown Mutexes */
329 KeRundownThread();
330
331 /* Terminate the Thread from the Scheduler */
332 KeTerminateThread(0);
333 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
334 KEBUGCHECK(0);
335 }
336
337 VOID
338 STDCALL
339 PsExitSpecialApc(PKAPC Apc,
340 PKNORMAL_ROUTINE* NormalRoutine,
341 PVOID* NormalContext,
342 PVOID* SystemArgument1,
343 PVOID* SystemArguemnt2)
344 {
345 NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
346
347 DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x)\n", PsGetCurrentThread(), PsGetCurrentProcess());
348
349 /* Free the APC */
350 ExFreePool(Apc);
351
352 /* Terminate the Thread */
353 PspExitThread(ExitStatus);
354
355 /* we should never reach this point! */
356 KEBUGCHECK(0);
357 }
358
359 VOID
360 STDCALL
361 PspExitNormalApc(PVOID NormalContext,
362 PVOID SystemArgument1,
363 PVOID SystemArgument2)
364 {
365 /* Not fully supported yet... must work out some issues that
366 * I don't understand yet -- Alex
367 */
368 DPRINT1("APC2\n");
369 PspExitThread((NTSTATUS)NormalContext);
370
371 /* we should never reach this point! */
372 KEBUGCHECK(0);
373 }
374
375 /*
376 * See "Windows Internals" - Chapter 13, Page 49
377 */
378 VOID
379 STDCALL
380 PspTerminateThreadByPointer(PETHREAD Thread,
381 NTSTATUS ExitStatus)
382 {
383 PKAPC Apc;
384
385 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
386 Thread, ExitStatus);
387
388 /* Check if we are already in the right context */
389 if (PsGetCurrentThread() == Thread) {
390
391 /* Directly terminate the thread */
392 PspExitThread(ExitStatus);
393
394 /* we should never reach this point! */
395 KEBUGCHECK(0);
396 }
397
398 /* Allocate the APC */
399 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
400
401 /* Initialize a Kernel Mode APC to Kill the Thread */
402 KeInitializeApc(Apc,
403 &Thread->Tcb,
404 OriginalApcEnvironment,
405 PsExitSpecialApc,
406 NULL,
407 PspExitNormalApc,
408 KernelMode,
409 (PVOID)ExitStatus);
410
411 /* Insert it into the APC Queue */
412 KeInsertQueueApc(Apc,
413 Apc,
414 NULL,
415 2);
416
417 /* Forcefully resume the thread */
418 KeForceResumeThread(&Thread->Tcb);
419 }
420
421 NTSTATUS
422 STDCALL
423 PspExitProcess(PEPROCESS Process)
424 {
425 DPRINT("PspExitProcess 0x%x\n", Process);
426
427 PspRunCreateProcessNotifyRoutines(Process, FALSE);
428
429 /* close all handles associated with our process, this needs to be done
430 when the last thread still runs */
431 ObKillProcess(Process);
432
433 KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
434
435 return(STATUS_SUCCESS);
436 }
437
438 NTSTATUS
439 STDCALL
440 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
441 IN NTSTATUS ExitStatus)
442 {
443 NTSTATUS Status;
444 PEPROCESS Process;
445 PETHREAD CurrentThread;
446 BOOLEAN KillByHandle;
447
448 PAGED_CODE();
449
450 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
451 ProcessHandle, ExitStatus);
452
453 KillByHandle = (ProcessHandle != NULL);
454
455 /* Get the Process Object */
456 Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
457 PROCESS_TERMINATE,
458 PsProcessType,
459 KeGetPreviousMode(),
460 (PVOID*)&Process,
461 NULL);
462 if (!NT_SUCCESS(Status)) {
463
464 DPRINT1("Invalid handle to Process\n");
465 return(Status);
466 }
467
468 CurrentThread = PsGetCurrentThread();
469
470 PsLockProcess(Process, FALSE);
471
472 if(Process->ExitTime.QuadPart != 0)
473 {
474 PsUnlockProcess(Process);
475 ObDereferenceObject(Process);
476 return STATUS_PROCESS_IS_TERMINATING;
477 }
478
479 /* Terminate all the Process's Threads */
480 PspTerminateProcessThreads(Process, ExitStatus);
481
482 /* only kill the calling thread if it either passed a process handle or
483 NtCurrentProcess() */
484 if (KillByHandle) {
485
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
488 to kill us */
489 KeQuerySystemTime(&Process->ExitTime);
490
491 /* Only master thread remains... kill it off */
492 if (CurrentThread->ThreadsProcess == Process) {
493
494 /* mark our thread as terminating so attempts to terminate it, when
495 unlocking the process, fail */
496 CurrentThread->Terminated = TRUE;
497
498 PsUnlockProcess(Process);
499
500 /* we can safely dereference the process because the current thread
501 holds a reference to it until it gets reaped */
502 ObDereferenceObject(Process);
503
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 */
507
508 PspExitThread(ExitStatus);
509
510 /* we should never reach this point! */
511 KEBUGCHECK(0);
512 }
513 }
514
515 /* unlock and dereference the process so the threads can kill themselves */
516 PsUnlockProcess(Process);
517 ObDereferenceObject(Process);
518
519 return(STATUS_SUCCESS);
520 }
521
522 NTSTATUS
523 STDCALL
524 NtTerminateThread(IN HANDLE ThreadHandle,
525 IN NTSTATUS ExitStatus)
526 {
527 PETHREAD Thread;
528 NTSTATUS Status;
529
530 PAGED_CODE();
531
532 /* Handle the special NULL case */
533 if (!ThreadHandle)
534 {
535 /* Check if we're the only thread left */
536 if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
537 {
538 /* This is invalid */
539 DPRINT1("Can't terminate self\n");
540 return STATUS_CANT_TERMINATE_SELF;
541 }
542 else
543 {
544 /* Use current handle */
545 ThreadHandle = NtCurrentThread();
546 }
547 }
548
549 /* Get the Thread Object */
550 Status = ObReferenceObjectByHandle(ThreadHandle,
551 THREAD_TERMINATE,
552 PsThreadType,
553 KeGetPreviousMode(),
554 (PVOID*)&Thread,
555 NULL);
556 if (!NT_SUCCESS(Status)) {
557
558 DPRINT1("Could not reference thread object\n");
559 return(Status);
560 }
561
562 /* Make sure this is not a system thread */
563 if (PsIsSystemThread(Thread)) {
564
565 DPRINT1("Trying to Terminate a system thread!\n");
566 ObDereferenceObject(Thread);
567 return STATUS_INVALID_PARAMETER;
568 }
569
570 /* Check to see if we're running in the same thread */
571 if (Thread != PsGetCurrentThread()) {
572
573 /* we need to lock the process to make sure it's not already terminating */
574 PsLockProcess(Thread->ThreadsProcess, FALSE);
575
576 /* This isn't our thread, terminate it if not already done */
577 if (!Thread->Terminated) {
578
579 Thread->Terminated = TRUE;
580
581 /* Terminate it */
582 PspTerminateThreadByPointer(Thread, ExitStatus);
583 }
584
585 PsUnlockProcess(Thread->ThreadsProcess);
586
587 /* Dereference the Thread and return */
588 ObDereferenceObject(Thread);
589
590 } else {
591
592 Thread->Terminated = TRUE;
593
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);
598
599 /* Terminate him, he's ours */
600 PspExitThread(ExitStatus);
601
602 /* We do never reach this point */
603 KEBUGCHECK(0);
604 }
605
606 return(STATUS_SUCCESS);
607 }
608
609 /*
610 * @implemented
611 */
612 NTSTATUS
613 STDCALL
614 PsTerminateSystemThread(NTSTATUS ExitStatus)
615 {
616 PETHREAD Thread = PsGetCurrentThread();
617
618 /* Make sure this is a system thread */
619 if (!PsIsSystemThread(Thread)) {
620
621 DPRINT1("Trying to Terminate a non-system thread!\n");
622 return STATUS_INVALID_PARAMETER;
623 }
624
625 /* Terminate it for real */
626 PspExitThread(ExitStatus);
627
628 /* we should never reach this point! */
629 KEBUGCHECK(0);
630
631 return(STATUS_SUCCESS);
632 }
633
634 NTSTATUS
635 STDCALL
636 NtRegisterThreadTerminatePort(HANDLE PortHandle)
637 {
638 NTSTATUS Status;
639 PTERMINATION_PORT TerminationPort;
640 PVOID TerminationLpcPort;
641 PETHREAD Thread;
642
643 PAGED_CODE();
644
645 /* Get the Port */
646 Status = ObReferenceObjectByHandle(PortHandle,
647 PORT_ALL_ACCESS,
648 LpcPortObjectType,
649 KeGetPreviousMode(),
650 &TerminationLpcPort,
651 NULL);
652 if (!NT_SUCCESS(Status)) {
653
654 DPRINT1("Failed to reference Port\n");
655 return(Status);
656 }
657
658 /* Allocate the Port and make sure it suceeded */
659 if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
660 sizeof(PTERMINATION_PORT),
661 TAG('P', 's', 'T', '=')))) {
662
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);
670
671 /* Return success */
672 return(STATUS_SUCCESS);
673
674 } else {
675
676 /* Dereference and Fail */
677 ObDereferenceObject(TerminationPort);
678 return(STATUS_INSUFFICIENT_RESOURCES);
679 }
680 }