Forgot to add the cmake changes
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/kill.c
5 * PURPOSE: Process Manager: Process and Thread Termination
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (xnavara@reactos.org)
8 * Thomas Weidenmueller (w3seek@reactos.org
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 LIST_ENTRY PspReaperListHead = { NULL, NULL };
20 WORK_QUEUE_ITEM PspReaperWorkItem;
21 LARGE_INTEGER ShortTime = {{-10 * 100 * 1000, -1}};
22
23 /* PRIVATE FUNCTIONS *********************************************************/
24
25 VOID
26 NTAPI
27 PspCatchCriticalBreak(IN PCHAR Message,
28 IN PVOID ProcessOrThread,
29 IN PCHAR ImageName)
30 {
31 CHAR Action[2];
32 BOOLEAN Handled = FALSE;
33 PAGED_CODE();
34
35 /* Check if a debugger is enabled */
36 if (KdDebuggerEnabled)
37 {
38 /* Print out the message */
39 DbgPrint(Message, ProcessOrThread, ImageName);
40 do
41 {
42 /* If a debugger isn't present, don't prompt */
43 if (KdDebuggerNotPresent) break;
44
45 /* A debuger is active, prompt for action */
46 DbgPrompt("Break, or Ignore (bi)?", Action, sizeof(Action));
47 switch (Action[0])
48 {
49 /* Break */
50 case 'B': case 'b':
51
52 /* Do a breakpoint */
53 DbgBreakPoint();
54
55 /* Ignore */
56 case 'I': case 'i':
57
58 /* Handle it */
59 Handled = TRUE;
60
61 /* Unrecognized */
62 default:
63 break;
64 }
65 } while (!Handled);
66 }
67
68 /* Did we ultimately handle this? */
69 if (!Handled)
70 {
71 /* We didn't, bugcheck */
72 KeBugCheckEx(CRITICAL_OBJECT_TERMINATION,
73 ((PKPROCESS)ProcessOrThread)->Header.Type,
74 (ULONG_PTR)ProcessOrThread,
75 (ULONG_PTR)ImageName,
76 (ULONG_PTR)Message);
77 }
78 }
79
80 NTSTATUS
81 NTAPI
82 PspTerminateProcess(IN PEPROCESS Process,
83 IN NTSTATUS ExitStatus)
84 {
85 PETHREAD Thread;
86 NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE;
87 PAGED_CODE();
88 PSTRACE(PS_KILL_DEBUG,
89 "Process: %p ExitStatus: %d\n", Process, ExitStatus);
90 PSREFTRACE(Process);
91
92 /* Check if this is a Critical Process */
93 if (Process->BreakOnTermination)
94 {
95 /* Break to debugger */
96 PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
97 Process,
98 Process->ImageFileName);
99 }
100
101 /* Set the delete flag */
102 InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT);
103
104 /* Get the first thread */
105 Thread = PsGetNextProcessThread(Process, NULL);
106 while (Thread)
107 {
108 /* Kill it */
109 PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
110 Thread = PsGetNextProcessThread(Process, Thread);
111
112 /* We had at least one thread, so termination is OK */
113 Status = STATUS_SUCCESS;
114 }
115
116 /* Check if there was nothing to terminate or if we have a debug port */
117 if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort))
118 {
119 /* Clear the handle table anyway */
120 ObClearProcessHandleTable(Process);
121 }
122
123 /* Return status */
124 return Status;
125 }
126
127 NTSTATUS
128 NTAPI
129 PsTerminateProcess(IN PEPROCESS Process,
130 IN NTSTATUS ExitStatus)
131 {
132 /* Call the internal API */
133 return PspTerminateProcess(Process, ExitStatus);
134 }
135
136 VOID
137 NTAPI
138 PspShutdownProcessManager(VOID)
139 {
140 PEPROCESS Process = NULL;
141
142 /* Loop every process */
143 Process = PsGetNextProcess(Process);
144 while (Process)
145 {
146 /* Make sure this isn't the idle or initial process */
147 if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
148 {
149 /* Kill it */
150 PspTerminateProcess(Process, STATUS_SYSTEM_SHUTDOWN);
151 }
152
153 /* Get the next process */
154 Process = PsGetNextProcess(Process);
155 }
156 }
157
158 VOID
159 NTAPI
160 PspExitApcRundown(IN PKAPC Apc)
161 {
162 PAGED_CODE();
163
164 /* Free the APC */
165 ExFreePool(Apc);
166 }
167
168 VOID
169 NTAPI
170 PspReapRoutine(IN PVOID Context)
171 {
172 PSINGLE_LIST_ENTRY NextEntry;
173 PETHREAD Thread;
174 PSTRACE(PS_KILL_DEBUG, "Context: %p\n", Context);
175
176 /* Start main loop */
177 do
178 {
179 /* Write magic value and return the next entry to process */
180 NextEntry = InterlockedExchangePointer((PVOID*)&PspReaperListHead.Flink,
181 (PVOID)1);
182 ASSERT((NextEntry != NULL) && (NextEntry != (PVOID)1));
183
184 /* Start inner loop */
185 do
186 {
187 /* Get the first Thread Entry */
188 Thread = CONTAINING_RECORD(NextEntry, ETHREAD, ReaperLink);
189
190 /* Delete this entry's kernel stack */
191 MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
192 Thread->Tcb.LargeStack);
193 Thread->Tcb.InitialStack = NULL;
194
195 /* Move to the next entry */
196 NextEntry = NextEntry->Next;
197
198 /* Dereference this thread */
199 ObDereferenceObject(Thread);
200 } while ((NextEntry != NULL) && (NextEntry != (PVOID)1));
201
202 /* Remove magic value, keep looping if it got changed */
203 } while (InterlockedCompareExchangePointer((PVOID*)&PspReaperListHead.Flink,
204 0,
205 (PVOID)1) != (PVOID)1);
206 }
207
208 #if DBG
209 VOID
210 NTAPI
211 PspCheckProcessList(VOID)
212 {
213 PLIST_ENTRY Entry;
214
215 KeAcquireGuardedMutex(&PspActiveProcessMutex);
216 DbgPrint("# checking PsActiveProcessHead @ %p\n", &PsActiveProcessHead);
217 for (Entry = PsActiveProcessHead.Flink;
218 Entry != &PsActiveProcessHead;
219 Entry = Entry->Flink)
220 {
221 PEPROCESS Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
222 POBJECT_HEADER Header;
223 PVOID Info, HeaderLocation;
224
225 /* Get the header and assume this is what we'll free */
226 Header = OBJECT_TO_OBJECT_HEADER(Process);
227 HeaderLocation = Header;
228
229 /* To find the header, walk backwards from how we allocated */
230 if ((Info = OBJECT_HEADER_TO_CREATOR_INFO(Header)))
231 {
232 HeaderLocation = Info;
233 }
234 if ((Info = OBJECT_HEADER_TO_NAME_INFO(Header)))
235 {
236 HeaderLocation = Info;
237 }
238 if ((Info = OBJECT_HEADER_TO_HANDLE_INFO(Header)))
239 {
240 HeaderLocation = Info;
241 }
242 if ((Info = OBJECT_HEADER_TO_QUOTA_INFO(Header)))
243 {
244 HeaderLocation = Info;
245 }
246
247 ExpCheckPoolAllocation(HeaderLocation, NonPagedPool, 'corP');
248 }
249
250 KeReleaseGuardedMutex(&PspActiveProcessMutex);
251 }
252 #endif
253
254 VOID
255 NTAPI
256 PspDeleteProcess(IN PVOID ObjectBody)
257 {
258 PEPROCESS Process = (PEPROCESS)ObjectBody;
259 KAPC_STATE ApcState;
260 PAGED_CODE();
261 PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
262 PSREFTRACE(Process);
263
264 /* Check if it has an Active Process Link */
265 if (Process->ActiveProcessLinks.Flink)
266 {
267 /* Remove it from the Active List */
268 KeAcquireGuardedMutex(&PspActiveProcessMutex);
269 RemoveEntryList(&Process->ActiveProcessLinks);
270 Process->ActiveProcessLinks.Flink = NULL;
271 Process->ActiveProcessLinks.Blink = NULL;
272 KeReleaseGuardedMutex(&PspActiveProcessMutex);
273 }
274
275 /* Check for Auditing information */
276 if (Process->SeAuditProcessCreationInfo.ImageFileName)
277 {
278 /* Free it */
279 ExFreePoolWithTag(Process->SeAuditProcessCreationInfo.ImageFileName,
280 TAG_SEPA);
281 Process->SeAuditProcessCreationInfo.ImageFileName = NULL;
282 }
283
284 /* Check if we have a job */
285 if (Process->Job)
286 {
287 /* Remove the process from the job */
288 PspRemoveProcessFromJob(Process, Process->Job);
289
290 /* Dereference it */
291 ObDereferenceObject(Process->Job);
292 Process->Job = NULL;
293 }
294
295 /* Increase the stack count */
296 Process->Pcb.StackCount++;
297
298 /* Check if we have a debug port */
299 if (Process->DebugPort)
300 {
301 /* Deference the Debug Port */
302 ObDereferenceObject(Process->DebugPort);
303 Process->DebugPort = NULL;
304 }
305
306 /* Check if we have an exception port */
307 if (Process->ExceptionPort)
308 {
309 /* Deference the Exception Port */
310 ObDereferenceObject(Process->ExceptionPort);
311 Process->ExceptionPort = NULL;
312 }
313
314 /* Check if we have a section object */
315 if (Process->SectionObject)
316 {
317 /* Deference the Section Object */
318 ObDereferenceObject(Process->SectionObject);
319 Process->SectionObject = NULL;
320 }
321
322 #if defined(_X86_)
323 /* Clean Ldt and Vdm objects */
324 PspDeleteLdt(Process);
325 PspDeleteVdmObjects(Process);
326 #endif
327
328 /* Delete the Object Table */
329 if (Process->ObjectTable)
330 {
331 /* Attach to the process */
332 KeStackAttachProcess(&Process->Pcb, &ApcState);
333
334 /* Kill the Object Info */
335 ObKillProcess(Process);
336
337 /* Detach */
338 KeUnstackDetachProcess(&ApcState);
339 }
340
341 /* Check if we have an address space, and clean it */
342 if (Process->HasAddressSpace)
343 {
344 /* Attach to the process */
345 KeStackAttachProcess(&Process->Pcb, &ApcState);
346
347 /* Clean the Address Space */
348 PspExitProcess(FALSE, Process);
349
350 /* Detach */
351 KeUnstackDetachProcess(&ApcState);
352
353 /* Completely delete the Address Space */
354 MmDeleteProcessAddressSpace(Process);
355 }
356
357 /* See if we have a PID */
358 if (Process->UniqueProcessId)
359 {
360 /* Delete the PID */
361 if (!(ExDestroyHandle(PspCidTable, Process->UniqueProcessId, NULL)))
362 {
363 /* Something wrong happened, bugcheck */
364 KeBugCheck(CID_HANDLE_DELETION);
365 }
366 }
367
368 /* Cleanup security information */
369 PspDeleteProcessSecurity(Process);
370
371 /* Check if we have kept information on the Working Set */
372 if (Process->WorkingSetWatch)
373 {
374 /* Free it */
375 ExFreePool(Process->WorkingSetWatch);
376
377 /* And return the quota it was taking up */
378 PsReturnProcessNonPagedPoolQuota(Process, 0x2000);
379 }
380
381 /* Dereference the Device Map */
382 ObDereferenceDeviceMap(Process);
383
384 /* Destroy the Quota Block */
385 PspDestroyQuotaBlock(Process);
386 }
387
388 VOID
389 NTAPI
390 PspDeleteThread(IN PVOID ObjectBody)
391 {
392 PETHREAD Thread = (PETHREAD)ObjectBody;
393 PEPROCESS Process = Thread->ThreadsProcess;
394 PAGED_CODE();
395 PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
396 PSREFTRACE(Thread);
397 ASSERT(Thread->Tcb.Win32Thread == NULL);
398
399 /* Check if we have a stack */
400 if (Thread->Tcb.InitialStack)
401 {
402 /* Release it */
403 MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
404 Thread->Tcb.LargeStack);
405 }
406
407 /* Check if we have a CID Handle */
408 if (Thread->Cid.UniqueThread)
409 {
410 /* Delete the CID Handle */
411 if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL)))
412 {
413 /* Something wrong happened, bugcheck */
414 KeBugCheck(CID_HANDLE_DELETION);
415 }
416 }
417
418 /* Cleanup impersionation information */
419 PspDeleteThreadSecurity(Thread);
420
421 /* Make sure the thread was inserted, before continuing */
422 if (!Process) return;
423
424 /* Check if the thread list is valid */
425 if (Thread->ThreadListEntry.Flink)
426 {
427 /* Lock the thread's process */
428 KeEnterCriticalRegion();
429 ExAcquirePushLockExclusive(&Process->ProcessLock);
430
431 /* Remove us from the list */
432 RemoveEntryList(&Thread->ThreadListEntry);
433
434 /* Release the lock */
435 ExReleasePushLockExclusive(&Process->ProcessLock);
436 KeLeaveCriticalRegion();
437 }
438
439 /* Dereference the Process */
440 ObDereferenceObject(Process);
441 }
442
443 /*
444 * FUNCTION: Terminates the current thread
445 * See "Windows Internals" - Chapter 13, Page 50-53
446 */
447 VOID
448 NTAPI
449 PspExitThread(IN NTSTATUS ExitStatus)
450 {
451 CLIENT_DIED_MSG TerminationMsg;
452 NTSTATUS Status;
453 PTEB Teb;
454 PEPROCESS CurrentProcess;
455 PETHREAD Thread, OtherThread, PreviousThread = NULL;
456 PVOID DeallocationStack;
457 SIZE_T Dummy;
458 BOOLEAN Last = FALSE;
459 PTERMINATION_PORT TerminationPort, NextPort;
460 PLIST_ENTRY FirstEntry, CurrentEntry;
461 PKAPC Apc;
462 PTOKEN PrimaryToken;
463 PAGED_CODE();
464 PSTRACE(PS_KILL_DEBUG, "ExitStatus: %d\n", ExitStatus);
465
466 /* Get the Current Thread and Process */
467 Thread = PsGetCurrentThread();
468 CurrentProcess = Thread->ThreadsProcess;
469 ASSERT((Thread) == PsGetCurrentThread());
470
471 /* Can't terminate a thread if it attached another process */
472 if (KeIsAttachedProcess())
473 {
474 /* Bugcheck */
475 KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
476 (ULONG_PTR)CurrentProcess,
477 (ULONG_PTR)Thread->Tcb.ApcState.Process,
478 (ULONG_PTR)Thread->Tcb.ApcStateIndex,
479 (ULONG_PTR)Thread);
480 }
481
482 /* Lower to Passive Level */
483 KeLowerIrql(PASSIVE_LEVEL);
484
485 /* Can't be a worker thread */
486 if (Thread->ActiveExWorker)
487 {
488 /* Bugcheck */
489 KeBugCheckEx(ACTIVE_EX_WORKER_THREAD_TERMINATION,
490 (ULONG_PTR)Thread,
491 0,
492 0,
493 0);
494 }
495
496 /* Can't have pending APCs */
497 if (Thread->Tcb.CombinedApcDisable != 0)
498 {
499 /* Bugcheck */
500 KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
501 0,
502 Thread->Tcb.CombinedApcDisable,
503 0,
504 1);
505 }
506
507 /* Lock the thread */
508 ExWaitForRundownProtectionRelease(&Thread->RundownProtect);
509
510 /* Cleanup the power state */
511 PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState);
512
513 /* Call the WMI Callback for Threads */
514 //WmiTraceThread(Thread, NULL, FALSE);
515
516 /* Run Thread Notify Routines before we desintegrate the thread */
517 PspRunCreateThreadNotifyRoutines(Thread, FALSE);
518
519 /* Lock the Process before we modify its thread entries */
520 KeEnterCriticalRegion();
521 ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
522
523 /* Decrease the active thread count, and check if it's 0 */
524 if (!(--CurrentProcess->ActiveThreads))
525 {
526 /* Set the delete flag */
527 InterlockedOr((PLONG)&CurrentProcess->Flags, PSF_PROCESS_DELETE_BIT);
528
529 /* Remember we are last */
530 Last = TRUE;
531
532 /* Check if this termination is due to the thread dying */
533 if (ExitStatus == STATUS_THREAD_IS_TERMINATING)
534 {
535 /* Check if the last thread was pending */
536 if (CurrentProcess->ExitStatus == STATUS_PENDING)
537 {
538 /* Use the last exit status */
539 CurrentProcess->ExitStatus = CurrentProcess->
540 LastThreadExitStatus;
541 }
542 }
543 else
544 {
545 /* Just a normal exit, write the code */
546 CurrentProcess->ExitStatus = ExitStatus;
547 }
548
549 /* Loop all the current threads */
550 FirstEntry = &CurrentProcess->ThreadListHead;
551 CurrentEntry = FirstEntry->Flink;
552 while (FirstEntry != CurrentEntry)
553 {
554 /* Get the thread on the list */
555 OtherThread = CONTAINING_RECORD(CurrentEntry,
556 ETHREAD,
557 ThreadListEntry);
558
559 /* Check if it's a thread that's still alive */
560 if ((OtherThread != Thread) &&
561 !(KeReadStateThread(&OtherThread->Tcb)) &&
562 (ObReferenceObjectSafe(OtherThread)))
563 {
564 /* It's a live thread and we referenced it, unlock process */
565 ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
566 KeLeaveCriticalRegion();
567
568 /* Wait on the thread */
569 KeWaitForSingleObject(OtherThread,
570 Executive,
571 KernelMode,
572 FALSE,
573 NULL);
574
575 /* Check if we had a previous thread to dereference */
576 if (PreviousThread) ObDereferenceObject(PreviousThread);
577
578 /* Remember the thread and re-lock the process */
579 PreviousThread = OtherThread;
580 KeEnterCriticalRegion();
581 ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
582 }
583
584 /* Go to the next thread */
585 CurrentEntry = CurrentEntry->Flink;
586 }
587 }
588 else if (ExitStatus != STATUS_THREAD_IS_TERMINATING)
589 {
590 /* Write down the exit status of the last thread to get killed */
591 CurrentProcess->LastThreadExitStatus = ExitStatus;
592 }
593
594 /* Unlock the Process */
595 ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
596 KeLeaveCriticalRegion();
597
598 /* Check if we had a previous thread to dereference */
599 if (PreviousThread) ObDereferenceObject(PreviousThread);
600
601 /* Check if the process has a debug port and if this is a user thread */
602 if ((CurrentProcess->DebugPort) && !(Thread->SystemThread))
603 {
604 /* Notify the Debug API. */
605 Last ? DbgkExitProcess(CurrentProcess->ExitStatus) :
606 DbgkExitThread(ExitStatus);
607 }
608
609 /* Check if this is a Critical Thread */
610 if ((KdDebuggerEnabled) && (Thread->BreakOnTermination))
611 {
612 /* Break to debugger */
613 PspCatchCriticalBreak("Critical thread 0x%p (in %s) exited\n",
614 Thread,
615 CurrentProcess->ImageFileName);
616 }
617
618 /* Check if it's the last thread and this is a Critical Process */
619 if ((Last) && (CurrentProcess->BreakOnTermination))
620 {
621 /* Check if a debugger is here to handle this */
622 if (KdDebuggerEnabled)
623 {
624 /* Break to debugger */
625 PspCatchCriticalBreak("Critical process 0x%p (in %s) exited\n",
626 CurrentProcess,
627 CurrentProcess->ImageFileName);
628 }
629 else
630 {
631 /* Bugcheck, we can't allow this */
632 KeBugCheckEx(CRITICAL_PROCESS_DIED,
633 (ULONG_PTR)CurrentProcess,
634 0,
635 0,
636 0);
637 }
638 }
639
640 /* Sanity check */
641 ASSERT(Thread->Tcb.CombinedApcDisable == 0);
642
643 /* Process the Termination Ports */
644 TerminationPort = Thread->TerminationPort;
645 if (TerminationPort)
646 {
647 /* Setup the message header */
648 TerminationMsg.h.u2.ZeroInit = 0;
649 TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
650 TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
651 TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
652 sizeof(PORT_MESSAGE);
653
654 /* Loop each port */
655 do
656 {
657 /* Save the Create Time */
658 TerminationMsg.CreateTime = Thread->CreateTime;
659
660 /* Loop trying to send message */
661 while (TRUE)
662 {
663 /* Send the LPC Message */
664 Status = LpcRequestPort(TerminationPort->Port,
665 &TerminationMsg.h);
666 if ((Status == STATUS_NO_MEMORY) ||
667 (Status == STATUS_INSUFFICIENT_RESOURCES))
668 {
669 /* Wait a bit and try again */
670 KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
671 continue;
672 }
673 break;
674 }
675
676 /* Dereference this LPC Port */
677 ObDereferenceObject(TerminationPort->Port);
678
679 /* Move to the next one */
680 NextPort = TerminationPort->Next;
681
682 /* Free the Termination Port Object */
683 ExFreePoolWithTag(TerminationPort, '=TsP');
684
685 /* Keep looping as long as there is a port */
686 TerminationPort = NextPort;
687 } while (TerminationPort);
688 }
689 else if (((ExitStatus == STATUS_THREAD_IS_TERMINATING) &&
690 (Thread->DeadThread)) ||
691 !(Thread->DeadThread))
692 {
693 /*
694 * This case is special and deserves some extra comments. What
695 * basically happens here is that this thread doesn't have a termination
696 * port, which means that it died before being fully created. Since we
697 * still have to notify an LPC Server, we'll use the exception port,
698 * which we know exists. However, we need to know how far the thread
699 * actually got created. We have three possibilities:
700 *
701 * - NtCreateThread returned an error really early: DeadThread is set.
702 * - NtCreateThread managed to create the thread: DeadThread is off.
703 * - NtCreateThread was creating the thread (with DeadThread set,
704 * but the thread got killed prematurely: STATUS_THREAD_IS_TERMINATING
705 * is our exit code.)
706 *
707 * For the 2 & 3rd scenarios, the thread has been created far enough to
708 * warrant notification to the LPC Server.
709 */
710
711 /* Setup the message header */
712 TerminationMsg.h.u2.ZeroInit = 0;
713 TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
714 TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
715 TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
716 sizeof(PORT_MESSAGE);
717
718 /* Make sure the process has an exception port */
719 if (CurrentProcess->ExceptionPort)
720 {
721 /* Save the Create Time */
722 TerminationMsg.CreateTime = Thread->CreateTime;
723
724 /* Loop trying to send message */
725 while (TRUE)
726 {
727 /* Send the LPC Message */
728 Status = LpcRequestPort(CurrentProcess->ExceptionPort,
729 &TerminationMsg.h);
730 if ((Status == STATUS_NO_MEMORY) ||
731 (Status == STATUS_INSUFFICIENT_RESOURCES))
732 {
733 /* Wait a bit and try again */
734 KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
735 continue;
736 }
737 break;
738 }
739 }
740 }
741
742 /* Rundown Win32 Thread if there is one */
743 if (Thread->Tcb.Win32Thread) PspW32ThreadCallout(Thread,
744 PsW32ThreadCalloutExit);
745
746 /* If we are the last thread and have a W32 Process */
747 if ((Last) && (CurrentProcess->Win32Process))
748 {
749 /* Run it down too */
750 PspW32ProcessCallout(CurrentProcess, FALSE);
751 }
752
753 /* Make sure Stack Swap is enabled */
754 if (!Thread->Tcb.EnableStackSwap)
755 {
756 /* Stack swap really shouldn't be disabled during exit! */
757 KeBugCheckEx(KERNEL_STACK_LOCKED_AT_EXIT, 0, 0, 0, 0);
758 }
759
760 /* Cancel I/O for the thread. */
761 IoCancelThreadIo(Thread);
762
763 /* Rundown Timers */
764 ExTimerRundown();
765
766 /* FIXME: Rundown Registry Notifications (NtChangeNotify)
767 CmNotifyRunDown(Thread); */
768
769 /* Rundown Mutexes */
770 KeRundownThread();
771
772 /* Check if we have a TEB */
773 Teb = Thread->Tcb.Teb;
774 if (Teb)
775 {
776 /* Check if the thread is still alive */
777 if (!Thread->DeadThread)
778 {
779 /* Check if we need to free its stack */
780 if (Teb->FreeStackOnTermination)
781 {
782 /* Set the TEB's Deallocation Stack as the Base Address */
783 Dummy = 0;
784 DeallocationStack = Teb->DeallocationStack;
785
786 /* Free the Thread's Stack */
787 ZwFreeVirtualMemory(NtCurrentProcess(),
788 &DeallocationStack,
789 &Dummy,
790 MEM_RELEASE);
791 }
792
793 /* Free the debug handle */
794 if (Teb->DbgSsReserved[1]) ObCloseHandle(Teb->DbgSsReserved[1],
795 UserMode);
796 }
797
798 /* Decommit the TEB */
799 MmDeleteTeb(CurrentProcess, Teb);
800 Thread->Tcb.Teb = NULL;
801 }
802
803 /* Free LPC Data */
804 LpcExitThread(Thread);
805
806 /* Save the exit status and exit time */
807 Thread->ExitStatus = ExitStatus;
808 KeQuerySystemTime(&Thread->ExitTime);
809
810 /* Sanity check */
811 ASSERT(Thread->Tcb.CombinedApcDisable == 0);
812
813 /* Check if this is the final thread or not */
814 if (Last)
815 {
816 /* Set the process exit time */
817 CurrentProcess->ExitTime = Thread->ExitTime;
818
819 /* Exit the process */
820 PspExitProcess(TRUE, CurrentProcess);
821
822 /* Get the process token and check if we need to audit */
823 PrimaryToken = PsReferencePrimaryToken(CurrentProcess);
824 if (SeDetailedAuditingWithToken(PrimaryToken))
825 {
826 /* Audit the exit */
827 SeAuditProcessExit(CurrentProcess);
828 }
829
830 /* Dereference the process token */
831 ObFastDereferenceObject(&CurrentProcess->Token, PrimaryToken);
832
833 /* Check if this is a VDM Process and rundown the VDM DPCs if so */
834 if (CurrentProcess->VdmObjects) { /* VdmRundownDpcs(CurrentProcess); */ }
835
836 /* Kill the process in the Object Manager */
837 ObKillProcess(CurrentProcess);
838
839 /* Check if we have a section object */
840 if (CurrentProcess->SectionObject)
841 {
842 /* Dereference and clear the Section Object */
843 ObDereferenceObject(CurrentProcess->SectionObject);
844 CurrentProcess->SectionObject = NULL;
845 }
846
847 /* Check if the process is part of a job */
848 if (CurrentProcess->Job)
849 {
850 /* Remove the process from the job */
851 PspExitProcessFromJob(CurrentProcess->Job, CurrentProcess);
852 }
853 }
854
855 /* Disable APCs */
856 KeEnterCriticalRegion();
857
858 /* Disable APC queueing, force a resumption */
859 Thread->Tcb.ApcQueueable = FALSE;
860 KeForceResumeThread(&Thread->Tcb);
861
862 /* Re-enable APCs */
863 KeLeaveCriticalRegion();
864
865 /* Flush the User APCs */
866 FirstEntry = KeFlushQueueApc(&Thread->Tcb, UserMode);
867 if (FirstEntry)
868 {
869 /* Start with the first entry */
870 CurrentEntry = FirstEntry;
871 do
872 {
873 /* Get the APC */
874 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
875
876 /* Move to the next one */
877 CurrentEntry = CurrentEntry->Flink;
878
879 /* Rundown the APC or de-allocate it */
880 if (Apc->RundownRoutine)
881 {
882 /* Call its own routine */
883 Apc->RundownRoutine(Apc);
884 }
885 else
886 {
887 /* Do it ourselves */
888 ExFreePool(Apc);
889 }
890 }
891 while (CurrentEntry != FirstEntry);
892 }
893
894 /* Clean address space if this was the last thread */
895 if (Last) MmCleanProcessAddressSpace(CurrentProcess);
896
897 /* Call the Lego routine */
898 if (Thread->Tcb.LegoData) PspRunLegoRoutine(&Thread->Tcb);
899
900 /* Flush the APC queue, which should be empty */
901 FirstEntry = KeFlushQueueApc(&Thread->Tcb, KernelMode);
902 if ((FirstEntry) || (Thread->Tcb.CombinedApcDisable != 0))
903 {
904 /* Bugcheck time */
905 KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
906 (ULONG_PTR)FirstEntry,
907 Thread->Tcb.CombinedApcDisable,
908 KeGetCurrentIrql(),
909 0);
910 }
911
912 /* Signal the process if this was the last thread */
913 if (Last) KeSetProcess(&CurrentProcess->Pcb, 0, FALSE);
914
915 /* Terminate the Thread from the Scheduler */
916 KeTerminateThread(0);
917 }
918
919 VOID
920 NTAPI
921 PsExitSpecialApc(IN PKAPC Apc,
922 IN OUT PKNORMAL_ROUTINE* NormalRoutine,
923 IN OUT PVOID* NormalContext,
924 IN OUT PVOID* SystemArgument1,
925 IN OUT PVOID* SystemArgument2)
926 {
927 NTSTATUS Status;
928 PAGED_CODE();
929 PSTRACE(PS_KILL_DEBUG,
930 "Apc: %p SystemArgument2: %p \n", Apc, SystemArgument2);
931
932 /* Don't do anything unless we are in User-Mode */
933 if (Apc->SystemArgument2)
934 {
935 /* Free the APC */
936 Status = (NTSTATUS)Apc->NormalContext;
937 PspExitApcRundown(Apc);
938
939 /* Terminate the Thread */
940 PspExitThread(Status);
941 }
942 }
943
944 VOID
945 NTAPI
946 PspExitNormalApc(IN PVOID NormalContext,
947 IN PVOID SystemArgument1,
948 IN PVOID SystemArgument2)
949 {
950 PKAPC Apc = (PKAPC)SystemArgument1;
951 PETHREAD Thread = PsGetCurrentThread();
952 PAGED_CODE();
953 PSTRACE(PS_KILL_DEBUG, "SystemArgument2: %p \n", SystemArgument2);
954
955 /* This should never happen */
956 ASSERT(!(((ULONG_PTR)SystemArgument2) & 1));
957
958 /* If we're here, this is not a System Thread, so kill it from User-Mode */
959 KeInitializeApc(Apc,
960 &Thread->Tcb,
961 OriginalApcEnvironment,
962 PsExitSpecialApc,
963 PspExitApcRundown,
964 PspExitNormalApc,
965 UserMode,
966 NormalContext);
967
968 /* Now insert the APC with the User-Mode Flag */
969 if (!(KeInsertQueueApc(Apc,
970 Apc,
971 (PVOID)((ULONG_PTR)SystemArgument2 | 1),
972 2)))
973 {
974 /* Failed to insert, free the APC */
975 PspExitApcRundown(Apc);
976 }
977
978 /* Set the APC Pending flag */
979 Thread->Tcb.ApcState.UserApcPending = TRUE;
980 }
981
982 /*
983 * See "Windows Internals" - Chapter 13, Page 49
984 */
985 NTSTATUS
986 NTAPI
987 PspTerminateThreadByPointer(IN PETHREAD Thread,
988 IN NTSTATUS ExitStatus,
989 IN BOOLEAN bSelf)
990 {
991 PKAPC Apc;
992 NTSTATUS Status = STATUS_SUCCESS;
993 ULONG Flags;
994 PAGED_CODE();
995 PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %d\n", Thread, ExitStatus);
996 PSREFTRACE(Thread);
997
998 /* Check if this is a Critical Thread, and Bugcheck */
999 if (Thread->BreakOnTermination)
1000 {
1001 /* Break to debugger */
1002 PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n",
1003 Thread,
1004 Thread->ThreadsProcess->ImageFileName);
1005 }
1006
1007 /* Check if we are already inside the thread */
1008 if ((bSelf) || (PsGetCurrentThread() == Thread))
1009 {
1010 /* This should only happen at passive */
1011 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
1012
1013 /* Mark it as terminated */
1014 PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT);
1015
1016 /* Directly terminate the thread */
1017 PspExitThread(ExitStatus);
1018 }
1019
1020 /* This shouldn't be a system thread */
1021 if (Thread->SystemThread) return STATUS_ACCESS_DENIED;
1022
1023 /* Allocate the APC */
1024 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
1025 if (!Apc) return STATUS_INSUFFICIENT_RESOURCES;
1026
1027 /* Set the Terminated Flag */
1028 Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT;
1029
1030 /* Set it, and check if it was already set while we were running */
1031 if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) &
1032 CT_TERMINATED_BIT))
1033 {
1034 /* Initialize a Kernel Mode APC to Kill the Thread */
1035 KeInitializeApc(Apc,
1036 &Thread->Tcb,
1037 OriginalApcEnvironment,
1038 PsExitSpecialApc,
1039 PspExitApcRundown,
1040 PspExitNormalApc,
1041 KernelMode,
1042 (PVOID)ExitStatus);
1043
1044 /* Insert it into the APC Queue */
1045 if (!KeInsertQueueApc(Apc, Apc, NULL, 2))
1046 {
1047 /* The APC was already in the queue, fail */
1048 Status = STATUS_UNSUCCESSFUL;
1049 }
1050 else
1051 {
1052 /* Forcefully resume the thread and return */
1053 KeForceResumeThread(&Thread->Tcb);
1054 return Status;
1055 }
1056 }
1057
1058 /* We failed, free the APC */
1059 ExFreePoolWithTag(Apc, TAG_TERMINATE_APC);
1060
1061 /* Return Status */
1062 return Status;
1063 }
1064
1065 BOOLEAN
1066 NTAPI
1067 PspIsProcessExiting(IN PEPROCESS Process)
1068 {
1069 return Process->Flags & PSF_PROCESS_EXITING_BIT;
1070 }
1071
1072 VOID
1073 NTAPI
1074 PspExitProcess(IN BOOLEAN LastThread,
1075 IN PEPROCESS Process)
1076 {
1077 ULONG Actual;
1078 PAGED_CODE();
1079 PSTRACE(PS_KILL_DEBUG,
1080 "LastThread: %u Process: %p\n", LastThread, Process);
1081 PSREFTRACE(Process);
1082
1083 /* Set Process Exit flag */
1084 InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_EXITING_BIT);
1085
1086 /* Check if we are the last thread */
1087 if (LastThread)
1088 {
1089 /* Notify the WMI Process Callback */
1090 //WmiTraceProcess(Process, FALSE);
1091
1092 /* Run the Notification Routines */
1093 PspRunCreateProcessNotifyRoutines(Process, FALSE);
1094 }
1095
1096 /* Cleanup the power state */
1097 PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState);
1098
1099 /* Clear the security port */
1100 if (!Process->SecurityPort)
1101 {
1102 /* So we don't double-dereference */
1103 Process->SecurityPort = (PVOID)1;
1104 }
1105 else if (Process->SecurityPort != (PVOID)1)
1106 {
1107 /* Dereference it */
1108 ObDereferenceObject(Process->SecurityPort);
1109 Process->SecurityPort = (PVOID)1;
1110 }
1111
1112 /* Check if we are the last thread */
1113 if (LastThread)
1114 {
1115 /* Check if we have to set the Timer Resolution */
1116 if (Process->SetTimerResolution)
1117 {
1118 /* Set it to default */
1119 ZwSetTimerResolution(KeMaximumIncrement, 0, &Actual);
1120 }
1121
1122 /* Check if we are part of a Job that has a completion port */
1123 if ((Process->Job) && (Process->Job->CompletionPort))
1124 {
1125 /* FIXME: Check job status code and do I/O completion if needed */
1126 }
1127
1128 /* FIXME: Notify the Prefetcher */
1129 }
1130 else
1131 {
1132 /* Clear process' address space here */
1133 MmCleanProcessAddressSpace(Process);
1134 }
1135 }
1136
1137 /* PUBLIC FUNCTIONS **********************************************************/
1138
1139 /*
1140 * @implemented
1141 */
1142 NTSTATUS
1143 NTAPI
1144 PsTerminateSystemThread(IN NTSTATUS ExitStatus)
1145 {
1146 PETHREAD Thread = PsGetCurrentThread();
1147
1148 /* Make sure this is a system thread */
1149 if (!Thread->SystemThread) return STATUS_INVALID_PARAMETER;
1150
1151 /* Terminate it for real */
1152 return PspTerminateThreadByPointer(Thread, ExitStatus, TRUE);
1153 }
1154
1155 /*
1156 * @implemented
1157 */
1158 NTSTATUS
1159 NTAPI
1160 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
1161 IN NTSTATUS ExitStatus)
1162 {
1163 NTSTATUS Status;
1164 PEPROCESS Process, CurrentProcess = PsGetCurrentProcess();
1165 PETHREAD Thread, CurrentThread = PsGetCurrentThread();
1166 BOOLEAN KillByHandle;
1167 PAGED_CODE();
1168 PSTRACE(PS_KILL_DEBUG,
1169 "ProcessHandle: %p ExitStatus: %d\n", ProcessHandle, ExitStatus);
1170
1171 /* Were we passed a process handle? */
1172 if (ProcessHandle)
1173 {
1174 /* Yes we were, use it */
1175 KillByHandle = TRUE;
1176 }
1177 else
1178 {
1179 /* We weren't... we assume this is suicide */
1180 KillByHandle = FALSE;
1181 ProcessHandle = NtCurrentProcess();
1182 }
1183
1184 /* Get the Process Object */
1185 Status = ObReferenceObjectByHandle(ProcessHandle,
1186 PROCESS_TERMINATE,
1187 PsProcessType,
1188 KeGetPreviousMode(),
1189 (PVOID*)&Process,
1190 NULL);
1191 if (!NT_SUCCESS(Status)) return(Status);
1192
1193 /* Check if this is a Critical Process, and Bugcheck */
1194 if (Process->BreakOnTermination)
1195 {
1196 /* Break to debugger */
1197 PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
1198 Process,
1199 Process->ImageFileName);
1200 }
1201
1202 /* Lock the Process */
1203 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1204 {
1205 /* Failed to lock, fail */
1206 ObDereferenceObject(Process);
1207 return STATUS_PROCESS_IS_TERMINATING;
1208 }
1209
1210 /* Set the delete flag, unless the process is comitting suicide */
1211 if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT);
1212
1213 /* Get the first thread */
1214 Status = STATUS_NOTHING_TO_TERMINATE;
1215 Thread = PsGetNextProcessThread(Process, NULL);
1216 if (Thread)
1217 {
1218 /* We know we have at least a thread */
1219 Status = STATUS_SUCCESS;
1220
1221 /* Loop and kill the others */
1222 do
1223 {
1224 /* Ensure it's not ours*/
1225 if (Thread != CurrentThread)
1226 {
1227 /* Kill it */
1228 PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
1229 }
1230
1231 /* Move to the next thread */
1232 Thread = PsGetNextProcessThread(Process, Thread);
1233 } while (Thread);
1234 }
1235
1236 /* Unlock the process */
1237 ExReleaseRundownProtection(&Process->RundownProtect);
1238
1239 /* Check if we are killing ourselves */
1240 if (Process == CurrentProcess)
1241 {
1242 /* Also make sure the caller gave us our handle */
1243 if (KillByHandle)
1244 {
1245 /* Dereference the process */
1246 ObDereferenceObject(Process);
1247
1248 /* Terminate ourselves */
1249 PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE);
1250 }
1251 }
1252 else if (ExitStatus == DBG_TERMINATE_PROCESS)
1253 {
1254 /* Disable debugging on this process */
1255 DbgkClearProcessDebugObject(Process, NULL);
1256 }
1257
1258 /* Check if there was nothing to terminate, or if we have a Debug Port */
1259 if ((Status == STATUS_NOTHING_TO_TERMINATE) ||
1260 ((Process->DebugPort) && (KillByHandle)))
1261 {
1262 /* Clear the handle table */
1263 ObClearProcessHandleTable(Process);
1264
1265 /* Return status now */
1266 Status = STATUS_SUCCESS;
1267 }
1268
1269 /* Decrease the reference count we added */
1270 ObDereferenceObject(Process);
1271
1272 /* Return status */
1273 return Status;
1274 }
1275
1276 NTSTATUS
1277 NTAPI
1278 NtTerminateThread(IN HANDLE ThreadHandle,
1279 IN NTSTATUS ExitStatus)
1280 {
1281 PETHREAD Thread;
1282 PETHREAD CurrentThread = PsGetCurrentThread();
1283 NTSTATUS Status;
1284 PAGED_CODE();
1285 PSTRACE(PS_KILL_DEBUG,
1286 "ThreadHandle: %p ExitStatus: %d\n", ThreadHandle, ExitStatus);
1287
1288 /* Handle the special NULL case */
1289 if (!ThreadHandle)
1290 {
1291 /* Check if we're the only thread left */
1292 if (PsGetCurrentProcess()->ActiveThreads == 1)
1293 {
1294 /* This is invalid */
1295 return STATUS_CANT_TERMINATE_SELF;
1296 }
1297
1298 /* Terminate us directly */
1299 goto TerminateSelf;
1300 }
1301 else if (ThreadHandle == NtCurrentThread())
1302 {
1303 TerminateSelf:
1304 /* Terminate this thread */
1305 return PspTerminateThreadByPointer(CurrentThread,
1306 ExitStatus,
1307 TRUE);
1308 }
1309
1310 /* We are terminating another thread, get the Thread Object */
1311 Status = ObReferenceObjectByHandle(ThreadHandle,
1312 THREAD_TERMINATE,
1313 PsThreadType,
1314 KeGetPreviousMode(),
1315 (PVOID*)&Thread,
1316 NULL);
1317 if (!NT_SUCCESS(Status)) return Status;
1318
1319 /* Check to see if we're running in the same thread */
1320 if (Thread != CurrentThread)
1321 {
1322 /* Terminate it */
1323 Status = PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
1324
1325 /* Dereference the Thread and return */
1326 ObDereferenceObject(Thread);
1327 }
1328 else
1329 {
1330 /* Dereference the thread and terminate ourselves */
1331 ObDereferenceObject(Thread);
1332 goto TerminateSelf;
1333 }
1334
1335 /* Return status */
1336 return Status;
1337 }
1338
1339 NTSTATUS
1340 NTAPI
1341 NtRegisterThreadTerminatePort(IN HANDLE PortHandle)
1342 {
1343 NTSTATUS Status;
1344 PTERMINATION_PORT TerminationPort;
1345 PVOID TerminationLpcPort;
1346 PETHREAD Thread;
1347 PAGED_CODE();
1348 PSTRACE(PS_KILL_DEBUG, "PortHandle: %p\n", PortHandle);
1349
1350 /* Get the Port */
1351 Status = ObReferenceObjectByHandle(PortHandle,
1352 PORT_ALL_ACCESS,
1353 LpcPortObjectType,
1354 KeGetPreviousMode(),
1355 &TerminationLpcPort,
1356 NULL);
1357 if (!NT_SUCCESS(Status)) return(Status);
1358
1359 /* Allocate the Port and make sure it suceeded */
1360 TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
1361 sizeof(TERMINATION_PORT),
1362 '=TsP');
1363 if(TerminationPort)
1364 {
1365 /* Associate the Port */
1366 Thread = PsGetCurrentThread();
1367 TerminationPort->Port = TerminationLpcPort;
1368 TerminationPort->Next = Thread->TerminationPort;
1369 Thread->TerminationPort = TerminationPort;
1370
1371 /* Return success */
1372 return STATUS_SUCCESS;
1373 }
1374
1375 /* Dereference and Fail */
1376 ObDereferenceObject(TerminationLpcPort);
1377 return STATUS_INSUFFICIENT_RESOURCES;
1378 }