* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ps/kill.c
* PURPOSE: Thread Termination and Reaping
- *
+ *
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* David Welch (welch@cwcom.net)
*/
#include <internal/debug.h>
/* GLOBALS *******************************************************************/
-
-#define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
PETHREAD PspReaperList = NULL;
WORK_QUEUE_ITEM PspReaperWorkItem;
BOOLEAN PspReaping = FALSE;
extern LIST_ENTRY PsActiveProcessHead;
extern FAST_MUTEX PspActiveProcessMutex;
+extern PHANDLE_TABLE PspCidTable;
/* FUNCTIONS *****************************************************************/
-STDCALL
VOID
+STDCALL
PspReapRoutine(PVOID Context)
{
KIRQL OldIrql;
PETHREAD Thread, NewThread;
-
+
/* Acquire lock */
DPRINT("Evil reaper running!!\n");
OldIrql = KeAcquireDispatcherDatabaseLock();
-
+
/* Get the first Thread Entry */
Thread = PspReaperList;
PspReaperList = NULL;
DPRINT("PspReaperList: %x\n", Thread);
-
+
/* Check to see if the list is empty */
do {
-
+
/* Unlock the Dispatcher */
KeReleaseDispatcherDatabaseLock(OldIrql);
-
+
/* Is there a thread on the list? */
while (Thread) {
-
+
/* Get the next Thread */
DPRINT("Thread: %x\n", Thread);
DPRINT("Thread: %x\n", Thread->ReaperLink);
NewThread = Thread->ReaperLink;
-
+
/* Remove reference to current thread */
ObDereferenceObject(Thread);
-
+
/* Move to next Thread */
Thread = NewThread;
}
-
+
/* No more linked threads... Reacquire the Lock */
OldIrql = KeAcquireDispatcherDatabaseLock();
-
+
/* Now try to get a new thread from the list */
Thread = PspReaperList;
PspReaperList = NULL;
DPRINT("PspReaperList: %x\n", Thread);
-
+
/* Loop again if there is a new thread */
} while (Thread);
-
+
PspReaping = FALSE;
DPRINT("Done reaping\n");
KeReleaseDispatcherDatabaseLock(OldIrql);
}
+VOID
+STDCALL
+PspKillMostProcesses(VOID)
+{
+ PLIST_ENTRY current_entry;
+ PEPROCESS current;
+
+ ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess->UniqueProcessId);
+
+ /* Acquire the Active Process Lock */
+ ExAcquireFastMutex(&PspActiveProcessMutex);
+
+ /* Loop all processes on the list */
+ current_entry = PsActiveProcessHead.Flink;
+ while (current_entry != &PsActiveProcessHead)
+ {
+ current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
+ current_entry = current_entry->Flink;
+
+ if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId)
+ {
+ /* Terminate all the Threads in this Process */
+ PspTerminateProcessThreads(current, STATUS_SUCCESS);
+ }
+ }
+
+ /* Release the lock */
+ ExReleaseFastMutex(&PspActiveProcessMutex);
+}
+
VOID
STDCALL
PspTerminateProcessThreads(PEPROCESS Process,
{
PLIST_ENTRY CurrentEntry;
PETHREAD Thread, CurrentThread = PsGetCurrentThread();
-
+
CurrentEntry = Process->ThreadListHead.Flink;
while (CurrentEntry != &Process->ThreadListHead) {
-
+
/* Get the Current Thread */
Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
-
+
/* Move to the Next Thread */
CurrentEntry = CurrentEntry->Flink;
-
+
/* Make sure it's not the one we're in */
if (Thread != CurrentThread) {
-
+
/* Make sure it didn't already terminate */
- if (!Thread->HasTerminated) {
-
- Thread->HasTerminated = TRUE;
+ if (!Thread->Terminated) {
+
+ Thread->Terminated = TRUE;
/* Terminate it by APC */
PspTerminateThreadByPointer(Thread, ExitStatus);
}
}
-VOID
-STDCALL
+VOID
+STDCALL
PspDeleteProcess(PVOID ObjectBody)
{
PEPROCESS Process = (PEPROCESS)ObjectBody;
- DPRINT("PiDeleteProcess(ObjectBody %x)\n",Process);
+ DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
- /* Delete the CID Handle */
- if(Process->UniqueProcessId != NULL) {
-
- PsDeleteCidHandle(Process->UniqueProcessId, PsProcessType);
+ /* Remove it from the Active List */
+ ExAcquireFastMutex(&PspActiveProcessMutex);
+ RemoveEntryList(&Process->ActiveProcessLinks);
+ ExReleaseFastMutex(&PspActiveProcessMutex);
+
+ /* Delete the CID Handle */
+ if(Process->UniqueProcessId)
+ {
+ ExDestroyHandle(PspCidTable, Process->UniqueProcessId);
}
-
+
/* KDB hook */
KDB_DELETEPROCESS_HOOK(Process);
-
- /* Dereference the Token and release Memory Information */
- ObDereferenceObject(Process->Token);
+
+ /* Dereference the Token */
+ SeDeassignPrimaryToken(Process);
+
+ /* Release Memory Information */
MmReleaseMmInfo(Process);
-
+
/* Delete the W32PROCESS structure if there's one associated */
if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
}
-VOID
+VOID
STDCALL
PspDeleteThread(PVOID ObjectBody)
{
PETHREAD Thread = (PETHREAD)ObjectBody;
PEPROCESS Process = Thread->ThreadsProcess;
- DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
+ DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
/* Deassociate the Process */
Thread->ThreadsProcess = NULL;
/* Delete the CID Handle */
- if(Thread->Cid.UniqueThread != NULL) {
-
- PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
+ if(Thread->Cid.UniqueThread)
+ {
+ ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread);
}
-
+
/* Free the W32THREAD structure if present */
if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
- /* Release the Thread */
- KeReleaseThread(ETHREAD_TO_KTHREAD(Thread));
-
+ /* Release the Kernel Stack */
+ MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, FALSE);
+
/* Dereference the Process */
ObDereferenceObject(Process);
}
-
+
/*
* FUNCTION: Terminates the current thread
* See "Windows Internals" - Chapter 13, Page 50-53
PETHREAD CurrentThread;
BOOLEAN Last;
PEPROCESS CurrentProcess;
- SIZE_T Length = PAGE_SIZE;
- PVOID TebBlock;
PTERMINATION_PORT TerminationPort;
+ PTEB Teb;
+ KIRQL oldIrql;
+ PLIST_ENTRY FirstEntry, CurrentEntry;
+ PKAPC Apc;
- DPRINT("PsTerminateCurrentThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
+ DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
/* Get the Current Thread and Process */
CurrentThread = PsGetCurrentThread();
- CurrentThread->HasTerminated = TRUE;
CurrentProcess = CurrentThread->ThreadsProcess;
+ /* Set the Exit Status and Exit Time */
+ CurrentThread->ExitStatus = ExitStatus;
+ KeQuerySystemTime(&CurrentThread->ExitTime);
+
/* 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);
}
-
+
/* Lower to Passive Level */
KeLowerIrql(PASSIVE_LEVEL);
- /* Run Thread Notify Routines before we desintegrate the thread */
- PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
-
- /* Set the Exit Status and Exit Time */
- CurrentThread->ExitStatus = ExitStatus;
- KeQuerySystemTime(&CurrentThread->ExitTime);
-
/* Lock the Process before we modify its thread entries */
PsLockProcess(CurrentProcess, FALSE);
+ /* wake up the thread so we don't deadlock on PsLockProcess */
+ KeForceResumeThread(&CurrentThread->Tcb);
+
+ /* Run Thread Notify Routines before we desintegrate the thread */
+ PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
+
/* Remove the thread from the thread list of its process */
RemoveEntryList(&CurrentThread->ThreadListEntry);
Last = IsListEmpty(&CurrentProcess->ThreadListHead);
-
+
/* Set the last Thread Exit Status */
CurrentProcess->LastThreadExitStatus = ExitStatus;
-
+
if (Last) {
/* 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 the process has a debug port */
if (CurrentProcess->DebugPort) {
-
+
/* Notify the Debug API. TODO */
//Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
}
-
- /* Process the Termination Ports */
- TerminationPort = CurrentThread->TerminationPort;
- DPRINT("TerminationPort: %p\n", TerminationPort);
- while (TerminationPort) {
-
+
+ /* Process the Termination Ports */
+ while ((TerminationPort = CurrentThread->TerminationPort)) {
+
+ DPRINT("TerminationPort: %p\n", TerminationPort);
+
+ /* Get the next one */
+ CurrentThread->TerminationPort = TerminationPort->Next;
+
/* Send the LPC Message */
LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
-
+ ObDereferenceObject(TerminationPort->Port);
+
/* Free the Port */
ExFreePool(TerminationPort);
-
- /* Get the next one */
- TerminationPort = TerminationPort->Next;
- DPRINT("TerminationPort: %p\n", TerminationPort);
}
-
- /* Rundown Win32 Structures */DPRINT1("Terminating win32 thread 0x%x (proc 0x%x)\n", CurrentThread, CurrentThread->ThreadsProcess);
- PsTerminateWin32Thread(CurrentThread);if (Last) {DPRINT1("Terminating win32 process 0x%x (thread 0x%x)\n", CurrentProcess, CurrentThread); }
+
+ /* Rundown Win32 Structures */
+ PsTerminateWin32Thread(CurrentThread);
if (Last) PsTerminateWin32Process(CurrentProcess);
-
- /* Cancel I/O for the thread. */
- IoCancelThreadIo(CurrentThread);
-
+
/* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
//CmNotifyRunDown(CurrentThread);
-
+
/* Free the TEB */
- if(CurrentThread->Tcb.Teb) {
-
- DPRINT("Decommit teb at %p\n", CurrentThread->Tcb.Teb);
- ExAcquireFastMutex(&CurrentProcess->TebLock);
- TebBlock = MM_ROUND_DOWN(CurrentThread->Tcb.Teb, MM_VIRTMEM_GRANULARITY);
-
- ZwFreeVirtualMemory(NtCurrentProcess(),
- (PVOID *)&CurrentThread->Tcb.Teb,
- &Length,
- MEM_DECOMMIT);
-
- DPRINT("teb %p, TebBlock %p\n", CurrentThread->Tcb.Teb, TebBlock);
-
- if (TebBlock != CurrentProcess->TebBlock ||
- CurrentProcess->TebBlock == CurrentProcess->TebLastAllocated) {
-
- MmLockAddressSpace(&CurrentProcess->AddressSpace);
- MmReleaseMemoryAreaIfDecommitted(CurrentProcess, &CurrentProcess->AddressSpace, TebBlock);
- MmUnlockAddressSpace(&CurrentProcess->AddressSpace);
+ if((Teb = CurrentThread->Tcb.Teb))
+ {
+ /* Clean up the stack first, if requested */
+ if (Teb->FreeStackOnTermination)
+ {
+ ULONG Dummy = 0;
+ ZwFreeVirtualMemory(NtCurrentProcess(),
+ &Teb->DeallocationStack,
+ &Dummy,
+ MEM_RELEASE);
}
-
+
+ DPRINT("Decommit teb at %p\n", Teb);
+ MmDeleteTeb(CurrentProcess, Teb);
CurrentThread->Tcb.Teb = NULL;
- ExReleaseFastMutex(&CurrentProcess->TebLock);
}
-
- /* The last Thread shuts down the Process */if (Last) {DPRINT1("calling PspExitProcess\n");}
+
+ /* The last Thread shuts down the Process */
if (Last) PspExitProcess(CurrentProcess);
-
- /* Unlock the Process */DPRINT1("Released process 0x%x lock by 0x%x\n", CurrentProcess, PsGetCurrentThread());
+
+ /* Unlock the Process */
PsUnlockProcess(CurrentProcess);
-
+
+ /* Cancel I/O for the thread. */
+ IoCancelThreadIo(CurrentThread);
+
/* 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);
InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
NULL,
(PKPROCESS)CurrentThread);
-
+ KeLowerIrql(oldIrql);
+
/* Rundown Mutexes */
KeRundownThread();
+ /* Disable new APC Queuing, this is as far as we'll let them go */
+ KeDisableThreadApcQueueing(&CurrentThread->Tcb);
+
+ /* Flush the User APCs */
+ FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, UserMode);
+ if (FirstEntry != NULL)
+ {
+ CurrentEntry = FirstEntry;
+ do
+ {
+ /* Get the APC */
+ Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
+
+ /* Move to the next one */
+ CurrentEntry = CurrentEntry->Flink;
+
+ /* Rundown the APC or de-allocate it */
+ if (Apc->RundownRoutine)
+ {
+ /* Call its own routine */
+ (Apc->RundownRoutine)(Apc);
+ }
+ else
+ {
+ /* Do it ourselves */
+ ExFreePool(Apc);
+ }
+ }
+ while (CurrentEntry != FirstEntry);
+ }
+
+ /* Call the Lego routine */
+ if (CurrentThread->Tcb.LegoData) PspRunLegoRoutine(&CurrentThread->Tcb);
+
+ /* Flush the APC queue, which should be empty */
+ if ((FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, KernelMode)))
+ {
+ /* Bugcheck time */
+ KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT,
+ (ULONG_PTR)FirstEntry,
+ CurrentThread->Tcb.KernelApcDisable,
+ oldIrql,
+ 0);
+ }
+
/* Terminate the Thread from the Scheduler */
KeTerminateThread(0);
DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
KEBUGCHECK(0);
}
-VOID
+VOID
STDCALL
PsExitSpecialApc(PKAPC Apc,
PKNORMAL_ROUTINE* NormalRoutine,
PVOID* SystemArgument1,
PVOID* SystemArguemnt2)
{
- NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
-
- DPRINT("PsExitSpecialApc called: 0x%x\n", PsGetCurrentThread());
-
- /* Free the APC */
- ExFreePool(Apc);
-
- /* Terminate the Thread */
- PspExitThread(ExitStatus);
-
- /* we should never reach this point! */
- KEBUGCHECK(0);
+ DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x, '%.16s')\n",
+ PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
+
+ /* Don't do anything unless we are in User-Mode */
+ if (Apc->SystemArgument2)
+ {
+ NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
+
+ /* Free the APC */
+ ExFreePool(Apc);
+
+ /* Terminate the Thread */
+ PspExitThread(ExitStatus);
+
+ /* we should never reach this point! */
+ KEBUGCHECK(0);
+ }
}
-VOID
+VOID
STDCALL
PspExitNormalApc(PVOID NormalContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
- /* Not fully supported yet... must work out some issues that
- * I don't understand yet -- Alex
- */
- DPRINT1("APC2\n");
- PspExitThread((NTSTATUS)NormalContext);
-
- /* we should never reach this point! */
- KEBUGCHECK(0);
+ PKAPC Apc = (PKAPC)SystemArgument1;
+ PETHREAD Thread = PsGetCurrentThread();
+ NTSTATUS ExitStatus;
+
+ DPRINT("PspExitNormalApc called: 0x%x (proc: 0x%x, '%.16s')\n",
+ PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
+
+ /* 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);
+ }
+
+ /* 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,
+ PspExitNormalApc,
+ UserMode,
+ NormalContext);
+
+ /* Now insert the APC with the User-Mode Flag */
+ KeInsertQueueApc(Apc, Apc, (PVOID)UserMode, 2);
+
+ /* Forcefully resume the thread */
+ KeForceResumeThread(&Thread->Tcb);
}
/*
PspTerminateThreadByPointer(PETHREAD Thread,
NTSTATUS ExitStatus)
{
- PKAPC Apc;
-
+ PKAPC Apc;
+
DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
Thread, ExitStatus);
-
+
/* Check if we are already in the right context */
if (PsGetCurrentThread() == Thread) {
/* we should never reach this point! */
KEBUGCHECK(0);
}
-
+
/* Allocate the APC */
Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
-
+
/* Initialize a Kernel Mode APC to Kill the Thread */
KeInitializeApc(Apc,
&Thread->Tcb,
PspExitNormalApc,
KernelMode,
(PVOID)ExitStatus);
-
+
/* Insert it into the APC Queue */
KeInsertQueueApc(Apc,
Apc,
NULL,
2);
-
+
/* Forcefully resume the thread */
KeForceResumeThread(&Thread->Tcb);
}
-
-NTSTATUS
+
+NTSTATUS
STDCALL
PspExitProcess(PEPROCESS Process)
{
- DPRINT("PspExitProcess\n");
-
+ DPRINT("PspExitProcess 0x%x\n", Process);
+
PspRunCreateProcessNotifyRoutines(Process, FALSE);
-
- /* Remove it from the Active List */
- ExAcquireFastMutex(&PspActiveProcessMutex);
- RemoveEntryList(&Process->ProcessListEntry);
- ExReleaseFastMutex(&PspActiveProcessMutex);
-
+
+ PspDestroyQuotaBlock(Process);
+
/* close all handles associated with our process, this needs to be done
when the last thread still runs */
ObKillProcess(Process);
KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
+ /* release the keep-alive reference of the process object */
+ ObDereferenceObject(Process);
+
return(STATUS_SUCCESS);
}
-NTSTATUS
+NTSTATUS
STDCALL
NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
IN NTSTATUS ExitStatus)
{
NTSTATUS Status;
PEPROCESS Process;
-
+ PETHREAD CurrentThread;
+ BOOLEAN KillByHandle;
+
PAGED_CODE();
-
+
DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
ProcessHandle, ExitStatus);
-
+
+ KillByHandle = (ProcessHandle != NULL);
+
/* Get the Process Object */
- Status = ObReferenceObjectByHandle(ProcessHandle,
+ Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
PROCESS_TERMINATE,
PsProcessType,
KeGetPreviousMode(),
(PVOID*)&Process,
NULL);
if (!NT_SUCCESS(Status)) {
-
+
DPRINT1("Invalid handle to Process\n");
return(Status);
}
-
+
+ CurrentThread = PsGetCurrentThread();
+
PsLockProcess(Process, FALSE);
-
+
if(Process->ExitTime.QuadPart != 0)
{
PsUnlockProcess(Process);
+ ObDereferenceObject(Process);
return STATUS_PROCESS_IS_TERMINATING;
}
-
+
/* Terminate all the Process's Threads */
PspTerminateProcessThreads(Process, ExitStatus);
-
- /* Only master thread remains... kill it off */
- if (PsGetCurrentThread()->ThreadsProcess == Process) {
-
+
+ /* only kill the calling thread if it either passed a process handle or
+ NtCurrentProcess() */
+ if (KillByHandle) {
+
/* 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);
-
- PsUnlockProcess(Process);
-
- /* we can safely dereference the process because the current thread
- holds a reference to it until it gets reaped */
- 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 */
+ Process->ExitStatus = ExitStatus;
- PspExitThread(ExitStatus);
-
- /* we should never reach this point! */
- KEBUGCHECK(0);
- }
- else
- {
- /* unlock and dereference the process so the threads can kill themselves */
- PsUnlockProcess(Process);
- ObDereferenceObject(Process);
- DPRINT1("Terminated foreign process 0x%x\n", Process);
+ /* Only master thread remains... kill it off */
+ if (CurrentThread->ThreadsProcess == Process) {
+
+ /* mark our thread as terminating so attempts to terminate it, when
+ unlocking the process, fail */
+ CurrentThread->Terminated = TRUE;
+
+ PsUnlockProcess(Process);
+
+ /* we can safely dereference the process because the current thread
+ holds a reference to it until it gets reaped */
+ 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 */
+
+ PspExitThread(ExitStatus);
+
+ /* we should never reach this point! */
+ KEBUGCHECK(0);
+ }
}
-
+
+ /* unlock and dereference the process so the threads can kill themselves */
+ PsUnlockProcess(Process);
+ ObDereferenceObject(Process);
+
return(STATUS_SUCCESS);
}
-NTSTATUS
+NTSTATUS
STDCALL
NtTerminateThread(IN HANDLE ThreadHandle,
IN NTSTATUS ExitStatus)
{
PETHREAD Thread;
NTSTATUS Status;
-
+
PAGED_CODE();
+ /* Handle the special NULL case */
+ if (!ThreadHandle)
+ {
+ /* Check if we're the only thread left */
+ if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
+ {
+ /* This is invalid */
+ DPRINT1("Can't terminate self\n");
+ return STATUS_CANT_TERMINATE_SELF;
+ }
+ else
+ {
+ /* Use current handle */
+ ThreadHandle = NtCurrentThread();
+ }
+ }
+
/* Get the Thread Object */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_TERMINATE,
KeGetPreviousMode(),
(PVOID*)&Thread,
NULL);
- if (Status != STATUS_SUCCESS) {
-
+ 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;
}
-
+
/* Check to see if we're running in the same thread */
if (Thread != PsGetCurrentThread()) {
-
- /* This isn't our thread, check if it's terminated already */
- if (!Thread->HasTerminated) {
-
+
+ /* 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);
+
+ /* 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);
}
-
- /* Dereference the Thread and return */
- ObDereferenceObject(Thread);
+
return(STATUS_SUCCESS);
}
/*
* @implemented
*/
-NTSTATUS
+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;
}
-
+
/* Terminate it for real */
PspExitThread(ExitStatus);
-
+
/* we should never reach this point! */
KEBUGCHECK(0);
-
+
return(STATUS_SUCCESS);
}
-NTSTATUS
+NTSTATUS
STDCALL
NtRegisterThreadTerminatePort(HANDLE PortHandle)
{
PTERMINATION_PORT TerminationPort;
PVOID TerminationLpcPort;
PETHREAD Thread;
-
+
PAGED_CODE();
-
+
/* Get the Port */
Status = ObReferenceObjectByHandle(PortHandle,
PORT_ALL_ACCESS,
LpcPortObjectType,
KeGetPreviousMode(),
&TerminationLpcPort,
- NULL);
+ NULL);
if (!NT_SUCCESS(Status)) {
-
+
DPRINT1("Failed to reference Port\n");
return(Status);
}
-
+
/* Allocate the Port and make sure it suceeded */
- if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
- sizeof(PTERMINATION_PORT),
+ if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(PTERMINATION_PORT),
TAG('P', 's', 'T', '=')))) {
-
+
/* Associate the Port */
Thread = PsGetCurrentThread();
TerminationPort->Port = TerminationLpcPort;
/* Return success */
return(STATUS_SUCCESS);
-
+
} else {
-
+
/* Dereference and Fail */
ObDereferenceObject(TerminationPort);
return(STATUS_INSUFFICIENT_RESOURCES);