release the keep-alive reference of the process object
[reactos.git] / reactos / ntoskrnl / ps / kill.c
index 5572bec..6e4b8a7 100644 (file)
@@ -3,7 +3,7 @@
  * 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')
 
-LIST_ENTRY PspReaperListHead;
+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;
-    PLIST_ENTRY ListEntry;
+    PETHREAD Thread, NewThread;
+
     /* Acquire lock */
     DPRINT("Evil reaper running!!\n");
     OldIrql = KeAcquireDispatcherDatabaseLock();
-    
-    /* Loop the reap list */
-    while((ListEntry = RemoveHeadList(&PspReaperListHead)) != &PspReaperListHead) {
-        
-        /* Get the Current Thread to Terminate */
-        Thread = CONTAINING_RECORD(ListEntry, ETHREAD, TerminationPortList);
-    
+
+    /* 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);
-        /* Remove the Reference */
-        ObDereferenceObject(Thread);
-        
-        /* Reacquire the Lock */
+
+        /* 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,
@@ -65,86 +115,91 @@ 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);
-                
-                /* Unsuspend it */
-                KeForceResumeThread(&Thread->Tcb);
             }
         }
-        
-        /* Move to the Next Thread */
-        CurrentEntry = CurrentEntry->Flink;
     }
 }
 
-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
@@ -156,125 +211,176 @@ PspExitThread(NTSTATUS ExitStatus)
     PETHREAD CurrentThread;
     BOOLEAN Last;
     PEPROCESS CurrentProcess;
-    SIZE_T Length = PAGE_SIZE;
-    PVOID TebBlock;
-    PLIST_ENTRY CurrentEntry;
     PTERMINATION_PORT TerminationPort;
+    PTEB Teb;
+    KIRQL oldIrql;
+    PLIST_ENTRY FirstEntry, CurrentEntry;
+    PKAPC Apc;
 
-    DPRINT("PsTerminateCurrentThread(ExitStatus %x)\n", ExitStatus);
+    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);
-
     /* 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;
-    
-    /* Unlock the Process */
-    PsUnlockProcess(CurrentProcess);
-    
+
+    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 */  
-    while ((CurrentEntry = RemoveHeadList(&CurrentThread->TerminationPortList)) != 
-                           &CurrentThread->TerminationPortList) {
-        
-        /* Get the Termination Port */
-        TerminationPort = CONTAINING_RECORD(CurrentEntry,
-                                            TERMINATION_PORT,
-                                            Links);
-        
+
+    /* 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);
     }
-      
+
     /* Rundown Win32 Structures */
     PsTerminateWin32Thread(CurrentThread);
     if (Last) PsTerminateWin32Process(CurrentProcess);
-   
-    /* Cancel I/O for the thread. */
-    IoCancelThreadIo(CurrentThread);
-   
-    /* Rundown Timers */
-    ExTimerRundown();
-    KeCancelTimer(&CurrentThread->Tcb.Timer);
-   
+
     /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
     //CmNotifyRunDown(CurrentThread);
-   
-    /* Rundown Mutexes */
-    KeRundownThread();
-     
+
     /* 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);
     }
-   
-    /* Set the Exit Status and Exit Time */
-    CurrentThread->ExitStatus = ExitStatus;
-    KeQuerySystemTime((PLARGE_INTEGER)&CurrentThread->ExitTime);
-       
+
+    /* The last Thread shuts down the Process */
+    if (Last) PspExitProcess(CurrentProcess);
+
+    /* 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);
-   
-    /* The last Thread shuts down the Process */
-    if (Last) PspExitProcess(CurrentProcess);
+    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);
@@ -282,7 +388,7 @@ PspExitThread(NTSTATUS ExitStatus)
     KEBUGCHECK(0);
 }
 
-VOID 
+VOID
 STDCALL
 PsExitSpecialApc(PKAPC Apc,
                  PKNORMAL_ROUTINE* NormalRoutine,
@@ -290,26 +396,71 @@ PsExitSpecialApc(PKAPC Apc,
                  PVOID* SystemArgument1,
                  PVOID* SystemArguemnt2)
 {
-    NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
-    
-    /* Free the APC */
-    ExFreePool(Apc);
-    
-    /* Terminate the Thread */
-    PspExitThread(ExitStatus);
+    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);
+    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);
 }
 
 /*
@@ -320,21 +471,24 @@ STDCALL
 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) {
-    
+
         /* Directly terminate the thread */
         PspExitThread(ExitStatus);
+
+        /* 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,
@@ -344,98 +498,151 @@ PspTerminateThreadByPointer(PETHREAD Thread,
                     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);
     }
-    
-    if(Process->ExitTime.QuadPart) {
-        
-        DPRINT1("Process has an exit time!\n");
-        KeLeaveCriticalRegion();
-        return STATUS_PROCESS_IS_TERMINATING;
+
+    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);
 
-    /* Save the Exit Time */
-    KeQuerySystemTime(&Process->ExitTime);
-            
-    /* Only master thread remains... kill it off */
-    if (PsGetCurrentThread()->ThreadsProcess == Process) {
-        
-        /* Unlock and dereference */
-        ObDereferenceObject(Process);
-        PspExitThread(ExitStatus);
-        return(STATUS_SUCCESS);
+    /* 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);
+        Process->ExitStatus = ExitStatus;
+
+        /* 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);
+        }
     }
-    
-    /* If we took this path instead, then do the same as above */ 
+
+    /* 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,
@@ -443,101 +650,126 @@ NtTerminateThread(IN HANDLE ThreadHandle,
                                        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);
-             
-             /* Resume it */
-             KeForceResumeThread(&Thread->Tcb);
-         }
-        
+        }
+
+        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)
 {
     NTSTATUS Status;
     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;
-        InsertTailList(&PsGetCurrentThread()->TerminationPortList, &TerminationPort->Links);
+        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);