/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS kernel
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ps/kill.c
- * PURPOSE: Thread Termination and Reaping
- *
- * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
- * David Welch (welch@cwcom.net)
+ * PURPOSE: Process Manager: Process and Thread Termination
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ * Filip Navara (xnavara@reactos.org)
+ * Thomas Weidenmueller (w3seek@reactos.org
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
-#include <internal/debug.h>
-
-#define LockEvent Spare0[0]
-#define LockCount Spare0[1]
-#define LockOwner Spare0[2]
-
+#include <debug.h>
/* GLOBALS *******************************************************************/
-PETHREAD PspReaperList = NULL;
+LIST_ENTRY PspReaperListHead = { NULL, NULL };
WORK_QUEUE_ITEM PspReaperWorkItem;
-BOOLEAN PspReaping = FALSE;
-extern LIST_ENTRY PsActiveProcessHead;
-extern FAST_MUTEX PspActiveProcessMutex;
-extern PHANDLE_TABLE PspCidTable;
+LARGE_INTEGER ShortTime = {{-10 * 100 * 1000, -1}};
-/* FUNCTIONS *****************************************************************/
+/* PRIVATE FUNCTIONS *********************************************************/
VOID
-STDCALL
-PspReapRoutine(PVOID Context)
+NTAPI
+PspCatchCriticalBreak(IN PCHAR Message,
+ IN PVOID ProcessOrThread,
+ IN PCHAR ImageName)
{
- KIRQL OldIrql;
- PETHREAD Thread, NewThread;
+ CHAR Action[2];
+ BOOLEAN Handled = FALSE;
+ PAGED_CODE();
- /* Acquire lock */
- DPRINT("Evil reaper running!!\n");
- OldIrql = KeAcquireDispatcherDatabaseLock();
+ /* Check if a debugger is enabled */
+ if (KdDebuggerEnabled)
+ {
+ /* Print out the message */
+ DbgPrint(Message, ProcessOrThread, ImageName);
+ do
+ {
+ /* If a debugger isn't present, don't prompt */
+ if (KdDebuggerNotPresent) break;
- /* Get the first Thread Entry */
- Thread = PspReaperList;
- PspReaperList = NULL;
- DPRINT("PspReaperList: %x\n", Thread);
+ /* A debuger is active, prompt for action */
+ DbgPrompt("Break, or Ignore (bi)?", Action, sizeof(Action));
+ switch (Action[0])
+ {
+ /* Break */
+ case 'B': case 'b':
- /* Check to see if the list is empty */
- do {
+ /* Do a breakpoint */
+ DbgBreakPoint();
- /* Unlock the Dispatcher */
- KeReleaseDispatcherDatabaseLock(OldIrql);
+ /* Ignore */
+ case 'I': case 'i':
- /* Is there a thread on the list? */
- while (Thread) {
+ /* Handle it */
+ Handled = TRUE;
- /* Get the next Thread */
- DPRINT("Thread: %x\n", Thread);
- DPRINT("Thread: %x\n", Thread->ReaperLink);
- NewThread = Thread->ReaperLink;
+ /* Unrecognized */
+ default:
+ break;
+ }
+ } while (!Handled);
+ }
- /* Remove reference to current thread */
- ObDereferenceObject(Thread);
+ /* Did we ultimately handle this? */
+ if (!Handled)
+ {
+ /* We didn't, bugcheck */
+ KeBugCheckEx(CRITICAL_OBJECT_TERMINATION,
+ ((PKPROCESS)ProcessOrThread)->Header.Type,
+ (ULONG_PTR)ProcessOrThread,
+ (ULONG_PTR)ImageName,
+ (ULONG_PTR)Message);
+ }
+}
- /* Move to next Thread */
- Thread = NewThread;
- }
+NTSTATUS
+NTAPI
+PspTerminateProcess(IN PEPROCESS Process,
+ IN NTSTATUS ExitStatus)
+{
+ PETHREAD Thread;
+ NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG,
+ "Process: %p ExitStatus: %p\n", Process, ExitStatus);
+ PSREFTRACE(Process);
- /* No more linked threads... Reacquire the Lock */
- OldIrql = KeAcquireDispatcherDatabaseLock();
+ /* Check if this is a Critical Process */
+ if (Process->BreakOnTermination)
+ {
+ /* Break to debugger */
+ PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
+ Process,
+ Process->ImageFileName);
+ }
- /* Now try to get a new thread from the list */
- Thread = PspReaperList;
- PspReaperList = NULL;
- DPRINT("PspReaperList: %x\n", Thread);
+ /* Set the delete flag */
+ InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT);
- /* Loop again if there is a new thread */
- } while (Thread);
+ /* Get the first thread */
+ Thread = PsGetNextProcessThread(Process, NULL);
+ while (Thread)
+ {
+ /* Kill it */
+ PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
+ Thread = PsGetNextProcessThread(Process, Thread);
+
+ /* We had at least one thread, so termination is OK */
+ Status = STATUS_SUCCESS;
+ }
- PspReaping = FALSE;
- DPRINT("Done reaping\n");
- KeReleaseDispatcherDatabaseLock(OldIrql);
+ /* Check if there was nothing to terminate or if we have a debug port */
+ if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort))
+ {
+ /* Clear the handle table anyway */
+ ObClearProcessHandleTable(Process);
+ }
+
+ /* Return status */
+ return Status;
}
-VOID
-STDCALL
-PspKillMostProcesses(VOID)
+NTSTATUS
+NTAPI
+PsTerminateProcess(IN PEPROCESS Process,
+ IN NTSTATUS ExitStatus)
{
- PLIST_ENTRY current_entry;
- PEPROCESS current;
-
- ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess->UniqueProcessId);
+ /* Call the internal API */
+ return PspTerminateProcess(Process, ExitStatus);
+}
- /* Acquire the Active Process Lock */
- ExAcquireFastMutex(&PspActiveProcessMutex);
+VOID
+NTAPI
+PspShutdownProcessManager(VOID)
+{
+ PEPROCESS Process = NULL;
- /* Loop all processes on the list */
- current_entry = PsActiveProcessHead.Flink;
- while (current_entry != &PsActiveProcessHead)
+ /* Loop every process */
+ Process = PsGetNextProcess(Process);
+ while (Process)
{
- current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
- current_entry = current_entry->Flink;
-
- if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId)
+ /* Make sure this isn't the idle or initial process */
+ if ((Process != PsInitialSystemProcess) && (Process != PsIdleProcess))
{
- /* Terminate all the Threads in this Process */
- PspTerminateProcessThreads(current, STATUS_SUCCESS);
+ /* Kill it */
+ PspTerminateProcess(Process, STATUS_SYSTEM_SHUTDOWN);
}
- }
- /* Release the lock */
- ExReleaseFastMutex(&PspActiveProcessMutex);
+ /* Get the next process */
+ Process = PsGetNextProcess(Process);
+ }
}
VOID
-STDCALL
-PspTerminateProcessThreads(PEPROCESS Process,
- NTSTATUS ExitStatus)
+NTAPI
+PspExitApcRundown(IN PKAPC Apc)
{
- PLIST_ENTRY CurrentEntry;
- PETHREAD Thread, CurrentThread = PsGetCurrentThread();
+ PAGED_CODE();
+
+ /* Free the APC */
+ ExFreePool(Apc);
+}
- CurrentEntry = Process->ThreadListHead.Flink;
- while (CurrentEntry != &Process->ThreadListHead) {
+VOID
+NTAPI
+PspReapRoutine(IN PVOID Context)
+{
+ PSINGLE_LIST_ENTRY NextEntry;
+ PETHREAD Thread;
+ PSTRACE(PS_KILL_DEBUG, "Context: %p\n", Context);
- /* Get the Current Thread */
- Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
+ /* Start main loop */
+ do
+ {
+ /* Write magic value and return the next entry to process */
+ NextEntry = InterlockedExchangePointer(&PspReaperListHead.Flink,
+ (PVOID)1);
+ ASSERT((NextEntry != NULL) && (NextEntry != (PVOID)1));
- /* Move to the Next Thread */
- CurrentEntry = CurrentEntry->Flink;
+ /* Start inner loop */
+ do
+ {
+ /* Get the first Thread Entry */
+ Thread = CONTAINING_RECORD(NextEntry, ETHREAD, ReaperLink);
- /* Make sure it's not the one we're in */
- if (Thread != CurrentThread) {
+ /* Delete this entry's kernel stack */
+ MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
+ Thread->Tcb.LargeStack);
+ Thread->Tcb.InitialStack = NULL;
- /* Make sure it didn't already terminate */
- if (!Thread->Terminated) {
+ /* Move to the next entry */
+ NextEntry = NextEntry->Next;
- Thread->Terminated = TRUE;
+ /* Dereference this thread */
+ ObDereferenceObject(Thread);
+ } while ((NextEntry != NULL) && (NextEntry != (PVOID)1));
- /* Terminate it by APC */
- PspTerminateThreadByPointer(Thread, ExitStatus);
- }
- }
- }
+ /* Remove magic value, keep looping if it got changed */
+ } while (InterlockedCompareExchangePointer(&PspReaperListHead.Flink,
+ 0,
+ 1) != (PVOID)1);
}
VOID
-STDCALL
-PspDeleteProcess(PVOID ObjectBody)
+NTAPI
+PspDeleteProcess(IN PVOID ObjectBody)
{
PEPROCESS Process = (PEPROCESS)ObjectBody;
+ KAPC_STATE ApcState;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
+ PSREFTRACE(Process);
+
+ /* Check if it has an Active Process Link */
+ if (Process->ActiveProcessLinks.Flink)
+ {
+ /* Remove it from the Active List */
+ KeAcquireGuardedMutex(&PspActiveProcessMutex);
+ RemoveEntryList(&Process->ActiveProcessLinks);
+ KeReleaseGuardedMutex(&PspActiveProcessMutex);
+ }
+
+ /* Check for Auditing information */
+ if (Process->SeAuditProcessCreationInfo.ImageFileName)
+ {
+ /* Free it */
+ ExFreePool(Process->SeAuditProcessCreationInfo.ImageFileName);
+ Process->SeAuditProcessCreationInfo.ImageFileName = NULL;
+ }
+
+ /* Check if we have a job */
+ if (Process->Job)
+ {
+ /* Remove the process from the job */
+ PspRemoveProcessFromJob(Process, Process->Job);
- DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
+ /* Dereference it */
+ ObDereferenceObject(Process->Job);
+ Process->Job = NULL;
+ }
- /* Remove it from the Active List */
- ExAcquireFastMutex(&PspActiveProcessMutex);
- RemoveEntryList(&Process->ActiveProcessLinks);
- ExReleaseFastMutex(&PspActiveProcessMutex);
+ /* Increase the stack count */
+ Process->Pcb.StackCount++;
- /* Delete the CID Handle */
- if(Process->UniqueProcessId)
+ /* Check if we have a debug port */
+ if (Process->DebugPort)
{
- ExDestroyHandle(PspCidTable, Process->UniqueProcessId);
+ /* Deference the Debug Port */
+ ObDereferenceObject(Process->DebugPort);
+ Process->DebugPort = NULL;
}
- /* Delete the process lock */
- ExFreePool(Process->LockEvent);
+ /* Check if we have an exception port */
+ if (Process->ExceptionPort)
+ {
+ /* Deference the Exception Port */
+ ObDereferenceObject(Process->ExceptionPort);
+ Process->ExceptionPort = NULL;
+ }
- /* KDB hook */
- KDB_DELETEPROCESS_HOOK(Process);
+ /* Check if we have a section object */
+ if (Process->SectionObject)
+ {
+ /* Deference the Section Object */
+ ObDereferenceObject(Process->SectionObject);
+ Process->SectionObject = NULL;
+ }
- /* Dereference the Token */
- SeDeassignPrimaryToken(Process);
+#if defined(_X86_)
+ /* Clean Ldt and Vdm objects */
+ PspDeleteLdt(Process);
+ PspDeleteVdmObjects(Process);
+#endif
+
+ /* Delete the Object Table */
+ if (Process->ObjectTable)
+ {
+ /* Attach to the process */
+ KeStackAttachProcess(&Process->Pcb, &ApcState);
- /* Release Memory Information */
- MmReleaseMmInfo(Process);
+ /* Kill the Object Info */
+ ObKillProcess(Process);
- /* Delete the W32PROCESS structure if there's one associated */
- if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
+ /* Detach */
+ KeUnstackDetachProcess(&ApcState);
+ }
+
+ /* Check if we have an address space, and clean it */
+ if (Process->HasAddressSpace)
+ {
+ /* Attach to the process */
+ KeStackAttachProcess(&Process->Pcb, &ApcState);
+
+ /* Clean the Address Space */
+ PspExitProcess(FALSE, Process);
+
+ /* Detach */
+ KeUnstackDetachProcess(&ApcState);
+
+ /* Completely delete the Address Space */
+ MmDeleteProcessAddressSpace(Process);
+ }
+
+ /* See if we have a PID */
+ if (Process->UniqueProcessId)
+ {
+ /* Delete the PID */
+ if (!(ExDestroyHandle(PspCidTable, Process->UniqueProcessId, NULL)))
+ {
+ /* Something wrong happened, bugcheck */
+ KeBugCheck(CID_HANDLE_DELETION);
+ }
+ }
+
+ /* Cleanup security information */
+ PspDeleteProcessSecurity(Process);
+
+ /* Check if we have kept information on the Working Set */
+ if (Process->WorkingSetWatch)
+ {
+ /* Free it */
+ ExFreePool(Process->WorkingSetWatch);
+
+ /* And return the quota it was taking up */
+ PsReturnProcessNonPagedPoolQuota(Process, 0x2000);
+ }
+
+ /* Dereference the Device Map */
+ ObDereferenceDeviceMap(Process);
+
+ /* Destroy the Quota Block */
+ PspDestroyQuotaBlock(Process);
}
VOID
-STDCALL
-PspDeleteThread(PVOID ObjectBody)
+NTAPI
+PspDeleteThread(IN PVOID ObjectBody)
{
PETHREAD Thread = (PETHREAD)ObjectBody;
PEPROCESS Process = Thread->ThreadsProcess;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody);
+ PSREFTRACE(Thread);
+ ASSERT(Thread->Tcb.Win32Thread == NULL);
- DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
-
- /* Deassociate the Process */
- Thread->ThreadsProcess = NULL;
+ /* Check if we have a stack */
+ if (Thread->Tcb.InitialStack)
+ {
+ /* Release it */
+ MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase,
+ Thread->Tcb.LargeStack);
+ }
- /* Delete the CID Handle */
- if(Thread->Cid.UniqueThread)
+ /* Check if we have a CID Handle */
+ if (Thread->Cid.UniqueThread)
{
- ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread);
+ /* Delete the CID Handle */
+ if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL)))
+ {
+ /* Something wrong happened, bugcheck */
+ KeBugCheck(CID_HANDLE_DELETION);
+ }
}
- /* Free the W32THREAD structure if present */
- if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
+ /* Cleanup impersionation information */
+ PspDeleteThreadSecurity(Thread);
- /* Release the Kernel Stack */
- MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, Thread->Tcb.LargeStack);
+ /* Make sure the thread was inserted, before continuing */
+ if (!Process) return;
+
+ /* Check if the thread list is valid */
+ if (Thread->ThreadListEntry.Flink)
+ {
+ /* Lock the thread's process */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&Process->ProcessLock);
+
+ /* Remove us from the list */
+ RemoveEntryList(&Thread->ThreadListEntry);
+
+ /* Release the lock */
+ ExReleasePushLockExclusive(&Process->ProcessLock);
+ KeLeaveCriticalRegion();
+ }
/* Dereference the Process */
ObDereferenceObject(Process);
* See "Windows Internals" - Chapter 13, Page 50-53
*/
VOID
-STDCALL
-PspExitThread(NTSTATUS ExitStatus)
+NTAPI
+PspExitThread(IN NTSTATUS ExitStatus)
{
- PETHREAD CurrentThread;
- BOOLEAN Last;
- PEPROCESS CurrentProcess;
- PTERMINATION_PORT TerminationPort;
+ CLIENT_DIED_MSG TerminationMsg;
+ NTSTATUS Status;
PTEB Teb;
- KIRQL oldIrql;
+ PEPROCESS CurrentProcess;
+ PETHREAD Thread, OtherThread, PreviousThread = NULL;
+ PVOID DeallocationStack;
+ ULONG Dummy;
+ BOOLEAN Last = FALSE;
+ PTERMINATION_PORT TerminationPort, NextPort;
PLIST_ENTRY FirstEntry, CurrentEntry;
PKAPC Apc;
-
- DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
+ PTOKEN PrimaryToken;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG, "ExitStatus: %p\n", ExitStatus);
/* Get the Current Thread and Process */
- CurrentThread = PsGetCurrentThread();
- CurrentProcess = CurrentThread->ThreadsProcess;
-
- /* Set the Exit Status and Exit Time */
- CurrentThread->ExitStatus = ExitStatus;
- KeQuerySystemTime(&CurrentThread->ExitTime);
+ Thread = PsGetCurrentThread();
+ CurrentProcess = Thread->ThreadsProcess;
+ ASSERT((Thread) == PsGetCurrentThread());
/* Can't terminate a thread if it attached another process */
- if (KeIsAttachedProcess()) {
-
- KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
- (ULONG) CurrentThread->Tcb.ApcState.Process,
- (ULONG) CurrentThread->Tcb.ApcStateIndex,
- (ULONG) CurrentThread);
+ if (KeIsAttachedProcess())
+ {
+ /* Bugcheck */
+ KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
+ (ULONG_PTR)CurrentProcess,
+ (ULONG_PTR)Thread->Tcb.ApcState.Process,
+ (ULONG_PTR)Thread->Tcb.ApcStateIndex,
+ (ULONG_PTR)Thread);
}
/* Lower to Passive Level */
KeLowerIrql(PASSIVE_LEVEL);
- /* Lock the Process before we modify its thread entries */
- PsLockProcess(CurrentProcess, FALSE);
+ /* Can't be a worker thread */
+ if (Thread->ActiveExWorker)
+ {
+ /* Bugcheck */
+ KeBugCheckEx(ACTIVE_EX_WORKER_THREAD_TERMINATION,
+ (ULONG_PTR)Thread,
+ 0,
+ 0,
+ 0);
+ }
+
+ /* Can't have pending APCs */
+ if (Thread->Tcb.CombinedApcDisable != 0)
+ {
+ /* Bugcheck */
+ KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
+ 0,
+ Thread->Tcb.CombinedApcDisable,
+ 0,
+ 1);
+ }
+
+ /* Lock the thread */
+ ExWaitForRundownProtectionRelease(&Thread->RundownProtect);
+
+ /* Cleanup the power state */
+ PopCleanupPowerState((PPOWER_STATE)&Thread->Tcb.PowerState);
- /* wake up the thread so we don't deadlock on PsLockProcess */
- KeForceResumeThread(&CurrentThread->Tcb);
+ /* Call the WMI Callback for Threads */
+ //WmiTraceThread(Thread, NULL, FALSE);
/* Run Thread Notify Routines before we desintegrate the thread */
- PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
+ PspRunCreateThreadNotifyRoutines(Thread, FALSE);
- /* Remove the thread from the thread list of its process */
- RemoveEntryList(&CurrentThread->ThreadListEntry);
- Last = IsListEmpty(&CurrentProcess->ThreadListHead);
+ /* Lock the Process before we modify its thread entries */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
- /* Set the last Thread Exit Status */
- CurrentProcess->LastThreadExitStatus = ExitStatus;
+ /* Decrease the active thread count, and check if it's 0 */
+ if (!(--CurrentProcess->ActiveThreads))
+ {
+ /* Set the delete flag */
+ InterlockedOr((PLONG)&CurrentProcess->Flags, PSF_PROCESS_DELETE_BIT);
- if (Last) {
+ /* Remember we are last */
+ Last = TRUE;
- /* Save the Exit Time if not already done by NtTerminateProcess. This
- happens when the last thread just terminates without explicitly
- terminating the process. */
- CurrentProcess->ExitTime = CurrentThread->ExitTime;
- CurrentProcess->ExitStatus = ExitStatus;
- }
+ /* Check if this termination is due to the thread dying */
+ if (ExitStatus == STATUS_THREAD_IS_TERMINATING)
+ {
+ /* Check if the last thread was pending */
+ if (CurrentProcess->ExitStatus == STATUS_PENDING)
+ {
+ /* Use the last exit status */
+ CurrentProcess->ExitStatus = CurrentProcess->
+ LastThreadExitStatus;
+ }
+ }
+ else
+ {
+ /* Just a normal exit, write the code */
+ CurrentProcess->ExitStatus = ExitStatus;
+ }
- /* Check if the process has a debug port */
- if (CurrentProcess->DebugPort) {
+ /* Loop all the current threads */
+ FirstEntry = &CurrentProcess->ThreadListHead;
+ CurrentEntry = FirstEntry->Flink;
+ while (FirstEntry != CurrentEntry)
+ {
+ /* Get the thread on the list */
+ OtherThread = CONTAINING_RECORD(CurrentEntry,
+ ETHREAD,
+ ThreadListEntry);
+
+ /* Check if it's a thread that's still alive */
+ if ((OtherThread != Thread) &&
+ !(KeReadStateThread(&OtherThread->Tcb)) &&
+ (ObReferenceObjectSafe(OtherThread)))
+ {
+ /* It's a live thread and we referenced it, unlock process */
+ ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
+ KeLeaveCriticalRegion();
+
+ /* Wait on the thread */
+ KeWaitForSingleObject(OtherThread,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ /* Check if we had a previous thread to dereference */
+ if (PreviousThread) ObDereferenceObject(PreviousThread);
+
+ /* Remember the thread and re-lock the process */
+ PreviousThread = OtherThread;
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
+ }
- /* Notify the Debug API. TODO */
- //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
+ /* Go to the next thread */
+ CurrentEntry = CurrentEntry->Flink;
+ }
+ }
+ else if (ExitStatus != STATUS_THREAD_IS_TERMINATING)
+ {
+ /* Write down the exit status of the last thread to get killed */
+ CurrentProcess->LastThreadExitStatus = ExitStatus;
}
- /* Process the Termination Ports */
- while ((TerminationPort = CurrentThread->TerminationPort)) {
+ /* Unlock the Process */
+ ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
+ KeLeaveCriticalRegion();
- DPRINT("TerminationPort: %p\n", TerminationPort);
+ /* Check if we had a previous thread to dereference */
+ if (PreviousThread) ObDereferenceObject(PreviousThread);
- /* Get the next one */
- CurrentThread->TerminationPort = TerminationPort->Next;
+ /* Check if the process has a debug port and if this is a user thread */
+ if ((CurrentProcess->DebugPort) && !(Thread->SystemThread))
+ {
+ /* Notify the Debug API. */
+ Last ? DbgkExitProcess(CurrentProcess->ExitStatus) :
+ DbgkExitThread(ExitStatus);
+ }
- /* Send the LPC Message */
- LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
- ObDereferenceObject(TerminationPort->Port);
+ /* Check if this is a Critical Thread */
+ if ((KdDebuggerEnabled) && (Thread->BreakOnTermination))
+ {
+ /* Break to debugger */
+ PspCatchCriticalBreak("Critical thread 0x%p (in %s) exited\n",
+ Thread,
+ CurrentProcess->ImageFileName);
+ }
- /* Free the Port */
- ExFreePool(TerminationPort);
+ /* Check if it's the last thread and this is a Critical Process */
+ if ((Last) && (CurrentProcess->BreakOnTermination))
+ {
+ /* Check if a debugger is here to handle this */
+ if (KdDebuggerEnabled)
+ {
+ /* Break to debugger */
+ PspCatchCriticalBreak("Critical process 0x%p (in %s) exited\n",
+ CurrentProcess,
+ CurrentProcess->ImageFileName);
+ }
+ else
+ {
+ /* Bugcheck, we can't allow this */
+ KeBugCheckEx(CRITICAL_PROCESS_DIED,
+ (ULONG_PTR)CurrentProcess,
+ 0,
+ 0,
+ 0);
+ }
}
- /* Rundown Win32 Structures */
- PsTerminateWin32Thread(CurrentThread);
- if (Last) PsTerminateWin32Process(CurrentProcess);
+ /* Sanity check */
+ ASSERT(Thread->Tcb.CombinedApcDisable == 0);
- /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
- //CmNotifyRunDown(CurrentThread);
+ /* Process the Termination Ports */
+ TerminationPort = Thread->TerminationPort;
+ if (TerminationPort)
+ {
+ /* Setup the message header */
+ TerminationMsg.h.u2.ZeroInit = 0;
+ TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
+ TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
+ TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
+ sizeof(PORT_MESSAGE);
+
+ /* Loop each port */
+ do
+ {
+ /* Save the Create Time */
+ TerminationMsg.CreateTime = Thread->CreateTime;
+
+ /* Loop trying to send message */
+ while (TRUE)
+ {
+ /* Send the LPC Message */
+ Status = LpcRequestPort(TerminationPort->Port,
+ &TerminationMsg.h);
+ if ((Status == STATUS_NO_MEMORY) ||
+ (Status == STATUS_INSUFFICIENT_RESOURCES))
+ {
+ /* Wait a bit and try again */
+ KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
+ continue;
+ }
+ break;
+ }
+
+ /* Dereference this LPC Port */
+ ObDereferenceObject(TerminationPort->Port);
- /* Free the TEB */
- if((Teb = CurrentThread->Tcb.Teb))
+ /* Move to the next one */
+ NextPort = TerminationPort->Next;
+
+ /* Free the Termination Port Object */
+ ExFreePool(TerminationPort);
+
+ /* Keep looping as long as there is a port */
+ TerminationPort = NextPort;
+ } while (TerminationPort);
+ }
+ else if (((ExitStatus == STATUS_THREAD_IS_TERMINATING) &&
+ (Thread->DeadThread)) ||
+ !(Thread->DeadThread))
{
- /* Clean up the stack first, if requested */
- if (Teb->FreeStackOnTermination)
+ /*
+ * This case is special and deserves some extra comments. What
+ * basically happens here is that this thread doesn't have a termination
+ * port, which means that it died before being fully created. Since we
+ * still have to notify an LPC Server, we'll use the exception port,
+ * which we know exists. However, we need to know how far the thread
+ * actually got created. We have three possibilites:
+ *
+ * - NtCreateThread returned an error really early: DeadThread is set.
+ * - NtCreateThread managed to create the thread: DeadThread is off.
+ * - NtCreateThread was creating the thread (with Deadthread set,
+ * but the thread got killed prematurely: STATUS_THREAD_IS_TERMINATING
+ * is our exit code.)
+ *
+ * For the 2 & 3rd scenarios, the thread has been created far enough to
+ * warrant notification to the LPC Server.
+ */
+
+ /* Setup the message header */
+ TerminationMsg.h.u2.s2.Type = LPC_CLIENT_DIED;
+ TerminationMsg.h.u1.s1.TotalLength = sizeof(TerminationMsg);
+ TerminationMsg.h.u1.s1.DataLength = sizeof(TerminationMsg) -
+ sizeof(PORT_MESSAGE);
+
+ /* Make sure the process has an exception port */
+ if (CurrentProcess->ExceptionPort)
{
- ULONG Dummy = 0;
- ZwFreeVirtualMemory(NtCurrentProcess(),
- &Teb->DeallocationStack,
- &Dummy,
- MEM_RELEASE);
+ /* Save the Create Time */
+ TerminationMsg.CreateTime = Thread->CreateTime;
+
+ /* Loop trying to send message */
+ while (TRUE)
+ {
+ /* Send the LPC Message */
+ Status = LpcRequestPort(CurrentProcess->ExceptionPort,
+ &TerminationMsg.h);
+ if ((Status == STATUS_NO_MEMORY) ||
+ (Status == STATUS_INSUFFICIENT_RESOURCES))
+ {
+ /* Wait a bit and try again */
+ KeDelayExecutionThread(KernelMode, FALSE, &ShortTime);
+ continue;
+ }
+ break;
+ }
}
-
- DPRINT("Decommit teb at %p\n", Teb);
- MmDeleteTeb(CurrentProcess, Teb);
- CurrentThread->Tcb.Teb = NULL;
}
- /* The last Thread shuts down the Process */
- if (Last) PspExitProcess(CurrentProcess);
+ /* Rundown Win32 Thread if there is one */
+ if (Thread->Tcb.Win32Thread) PspW32ThreadCallout(Thread,
+ PsW32ThreadCalloutExit);
- /* Unlock the Process */
- PsUnlockProcess(CurrentProcess);
+ /* If we are the last thread and have a W32 Process */
+ if ((Last) && (CurrentProcess->Win32Process))
+ {
+ /* Run it down too */
+ PspW32ProcessCallout(CurrentProcess, FALSE);
+ }
+
+ /* Make sure Stack Swap is enabled */
+ if (!Thread->Tcb.EnableStackSwap)
+ {
+ /* Stack swap really shouldn't be disabled during exit! */
+ KeBugCheckEx(KERNEL_STACK_LOCKED_AT_EXIT, 0, 0, 0, 0);
+ }
/* Cancel I/O for the thread. */
- IoCancelThreadIo(CurrentThread);
+ IoCancelThreadIo(Thread);
/* Rundown Timers */
ExTimerRundown();
- KeCancelTimer(&CurrentThread->Tcb.Timer);
- /* If the Processor Control Block's NpxThread points to the current thread
- * unset it.
- */
- KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
- (void)InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
- NULL,
- (PVOID)CurrentThread);
- KeLowerIrql(oldIrql);
+ /* FIXME: Rundown Registry Notifications (NtChangeNotify)
+ CmNotifyRunDown(Thread); */
/* Rundown Mutexes */
KeRundownThread();
- /* Disable new APC Queuing, this is as far as we'll let them go */
- KeDisableThreadApcQueueing(&CurrentThread->Tcb);
+ /* Check if we have a TEB */
+ Teb = Thread->Tcb.Teb;
+ if (Teb)
+ {
+ /* Check if the thread is still alive */
+ if (!Thread->DeadThread)
+ {
+ /* Check if we need to free its stack */
+ if (Teb->FreeStackOnTermination)
+ {
+ /* Set the TEB's Deallocation Stack as the Base Address */
+ Dummy = 0;
+ DeallocationStack = Teb->DeallocationStack;
+
+ /* Free the Thread's Stack */
+ ZwFreeVirtualMemory(NtCurrentProcess(),
+ &DeallocationStack,
+ &Dummy,
+ MEM_RELEASE);
+ }
+
+ /* Free the debug handle */
+ if (Teb->DbgSsReserved[1]) ObCloseHandle(Teb->DbgSsReserved[1],
+ UserMode);
+ }
+
+ /* Decommit the TEB */
+ MmDeleteTeb(CurrentProcess, Teb);
+ Thread->Tcb.Teb = NULL;
+ }
+
+ /* Free LPC Data */
+ LpcExitThread(Thread);
+
+ /* Save the exit status and exit time */
+ Thread->ExitStatus = ExitStatus;
+ KeQuerySystemTime(&Thread->ExitTime);
+
+ /* Sanity check */
+ ASSERT(Thread->Tcb.CombinedApcDisable == 0);
+
+ /* Check if this is the final thread or not */
+ if (Last)
+ {
+ /* Set the process exit time */
+ CurrentProcess->ExitTime = Thread->ExitTime;
+
+ /* Exit the process */
+ PspExitProcess(TRUE, CurrentProcess);
+
+ /* Get the process token and check if we need to audit */
+ PrimaryToken = PsReferencePrimaryToken(CurrentProcess);
+ if (SeDetailedAuditingWithToken(PrimaryToken))
+ {
+ /* Audit the exit */
+ SeAuditProcessExit(CurrentProcess);
+ }
+
+ /* Dereference the process token */
+ ObFastDereferenceObject(&CurrentProcess->Token, PrimaryToken);
+
+ /* Check if this is a VDM Process and rundown the VDM DPCs if so */
+ if (CurrentProcess->VdmObjects) { /* VdmRundownDpcs(CurrentProcess); */ }
+
+ /* Kill the process in the Object Manager */
+ ObKillProcess(CurrentProcess);
+
+ /* Check if we have a section object */
+ if (CurrentProcess->SectionObject)
+ {
+ /* Dereference and clear the Section Object */
+ ObDereferenceObject(CurrentProcess->SectionObject);
+ CurrentProcess->SectionObject = NULL;
+ }
+
+ /* Check if the process is part of a job */
+ if (CurrentProcess->Job)
+ {
+ /* Remove the process from the job */
+ PspExitProcessFromJob(CurrentProcess->Job, CurrentProcess);
+ }
+ }
+
+ /* Disable APCs */
+ KeEnterCriticalRegion();
+
+ /* Disable APC queueing, force a resumption */
+ Thread->Tcb.ApcQueueable = FALSE;
+ KeForceResumeThread(&Thread->Tcb);
+
+ /* Re-enable APCs */
+ KeLeaveCriticalRegion();
/* Flush the User APCs */
- FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, UserMode);
- if (FirstEntry != NULL)
+ FirstEntry = KeFlushQueueApc(&Thread->Tcb, UserMode);
+ if (FirstEntry)
{
+ /* Start with the first entry */
CurrentEntry = FirstEntry;
do
{
if (Apc->RundownRoutine)
{
/* Call its own routine */
- (Apc->RundownRoutine)(Apc);
+ Apc->RundownRoutine(Apc);
}
else
{
while (CurrentEntry != FirstEntry);
}
+ /* Clean address space if this was the last thread */
+ if (Last) MmCleanProcessAddressSpace(CurrentProcess);
+
/* Call the Lego routine */
- if (CurrentThread->Tcb.LegoData) PspRunLegoRoutine(&CurrentThread->Tcb);
+ if (Thread->Tcb.LegoData) PspRunLegoRoutine(&Thread->Tcb);
/* Flush the APC queue, which should be empty */
- if ((FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, KernelMode)))
+ FirstEntry = KeFlushQueueApc(&Thread->Tcb, KernelMode);
+ if ((FirstEntry) || (Thread->Tcb.CombinedApcDisable != 0))
{
/* Bugcheck time */
- KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT,
+ KeBugCheckEx(KERNEL_APC_PENDING_DURING_EXIT,
(ULONG_PTR)FirstEntry,
- CurrentThread->Tcb.KernelApcDisable,
- oldIrql,
+ Thread->Tcb.CombinedApcDisable,
+ KeGetCurrentIrql(),
0);
}
+ /* Signal the process if this was the last thread */
+ if (Last) KeSetProcess(&CurrentProcess->Pcb, 0, FALSE);
+
/* Terminate the Thread from the Scheduler */
KeTerminateThread(0);
- DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
- KEBUGCHECK(0);
}
VOID
-STDCALL
-PsExitSpecialApc(PKAPC Apc,
- PKNORMAL_ROUTINE* NormalRoutine,
- PVOID* NormalContext,
- PVOID* SystemArgument1,
- PVOID* SystemArguemnt2)
+NTAPI
+PsExitSpecialApc(IN PKAPC Apc,
+ IN OUT PKNORMAL_ROUTINE* NormalRoutine,
+ IN OUT PVOID* NormalContext,
+ IN OUT PVOID* SystemArgument1,
+ IN OUT PVOID* SystemArgument2)
{
- DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x, '%.16s')\n",
- PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
+ NTSTATUS Status;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG,
+ "Apc: %p SystemArgument2: %p \n", Apc, SystemArgument2);
/* Don't do anything unless we are in User-Mode */
if (Apc->SystemArgument2)
{
- NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
-
/* Free the APC */
- ExFreePool(Apc);
+ Status = (NTSTATUS)Apc->NormalContext;
+ PspExitApcRundown(Apc);
/* Terminate the Thread */
- PspExitThread(ExitStatus);
-
- /* we should never reach this point! */
- KEBUGCHECK(0);
+ PspExitThread(Status);
}
}
VOID
-STDCALL
-PspExitNormalApc(PVOID NormalContext,
- PVOID SystemArgument1,
- PVOID SystemArgument2)
+NTAPI
+PspExitNormalApc(IN PVOID NormalContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
{
PKAPC Apc = (PKAPC)SystemArgument1;
PETHREAD Thread = PsGetCurrentThread();
- NTSTATUS ExitStatus;
-
- DPRINT("PspExitNormalApc called: 0x%x (proc: 0x%x, '%.16s')\n",
- PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG, "SystemArgument2: %p \n", SystemArgument2);
/* This should never happen */
- ASSERT(!SystemArgument2);
-
- /* If this is a system thread, we can safely kill it from Kernel-Mode */
- if (PsIsSystemThread(Thread))
- {
- /* Get the Exit Status */
- DPRINT1("Killing System Thread\n");
- ExitStatus = (NTSTATUS)Apc->NormalContext;
-
- /* Free the APC */
- ExFreePool(Apc);
-
- /* Exit the Thread */
- PspExitThread(ExitStatus);
- }
+ ASSERT(!(((ULONG_PTR)SystemArgument2) & 1));
/* If we're here, this is not a System Thread, so kill it from User-Mode */
- DPRINT("Initializing User-Mode APC\n");
KeInitializeApc(Apc,
&Thread->Tcb,
OriginalApcEnvironment,
PsExitSpecialApc,
- NULL,
+ PspExitApcRundown,
PspExitNormalApc,
UserMode,
NormalContext);
/* Now insert the APC with the User-Mode Flag */
- KeInsertQueueApc(Apc, Apc, (PVOID)UserMode, 2);
+ if (!(KeInsertQueueApc(Apc,
+ Apc,
+ (PVOID)((ULONG_PTR)SystemArgument2 | 1),
+ 2)))
+ {
+ /* Failed to insert, free the APC */
+ PspExitApcRundown(Apc);
+ }
- /* Forcefully resume the thread */
- KeForceResumeThread(&Thread->Tcb);
+ /* Set the APC Pending flag */
+ Thread->Tcb.ApcState.UserApcPending = TRUE;
}
/*
* See "Windows Internals" - Chapter 13, Page 49
*/
-VOID
-STDCALL
-PspTerminateThreadByPointer(PETHREAD Thread,
- NTSTATUS ExitStatus)
+NTSTATUS
+NTAPI
+PspTerminateThreadByPointer(IN PETHREAD Thread,
+ IN NTSTATUS ExitStatus,
+ IN BOOLEAN bSelf)
{
PKAPC Apc;
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Flags;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %p\n", Thread, ExitStatus);
+ PSREFTRACE(Thread);
+
+ /* Check if this is a Critical Thread, and Bugcheck */
+ if (Thread->BreakOnTermination)
+ {
+ /* Break to debugger */
+ PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n",
+ Thread,
+ Thread->ThreadsProcess->ImageFileName);
+ }
- DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
- Thread, ExitStatus);
+ /* Check if we are already inside the thread */
+ if ((bSelf) || (PsGetCurrentThread() == Thread))
+ {
+ /* This should only happen at passive */
+ ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
- /* Check if we are already in the right context */
- if (PsGetCurrentThread() == Thread) {
+ /* Mark it as terminated */
+ PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT);
/* Directly terminate the thread */
PspExitThread(ExitStatus);
-
- /* we should never reach this point! */
- KEBUGCHECK(0);
}
+ /* This shouldn't be a system thread */
+ if (Thread->SystemThread) return STATUS_ACCESS_DENIED;
+
/* Allocate the APC */
Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
+ if (!Apc) return STATUS_INSUFFICIENT_RESOURCES;
- /* Initialize a Kernel Mode APC to Kill the Thread */
- KeInitializeApc(Apc,
- &Thread->Tcb,
- OriginalApcEnvironment,
- PsExitSpecialApc,
- NULL,
- PspExitNormalApc,
- KernelMode,
- (PVOID)ExitStatus);
+ /* Set the Terminated Flag */
+ Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT;
+
+ /* Set it, and check if it was already set while we were running */
+ if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) &
+ CT_TERMINATED_BIT))
+ {
+ /* Initialize a Kernel Mode APC to Kill the Thread */
+ KeInitializeApc(Apc,
+ &Thread->Tcb,
+ OriginalApcEnvironment,
+ PsExitSpecialApc,
+ PspExitApcRundown,
+ PspExitNormalApc,
+ KernelMode,
+ (PVOID)ExitStatus);
+
+ /* Insert it into the APC Queue */
+ if (!KeInsertQueueApc(Apc, Apc, NULL, 2))
+ {
+ /* The APC was already in the queue, fail */
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ /* Forcefully resume the thread and return */
+ KeForceResumeThread(&Thread->Tcb);
+ return Status;
+ }
+ }
- /* Insert it into the APC Queue */
- KeInsertQueueApc(Apc,
- Apc,
- NULL,
- 2);
+ /* We failed, free the APC */
+ ExFreePool(Apc);
- /* Forcefully resume the thread */
- KeForceResumeThread(&Thread->Tcb);
+ /* Return Status */
+ return Status;
}
-NTSTATUS
-STDCALL
-PspExitProcess(PEPROCESS Process)
+VOID
+NTAPI
+PspExitProcess(IN BOOLEAN LastThread,
+ IN PEPROCESS Process)
{
- DPRINT("PspExitProcess 0x%x\n", Process);
+ ULONG Actual;
+ PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG,
+ "LastThread: %p Process: %p\n", LastThread, Process);
+ PSREFTRACE(Process);
- PspRunCreateProcessNotifyRoutines(Process, FALSE);
+ /* Set Process Exit flag */
+ InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_EXITING_BIT);
- PspDestroyQuotaBlock(Process);
+ /* Check if we are the last thread */
+ if (LastThread)
+ {
+ /* Notify the WMI Process Callback */
+ //WmiTraceProcess(Process, FALSE);
- /* close all handles associated with our process, this needs to be done
- when the last thread still runs */
- ObKillProcess(Process);
+ /* Run the Notification Routines */
+ PspRunCreateProcessNotifyRoutines(Process, FALSE);
+ }
- KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
-
- /* release the keep-alive reference of the process object */
- ObDereferenceObject(Process);
+ /* Cleanup the power state */
+ PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState);
+
+ /* Clear the security port */
+ if (!Process->SecurityPort)
+ {
+ /* So we don't double-dereference */
+ Process->SecurityPort = (PVOID)1;
+ }
+ else if (Process->SecurityPort != (PVOID)1)
+ {
+ /* Dereference it */
+ ObDereferenceObject(Process->SecurityPort);
+ Process->SecurityPort = (PVOID)1;
+ }
+
+ /* Check if we are the last thread */
+ if (LastThread)
+ {
+ /* Check if we have to set the Timer Resolution */
+ if (Process->SetTimerResolution)
+ {
+ /* Set it to default */
+ ZwSetTimerResolution(KeMaximumIncrement, 0, &Actual);
+ }
+
+ /* Check if we are part of a Job that has a completion port */
+ if ((Process->Job) && (Process->Job->CompletionPort))
+ {
+ /* FIXME: Check job status code and do I/O completion if needed */
+ }
+
+ /* FIXME: Notify the Prefetcher */
+ }
+ else
+ {
+ /* Clear process' address space here */
+ MmCleanProcessAddressSpace(Process);
+ }
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+PsTerminateSystemThread(IN NTSTATUS ExitStatus)
+{
+ PETHREAD Thread = PsGetCurrentThread();
- return(STATUS_SUCCESS);
+ /* Make sure this is a system thread */
+ if (!Thread->SystemThread) return STATUS_INVALID_PARAMETER;
+
+ /* Terminate it for real */
+ return PspTerminateThreadByPointer(Thread, ExitStatus, TRUE);
}
+/*
+ * @implemented
+ */
NTSTATUS
-STDCALL
-NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
+NTAPI
+NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
IN NTSTATUS ExitStatus)
{
NTSTATUS Status;
- PEPROCESS Process;
- PETHREAD CurrentThread;
+ PEPROCESS Process, CurrentProcess = PsGetCurrentProcess();
+ PETHREAD Thread, CurrentThread = PsGetCurrentThread();
BOOLEAN KillByHandle;
-
PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG,
+ "ProcessHandle: %p ExitStatus: %p\n", ProcessHandle, ExitStatus);
- DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
- ProcessHandle, ExitStatus);
-
- KillByHandle = (ProcessHandle != NULL);
+ /* Were we passed a process handle? */
+ if (ProcessHandle)
+ {
+ /* Yes we were, use it */
+ KillByHandle = TRUE;
+ }
+ else
+ {
+ /* We weren't... we assume this is suicide */
+ KillByHandle = FALSE;
+ ProcessHandle = NtCurrentProcess();
+ }
/* Get the Process Object */
- Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
+ Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_TERMINATE,
PsProcessType,
KeGetPreviousMode(),
(PVOID*)&Process,
NULL);
- if (!NT_SUCCESS(Status)) {
+ if (!NT_SUCCESS(Status)) return(Status);
- DPRINT1("Invalid handle to Process\n");
- return(Status);
+ /* Check if this is a Critical Process, and Bugcheck */
+ if (Process->BreakOnTermination)
+ {
+ /* Break to debugger */
+ PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
+ Process,
+ Process->ImageFileName);
}
- CurrentThread = PsGetCurrentThread();
-
- PsLockProcess(Process, FALSE);
-
- if(Process->ExitTime.QuadPart != 0)
+ /* Lock the Process */
+ if (!ExAcquireRundownProtection(&Process->RundownProtect))
{
- PsUnlockProcess(Process);
- ObDereferenceObject(Process);
- return STATUS_PROCESS_IS_TERMINATING;
+ /* Failed to lock, fail */
+ ObDereferenceObject (Process);
+ return STATUS_PROCESS_IS_TERMINATING;
}
- /* Terminate all the Process's Threads */
- PspTerminateProcessThreads(Process, ExitStatus);
-
- /* only kill the calling thread if it either passed a process handle or
- NtCurrentProcess() */
- if (KillByHandle) {
+ /* Set the delete flag, unless the process is comitting suicide */
+ if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT);
- /* set the exit time as we're about to release the process lock before
- we kill ourselves to prevent threads outside of our process trying
- to kill us */
- KeQuerySystemTime(&Process->ExitTime);
- Process->ExitStatus = ExitStatus;
+ /* Get the first thread */
+ Status = STATUS_NOTHING_TO_TERMINATE;
+ Thread = PsGetNextProcessThread(Process, NULL);
+ if (Thread)
+ {
+ /* We know we have at least a thread */
+ Status = STATUS_SUCCESS;
- /* Only master thread remains... kill it off */
- if (CurrentThread->ThreadsProcess == Process) {
+ /* Loop and kill the others */
+ do
+ {
+ /* Ensure it's not ours*/
+ if (Thread != CurrentThread)
+ {
+ /* Kill it */
+ PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
+ }
- /* mark our thread as terminating so attempts to terminate it, when
- unlocking the process, fail */
- CurrentThread->Terminated = TRUE;
+ /* Move to the next thread */
+ Thread = PsGetNextProcessThread(Process, Thread);
+ } while (Thread);
+ }
- PsUnlockProcess(Process);
+ /* Unlock the process */
+ ExReleaseRundownProtection(&Process->RundownProtect);
- /* we can safely dereference the process because the current thread
- holds a reference to it until it gets reaped */
+ /* Check if we are killing ourselves */
+ if (Process == CurrentProcess)
+ {
+ /* Also make sure the caller gave us our handle */
+ if (KillByHandle)
+ {
+ /* Dereference the process */
ObDereferenceObject(Process);
- /* now the other threads get a chance to terminate, we don't wait but
- just kill ourselves right now. The process will be run down when the
- last thread terminates */
+ /* Terminate ourselves */
+ PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE);
+ }
+ }
+ else if (ExitStatus == DBG_TERMINATE_PROCESS)
+ {
+ /* Disable debugging on this process */
+ DbgkClearProcessDebugObject(Process, NULL);
+ }
- PspExitThread(ExitStatus);
+ /* Check if there was nothing to terminate, or if we have a Debug Port */
+ if ((Status == STATUS_NOTHING_TO_TERMINATE) ||
+ ((Process->DebugPort) && (KillByHandle)))
+ {
+ /* Clear the handle table */
+ ObClearProcessHandleTable(Process);
- /* we should never reach this point! */
- KEBUGCHECK(0);
- }
+ /* Return status now */
+ Status = STATUS_SUCCESS;
}
- /* unlock and dereference the process so the threads can kill themselves */
- PsUnlockProcess(Process);
+ /* Decrease the reference count we added */
ObDereferenceObject(Process);
- return(STATUS_SUCCESS);
+ /* Return status */
+ return Status;
}
NTSTATUS
-STDCALL
+NTAPI
NtTerminateThread(IN HANDLE ThreadHandle,
IN NTSTATUS ExitStatus)
{
PETHREAD Thread;
+ PETHREAD CurrentThread = PsGetCurrentThread();
NTSTATUS Status;
-
PAGED_CODE();
-
+ PSTRACE(PS_KILL_DEBUG,
+ "ThreadHandle: %p ExitStatus: %p\n", ThreadHandle, ExitStatus);
+
/* Handle the special NULL case */
if (!ThreadHandle)
{
/* Check if we're the only thread left */
- if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
+ if (PsGetCurrentProcess()->ActiveThreads == 1)
{
/* This is invalid */
- DPRINT1("Can't terminate self\n");
return STATUS_CANT_TERMINATE_SELF;
}
- else
- {
- /* Use current handle */
- ThreadHandle = NtCurrentThread();
- }
+
+ /* Terminate us directly */
+ goto TerminateSelf;
+ }
+ else if (ThreadHandle == NtCurrentThread())
+ {
+TerminateSelf:
+ /* Terminate this thread */
+ return PspTerminateThreadByPointer(CurrentThread,
+ ExitStatus,
+ TRUE);
}
- /* Get the Thread Object */
+ /* We are terminating another thread, get the Thread Object */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_TERMINATE,
PsThreadType,
KeGetPreviousMode(),
(PVOID*)&Thread,
NULL);
- if (!NT_SUCCESS(Status)) {
-
- DPRINT1("Could not reference thread object\n");
- return(Status);
- }
-
- /* Make sure this is not a system thread */
- if (PsIsSystemThread(Thread)) {
-
- DPRINT1("Trying to Terminate a system thread!\n");
- ObDereferenceObject(Thread);
- return STATUS_INVALID_PARAMETER;
- }
+ if (!NT_SUCCESS(Status)) return Status;
/* Check to see if we're running in the same thread */
- if (Thread != PsGetCurrentThread()) {
-
- /* we need to lock the process to make sure it's not already terminating */
- PsLockProcess(Thread->ThreadsProcess, FALSE);
-
- /* This isn't our thread, terminate it if not already done */
- if (!Thread->Terminated) {
-
- Thread->Terminated = TRUE;
-
- /* Terminate it */
- PspTerminateThreadByPointer(Thread, ExitStatus);
- }
-
- PsUnlockProcess(Thread->ThreadsProcess);
+ if (Thread != CurrentThread)
+ {
+ /* Terminate it */
+ Status = PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
/* Dereference the Thread and return */
ObDereferenceObject(Thread);
-
- } else {
-
- Thread->Terminated = TRUE;
-
- /* it's safe to dereference thread, there's at least the keep-alive
- reference which will be removed by the thread reaper causing the
- thread to be finally destroyed */
- ObDereferenceObject(Thread);
-
- /* Terminate him, he's ours */
- PspExitThread(ExitStatus);
-
- /* We do never reach this point */
- KEBUGCHECK(0);
}
-
- return(STATUS_SUCCESS);
-}
-
-/*
- * @implemented
- */
-NTSTATUS
-STDCALL
-PsTerminateSystemThread(NTSTATUS ExitStatus)
-{
- PETHREAD Thread = PsGetCurrentThread();
-
- /* Make sure this is a system thread */
- if (!PsIsSystemThread(Thread)) {
-
- DPRINT1("Trying to Terminate a non-system thread!\n");
- return STATUS_INVALID_PARAMETER;
+ else
+ {
+ /* Dereference the thread and terminate ourselves */
+ ObDereferenceObject(Thread);
+ goto TerminateSelf;
}
- /* Terminate it for real */
- PspExitThread(ExitStatus);
-
- /* we should never reach this point! */
- KEBUGCHECK(0);
-
- return(STATUS_SUCCESS);
+ /* Return status */
+ return Status;
}
NTSTATUS
-STDCALL
-NtRegisterThreadTerminatePort(HANDLE PortHandle)
+NTAPI
+NtRegisterThreadTerminatePort(IN HANDLE PortHandle)
{
NTSTATUS Status;
PTERMINATION_PORT TerminationPort;
PVOID TerminationLpcPort;
PETHREAD Thread;
-
PAGED_CODE();
+ PSTRACE(PS_KILL_DEBUG, "PortHandle: %p\n", PortHandle);
/* Get the Port */
Status = ObReferenceObjectByHandle(PortHandle,
KeGetPreviousMode(),
&TerminationLpcPort,
NULL);
- if (!NT_SUCCESS(Status)) {
-
- DPRINT1("Failed to reference Port\n");
- return(Status);
- }
+ if (!NT_SUCCESS(Status)) return(Status);
/* Allocate the Port and make sure it suceeded */
- if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
- sizeof(TERMINATION_PORT),
- TAG('P', 's', 'T', '=')))) {
-
+ TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(TERMINATION_PORT),
+ '=TsP');
+ if(TerminationPort)
+ {
/* Associate the Port */
Thread = PsGetCurrentThread();
TerminationPort->Port = TerminationLpcPort;
- DPRINT("TerminationPort: %p\n", TerminationPort);
TerminationPort->Next = Thread->TerminationPort;
Thread->TerminationPort = TerminationPort;
- DPRINT("TerminationPort: %p\n", Thread->TerminationPort);
/* Return success */
- return(STATUS_SUCCESS);
-
- } else {
-
- /* Dereference and Fail */
- ObDereferenceObject(TerminationPort);
- return(STATUS_INSUFFICIENT_RESOURCES);
+ return STATUS_SUCCESS;
}
+
+ /* Dereference and Fail */
+ ObDereferenceObject(TerminationPort);
+ return STATUS_INSUFFICIENT_RESOURCES;
}