fixed a few race conditions during thread/process termination leading to dead-locks
[reactos.git] / reactos / ntoskrnl / ps / create.c
index 7713895..0aa5a92 100644 (file)
@@ -1,13 +1,13 @@
-/* $Id: create.c,v 1.43 2002/02/08 02:57:07 chorns Exp $
+/* $Id$
  *
- * COPYRIGHT:              See COPYING in the top level directory
- * PROJECT:                ReactOS kernel
- * FILE:                   ntoskrnl/ps/thread.c
- * PURPOSE:                Thread managment
- * PROGRAMMER:             David Welch (welch@mcmail.com)
- * REVISION HISTORY: 
- *               23/06/98: Created
- *               12/10/99: Phillip Susi:  Thread priorities, and APC work
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            ntoskrnl/ps/create.c
+ * PURPOSE:         Thread managment
+ * 
+ * PROGRAMMERS:     David Welch (welch@mcmail.com)
+ *                  Phillip Susi
+ *                  Skywing
  */
 
 /*
 
 /* INCLUDES ****************************************************************/
 
-#include <ddk/ntddk.h>
-#include <internal/ke.h>
-#include <internal/ob.h>
-#include <internal/ps.h>
-#include <internal/ob.h>
-#include <internal/id.h>
-#include <internal/dbg.h>
-
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
 
 /* GLOBAL *******************************************************************/
 
-static ULONG PiNextThreadUniqueId = 0;
-
-extern KSPIN_LOCK PiThreadListLock;
-extern ULONG PiNrThreads;
-
-extern LIST_ENTRY PiThreadListHead;
-
 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT    8
 
 static ULONG PiThreadNotifyRoutineCount = 0;
 static PCREATE_THREAD_NOTIFY_ROUTINE
 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
 
+ULONG
+STDCALL
+KeSuspendThread(PKTHREAD Thread);
 /* FUNCTIONS ***************************************************************/
 
-NTSTATUS STDCALL
-PsAssignImpersonationToken(PETHREAD Thread,
-                          HANDLE TokenHandle)
-{
-   PACCESS_TOKEN Token;
-   SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
-   NTSTATUS Status;
-   
-   if (TokenHandle != NULL)
-     {
-       Status = ObReferenceObjectByHandle(TokenHandle,
-                                          0,
-                                          SeTokenType,
-                                          UserMode,
-                                          (PVOID*)&Token,
-                                          NULL);
-       if (!NT_SUCCESS(Status))
-         {
-            return(Status);
-         }
-       ImpersonationLevel = Token->ImpersonationLevel;
-     }
-   else
-     {
-       Token = NULL;
-       ImpersonationLevel = 0;
-     }
-   
-   PsImpersonateClient(Thread,
-                      Token,
-                      0,
-                      0,
-                      ImpersonationLevel);
-   if (Token != NULL)
-     {
-       ObDereferenceObject(Token);
-     }
-   return(STATUS_SUCCESS);
-}
-
-VOID STDCALL
-PsRevertToSelf(VOID)
-{
-   PETHREAD Thread;
-
-   Thread = PsGetCurrentThread();
-
-   if (Thread->ActiveImpersonationInfo != 0)
-     {
-       ObDereferenceObject(Thread->ImpersonationInfo->Token);
-       Thread->ActiveImpersonationInfo = 0;
-     }
-}
-
-VOID STDCALL
-PsImpersonateClient(PETHREAD Thread,
-                   PACCESS_TOKEN Token,
-                   UCHAR b,
-                   UCHAR c,
-                   SECURITY_IMPERSONATION_LEVEL Level)
-{
-   if (Token == 0)
-     {
-       if (Thread->ActiveImpersonationInfo != 0)
-         {
-            Thread->ActiveImpersonationInfo = 0;
-            if (Thread->ImpersonationInfo->Token != NULL)
-              {
-                 ObDereferenceObject(Thread->ImpersonationInfo->Token);
-              }
-         }
-       return;
-     }
-   if (Thread->ActiveImpersonationInfo == 0 ||
-       Thread->ImpersonationInfo == NULL)
-     {
-       Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
-                                          sizeof(PS_IMPERSONATION_INFO));      
-     }
-   Thread->ImpersonationInfo->Level = Level;
-   Thread->ImpersonationInfo->Unknown2 = c;
-   Thread->ImpersonationInfo->Unknown1 = b;
-   Thread->ImpersonationInfo->Token = Token;
-   ObReferenceObjectByPointer(Token,
-                             0,
-                             SeTokenType,
-                             KernelMode);
-   Thread->ActiveImpersonationInfo = 1;
-}
-
-PACCESS_TOKEN
-PsReferenceEffectiveToken(PETHREAD Thread,
-                         PTOKEN_TYPE TokenType,
-                         PUCHAR b,
-                         PSECURITY_IMPERSONATION_LEVEL Level)
-{
-   PEPROCESS Process;
-   PACCESS_TOKEN Token;
-   
-   if (Thread->ActiveImpersonationInfo == 0)
-     {
-       Process = Thread->ThreadsProcess;
-       *TokenType = TokenPrimary;
-       *b = 0;
-       Token = Process->Token;
-     }
-   else
-     {
-       Token = Thread->ImpersonationInfo->Token;
-       *TokenType = TokenImpersonation;
-       *b = Thread->ImpersonationInfo->Unknown2;
-       *Level = Thread->ImpersonationInfo->Level;      
-     }
-   return(Token);
-}
-
-NTSTATUS STDCALL 
-NtImpersonateThread (IN HANDLE ThreadHandle,
-                    IN HANDLE ThreadToImpersonateHandle,
-                    IN PSECURITY_QUALITY_OF_SERVICE    
-                    SecurityQualityOfService)
-{
-   PETHREAD Thread;
-   PETHREAD ThreadToImpersonate;
-   NTSTATUS Status;
-   SE_SOME_STRUCT2 b;
-   
-   Status = ObReferenceObjectByHandle(ThreadHandle,
-                                     0,
-                                     PsThreadType,
-                                     UserMode,
-                                     (PVOID*)&Thread,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-   
-   Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
-                                     0,
-                                     PsThreadType,
-                                     UserMode,
-                                     (PVOID*)&ThreadToImpersonate,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       ObDereferenceObject(Thread);
-       return(Status);
-     }
-   
-   Status = SeCreateClientSecurity(ThreadToImpersonate,
-                                  SecurityQualityOfService,
-                                  0,
-                                  &b);
-   if (!NT_SUCCESS(Status))
-     {
-       ObDereferenceObject(Thread);
-       ObDereferenceObject(ThreadToImpersonate);
-       return(Status);
-     }
-   
-   SeImpersonateClient(&b, Thread);
-   if (b.Token != NULL)
-     {
-       ObDereferenceObject(b.Token);
-     }
-   return(STATUS_SUCCESS);
-}
-
-NTSTATUS STDCALL 
-NtOpenThreadToken(IN   HANDLE          ThreadHandle,  
-                 IN    ACCESS_MASK     DesiredAccess,  
-                 IN    BOOLEAN         OpenAsSelf,     
-                 OUT   PHANDLE         TokenHandle)
-{
-#if 0
-   PETHREAD Thread;
-   NTSTATUS Status;
-   PACCESS_TOKEN Token;
-   
-   Status = ObReferenceObjectByHandle(ThreadHandle,
-                                     0,
-                                     PsThreadType,
-                                     UserMode,
-                                     (PVOID*)&Thread,
-                                     NULL);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-   
-   Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
-   SepCreateImpersonationTokenDacl(Token);
-#endif
-   return(STATUS_UNSUCCESSFUL);
-}
-
-PACCESS_TOKEN STDCALL 
-PsReferenceImpersonationToken(PETHREAD Thread,
-                             PULONG Unknown1,
-                             PULONG Unknown2,
-                             SECURITY_IMPERSONATION_LEVEL* Level)
-{
-   if (Thread->ActiveImpersonationInfo == 0)
-     {
-       return(NULL);
-     }
-   
-   *Level = Thread->ImpersonationInfo->Level;
-   *Unknown1 = Thread->ImpersonationInfo->Unknown1;
-   *Unknown2 = Thread->ImpersonationInfo->Unknown2;
-   ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
-                             TOKEN_ALL_ACCESS,
-                             SeTokenType,
-                             KernelMode);
-   return(Thread->ImpersonationInfo->Token);
-}
-
-VOID STDCALL
-PiTimeoutThread(struct _KDPC *dpc,
-               PVOID Context,
-               PVOID arg1,
-               PVOID arg2)
-{
-   // wake up the thread, and tell it it timed out
-   NTSTATUS Status = STATUS_TIMEOUT;
-   
-   DPRINT("PiTimeoutThread()\n");
-   
-   KeRemoveAllWaitsThread((PETHREAD)Context, Status);
-}
-
 VOID
 PiBeforeBeginThread(CONTEXT c)
 {
-   DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
-   //KeReleaseSpinLock(&PiThreadListLock, PASSIVE_LEVEL);
    KeLowerIrql(PASSIVE_LEVEL);
 }
 
-#if 0
-VOID 
-PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
-{
-   NTSTATUS Ret;
-   
-   //   KeReleaseSpinLock(&PiThreadListLock,PASSIVE_LEVEL);
-   KeLowerIrql(PASSIVE_LEVEL);
-   Ret = StartRoutine(StartContext);
-   PsTerminateSystemThread(Ret);
-   KeBugCheck(0);
-}
-#endif
-
-VOID STDCALL
-PiDeleteThread(PVOID ObjectBody)
-{
-  KIRQL oldIrql;
-  PETHREAD Thread;
-  ULONG i;
-
-  DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
-
-  KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
-  Thread = (PETHREAD)ObjectBody;
-
-  for (i = 0; i < PiThreadNotifyRoutineCount; i++)
-    {
-      PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
-                              Thread->Cid.UniqueThread,
-                              FALSE);
-    }
-
-  DPRINT("Process %x(%d)\n",
-        Thread->ThreadsProcess,
-        ObGetReferenceCount(Thread->ThreadsProcess));
-  ObDereferenceObject(Thread->ThreadsProcess);
-  Thread->ThreadsProcess = NULL;
-  PiNrThreads--;
-  RemoveEntryList(&Thread->Tcb.ThreadListEntry);
-  HalReleaseTask(Thread);
-  KeReleaseSpinLock(&PiThreadListLock, oldIrql);
-  DPRINT("PiDeleteThread() finished\n");
-}
-
-VOID STDCALL
-PiCloseThread(PVOID ObjectBody,
-             ULONG HandleCount)
-{
-   DPRINT("PiCloseThread(ObjectBody %x)\n", ObjectBody);
-   DPRINT("ObGetReferenceCount(ObjectBody) %d "
-          "ObGetHandleCount(ObjectBody) %d\n",
-          ObGetReferenceCount(ObjectBody),
-          ObGetHandleCount(ObjectBody));
-}
-
 NTSTATUS
-PsInitializeThread(HANDLE ProcessHandle,
+PsInitializeThread(PEPROCESS Process,
                   PETHREAD* ThreadPtr,
-                  PHANDLE ThreadHandle,
-                  ACCESS_MASK  DesiredAccess,
-                  POBJECT_ATTRIBUTES ThreadAttributes,
+                  POBJECT_ATTRIBUTES ObjectAttributes,
+                  KPROCESSOR_MODE AccessMode,
                   BOOLEAN First)
 {
    PETHREAD Thread;
    NTSTATUS Status;
    KIRQL oldIrql;
-   PEPROCESS Process;
-   ULONG i;
    
-   /*
-    * Reference process
-    */
-   if (ProcessHandle != NULL)
-     {
-       Status = ObReferenceObjectByHandle(ProcessHandle,
-                                          PROCESS_CREATE_THREAD,
-                                          PsProcessType,
-                                          UserMode,
-                                          (PVOID*)&Process,
-                                          NULL);
-       if (Status != STATUS_SUCCESS)
-         {
-            DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
-            return(Status);
-         }
-       DPRINT( "Creating thread in process %x\n", Process );
-     }
-   else
+   PAGED_CODE();
+   
+   if (Process == NULL)
      {
        Process = PsInitialSystemProcess;
-       ObReferenceObjectByPointer(Process,
-                                  PROCESS_CREATE_THREAD,
-                                  PsProcessType,
-                                  UserMode);
      }
    
    /*
     * Create and initialize thread
     */
-   Status = ObCreateObject(ThreadHandle,
-                          DesiredAccess,
-                          ThreadAttributes,
+   Status = ObCreateObject(AccessMode,
                           PsThreadType,
+                          ObjectAttributes,
+                          KernelMode,
+                          NULL,
+                          sizeof(ETHREAD),
+                          0,
+                          0,
                           (PVOID*)&Thread);
    if (!NT_SUCCESS(Status))
      {
-       return(Status);
+        return(Status);
      }
 
-   DPRINT("Thread = %x\n",Thread);
-   
-   PiNrThreads++;
-   
-   KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
-   Thread->ThreadsProcess = Process;
    /*
-    * FIXME: What lock protects this?
+    * Reference process
     */
-   InsertTailList(&Thread->ThreadsProcess->ThreadListHead, 
-                 &Thread->Tcb.ProcessThreadListEntry);
-   InitializeListHead(&Thread->TerminationPortList);
+   ObReferenceObjectByPointer(Process,
+                              PROCESS_CREATE_THREAD,
+                              PsProcessType,
+                              KernelMode);
+
+   Thread->ThreadsProcess = Process;
+   Thread->Cid.UniqueThread = NULL;
+   Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
+
+   DPRINT("Thread = %x\n",Thread);
+
+   KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
+   InitializeListHead(&Thread->ActiveTimerListHead);
    KeInitializeSpinLock(&Thread->ActiveTimerListLock);
    InitializeListHead(&Thread->IrpList);
-   Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
-                                             &PiNextThreadUniqueId);
-   Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
-   Thread->DeadThread = 0;
-   Thread->Win32Thread = 0;
+   Thread->DeadThread = FALSE;
+   Thread->HasTerminated = FALSE;
+   Thread->Tcb.Win32Thread = NULL;
    DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
    
-   *ThreadPtr = Thread;
-   
-   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
-   InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
-   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
 
-   Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
+   Thread->Tcb.BasePriority = (CHAR)Process->Pcb.BasePriority;
    Thread->Tcb.Priority = Thread->Tcb.BasePriority;
 
-  for (i = 0; i < PiThreadNotifyRoutineCount; i++)
-    {
-      PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
-                              Thread->Cid.UniqueThread,
-                              TRUE);
-    }
+   /*
+    * Local Procedure Call facility (LPC)
+    */
+   KeInitializeSemaphore  (& Thread->LpcReplySemaphore, 0, LONG_MAX);
+   Thread->LpcReplyMessage = NULL;
+   Thread->LpcReplyMessageId = 0; /* not valid */
+   /* Thread->LpcReceiveMessageId = 0; */
+   Thread->LpcExitThreadCalled = FALSE;
+   Thread->LpcReceivedMsgIdValid = FALSE;
+
+   oldIrql = KeAcquireDispatcherDatabaseLock();
+   InsertTailList(&Process->ThreadListHead,
+                 &Thread->ThreadListEntry);
+   KeReleaseDispatcherDatabaseLock(oldIrql);
 
-  return(STATUS_SUCCESS);
+   *ThreadPtr = Thread;
+
+   return STATUS_SUCCESS;
 }
 
 
@@ -445,52 +132,80 @@ PsCreateTeb(HANDLE ProcessHandle,
            PETHREAD Thread,
            PINITIAL_TEB InitialTeb)
 {
-   MEMORY_BASIC_INFORMATION Info;
+   PEPROCESS Process;
    NTSTATUS Status;
    ULONG ByteCount;
    ULONG RegionSize;
    ULONG TebSize;
    PVOID TebBase;
    TEB Teb;
-   ULONG ResultLength;
+   
+   PAGED_CODE();
 
-   TebBase = (PVOID)0x7FFDE000;
-   TebSize = PAGESIZE;
+   TebSize = PAGE_SIZE;
 
-   while (TRUE)
+   if (NULL == Thread->ThreadsProcess)
      {
-       Status = NtQueryVirtualMemory(ProcessHandle,
-                                     TebBase,
-                                     MemoryBasicInformation,
-                                     &Info,
-                                     sizeof(Info),
-                                     &ResultLength);
-       if (!NT_SUCCESS(Status))
-         {
-            CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
-            KeBugCheck(0);
-         }
-       /* FIXME: Race between this and the above check */
-       if (Info.State == MEM_FREE)
-         {
-            /* The TEB must reside in user space */
-            Status = NtAllocateVirtualMemory(ProcessHandle,
-                                             &TebBase,
-                                             0,
-                                             &TebSize,
-                                             MEM_RESERVE | MEM_COMMIT,
-                                             PAGE_READWRITE);
-            if (NT_SUCCESS(Status))
-              {
-                 break;
-              }
-         }
-            
-       TebBase = TebBase - TebSize;
+       /* We'll be allocating a 64k block here and only use 4k of it, but this
+          path should almost never be taken. Actually, I never saw it was taken,
+          so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and
+          move on */
+       TebBase = NULL;
+       Status = ZwAllocateVirtualMemory(ProcessHandle,
+                                        &TebBase,
+                                        0,
+                                        &TebSize,
+                                        MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
+                                        PAGE_READWRITE);
+       if (! NT_SUCCESS(Status))
+         {
+           DPRINT1("Failed to allocate virtual memory for TEB\n");
+           return Status;
+         }
+     }
+   else
+     {
+       Process = Thread->ThreadsProcess;
+       PsLockProcess(Process, FALSE);
+       if (NULL == Process->TebBlock ||
+           Process->TebBlock == Process->TebLastAllocated)
+         {
+           Process->TebBlock = NULL;
+           RegionSize = MM_VIRTMEM_GRANULARITY;
+           Status = ZwAllocateVirtualMemory(ProcessHandle,
+                                            &Process->TebBlock,
+                                            0,
+                                            &RegionSize,
+                                            MEM_RESERVE | MEM_TOP_DOWN,
+                                            PAGE_READWRITE);
+           if (! NT_SUCCESS(Status))
+             {
+               PsUnlockProcess(Process);
+               DPRINT1("Failed to reserve virtual memory for TEB\n");
+               return Status;
+             }
+           Process->TebLastAllocated = (PVOID) ((char *) Process->TebBlock + RegionSize);
+         }
+       TebBase = (PVOID) ((char *) Process->TebLastAllocated - PAGE_SIZE);
+       Status = ZwAllocateVirtualMemory(ProcessHandle,
+                                        &TebBase,
+                                        0,
+                                        &TebSize,
+                                        MEM_COMMIT,
+                                        PAGE_READWRITE);
+       if (! NT_SUCCESS(Status))
+         {
+           DPRINT1("Failed to commit virtual memory for TEB\n");
+           return Status;
+         }
+       Process->TebLastAllocated = TebBase;
+       PsUnlockProcess(Process);
      }
 
    DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
+   ASSERT(NULL != TebBase && PAGE_SIZE <= TebSize);
 
+   RtlZeroMemory(&Teb, sizeof(TEB));
    /* set all pointers to and from the TEB */
    Teb.Tib.Self = TebBase;
    if (Thread->ThreadsProcess)
@@ -500,17 +215,31 @@ PsCreateTeb(HANDLE ProcessHandle,
    DPRINT("Teb.Peb %x\n", Teb.Peb);
    
    /* store stack information from InitialTeb */
-   if (InitialTeb != NULL)
-     {
-        Teb.Tib.StackBase = InitialTeb->StackBase;
-        Teb.Tib.StackLimit = InitialTeb->StackLimit;
-        Teb.DeallocationStack = InitialTeb->StackAllocate;
-     }
+   if(InitialTeb != NULL)
+   {
+    /* fixed-size stack */
+    if(InitialTeb->StackBase && InitialTeb->StackLimit)
+    {
+     Teb.Tib.StackBase = InitialTeb->StackBase;
+     Teb.Tib.StackLimit = InitialTeb->StackLimit;
+     Teb.DeallocationStack = InitialTeb->StackLimit;
+    }
+    /* expandable stack */
+    else
+    {
+     Teb.Tib.StackBase = InitialTeb->StackCommit;
+     Teb.Tib.StackLimit = InitialTeb->StackCommitMax;
+     Teb.DeallocationStack = InitialTeb->StackReserved;
+    }
+   }
 
    /* more initialization */
    Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
    Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
    Teb.CurrentLocale = PsDefaultThreadLocaleId;
+
+   /* Terminate the exception handler list */
+   Teb.Tib.ExceptionList = (PVOID)-1;
    
    DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
    
@@ -546,81 +275,233 @@ PsCreateTeb(HANDLE ProcessHandle,
 }
 
 
+VOID STDCALL
+LdrInitApcRundownRoutine(PKAPC Apc)
+{
+   ExFreePool(Apc);
+}
+
+
+VOID STDCALL
+LdrInitApcKernelRoutine(PKAPC Apc,
+                       PKNORMAL_ROUTINE* NormalRoutine,
+                       PVOID* NormalContext,
+                       PVOID* SystemArgument1,
+                       PVOID* SystemArgument2)
+{
+  ExFreePool(Apc);
+}
+
+
 NTSTATUS STDCALL
-NtCreateThread (PHANDLE                ThreadHandle,
-               ACCESS_MASK             DesiredAccess,
-               POBJECT_ATTRIBUTES      ObjectAttributes,
-               HANDLE                  ProcessHandle,
-               PCLIENT_ID              Client,
-               PCONTEXT                ThreadContext,
-               PINITIAL_TEB            InitialTeb,
-               BOOLEAN CreateSuspended)
+NtCreateThread(OUT PHANDLE ThreadHandle,
+              IN ACCESS_MASK DesiredAccess,
+              IN POBJECT_ATTRIBUTES ObjectAttributes  OPTIONAL,
+              IN HANDLE ProcessHandle,
+              OUT PCLIENT_ID ClientId,
+              IN PCONTEXT ThreadContext,
+              IN PINITIAL_TEB InitialTeb,
+              IN BOOLEAN CreateSuspended)
 {
-   PETHREAD Thread;
-   PTEB  TebBase;
-   NTSTATUS Status;
-   
-   DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
-         ThreadHandle,ThreadContext);
-   
-   Status = PsInitializeThread(ProcessHandle,
-                              &Thread,
-                              ThreadHandle,
-                              DesiredAccess,
-                              ObjectAttributes,
-                              FALSE);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-   
-   Status = Ke386InitThreadWithContext(&Thread->Tcb,
-                                      ThreadContext);
-   if (!NT_SUCCESS(Status))
-     {
-       return(Status);
-     }
-   
-   Status = PsCreateTeb(ProcessHandle,
-                        &TebBase,
-                        Thread,
-                        InitialTeb);
-   if (!NT_SUCCESS(Status))
-     {
-        return(Status);
-     }
-   
-   /* Attention: TebBase is in user memory space */
-   Thread->Tcb.Teb = TebBase;
+  HANDLE hThread;
+  CONTEXT SafeContext;
+  INITIAL_TEB SafeInitialTeb;
+  PEPROCESS Process;
+  PETHREAD Thread;
+  PTEB TebBase;
+  PKAPC LdrInitApc;
+  KIRQL oldIrql;
+  KPROCESSOR_MODE PreviousMode;
+  NTSTATUS Status = STATUS_SUCCESS;
+  
+  PAGED_CODE();
+  
+  if(ThreadContext == NULL)
+  {
+    return STATUS_INVALID_PARAMETER;
+  }
+  
+  PreviousMode = ExGetPreviousMode();
+
+  if(PreviousMode != KernelMode)
+  {
+    _SEH_TRY
+    {
+      ProbeForWrite(ThreadHandle,
+                    sizeof(HANDLE),
+                    sizeof(ULONG));
+      if(ClientId != NULL)
+      {
+        ProbeForWrite(ClientId,
+                      sizeof(CLIENT_ID),
+                      sizeof(ULONG));
+      }
+      ProbeForRead(ThreadContext,
+                   sizeof(CONTEXT),
+                   sizeof(ULONG));
+      SafeContext = *ThreadContext;
+      ThreadContext = &SafeContext;
+      ProbeForRead(InitialTeb,
+                   sizeof(INITIAL_TEB),
+                   sizeof(ULONG));
+      SafeInitialTeb = *InitialTeb;
+      InitialTeb = &SafeInitialTeb;
+    }
+    _SEH_HANDLE
+    {
+      Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
 
-   Thread->StartAddress=NULL;
+    if(!NT_SUCCESS(Status))
+    {
+      return Status;
+    }
+  }
+
+  DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
+        ThreadHandle,ThreadContext);
+
+  Status = ObReferenceObjectByHandle(ProcessHandle,
+                                     PROCESS_CREATE_THREAD,
+                                     PsProcessType,
+                                     PreviousMode,
+                                     (PVOID*)&Process,
+                                     NULL);
+  if(!NT_SUCCESS(Status))
+  {
+    return(Status);
+  }
+  
+  Status = PsLockProcess(Process, FALSE);
+  if (!NT_SUCCESS(Status))
+    {
+       ObDereferenceObject(Process);
+       return(Status);
+    }
 
-   if (Client != NULL)
-     {
-       *Client=Thread->Cid;
-     }
-   
-   /*
-    * Maybe send a message to the process's debugger
-    */
-   DbgkCreateThread((PVOID)ThreadContext->Eip);
-   
-   /*
-    * Start the thread running
-    */
-   if (!CreateSuspended)
-     {
-       DPRINT("Not creating suspended\n");
-       PsUnblockThread(Thread, NULL);
-     }
-   else
-     {
-       KeBugCheck(0);
-     }
-   return(STATUS_SUCCESS);
+  if(Process->ExitTime.QuadPart != 0)
+    {
+       PsUnlockProcess(Process);
+       return STATUS_PROCESS_IS_TERMINATING;
+    }
+
+  PsUnlockProcess(Process);
+
+  Status = PsInitializeThread(Process,
+                             &Thread,
+                             ObjectAttributes,
+                             PreviousMode,
+                             FALSE);
+
+  ObDereferenceObject(Process);
+  
+  if (!NT_SUCCESS(Status))
+    {
+      return(Status);
+    }
+  
+  /* create a client id handle */
+  Status = PsCreateCidHandle(Thread, PsThreadType, &Thread->Cid.UniqueThread);
+  if (!NT_SUCCESS(Status))
+    {
+      ObDereferenceObject(Thread);
+      return Status;
+    }
+
+  Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
+  if (!NT_SUCCESS(Status))
+    {
+      PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
+      ObDereferenceObject(Thread);
+      return(Status);
+    }
+
+  Status = PsCreateTeb(ProcessHandle,
+                      &TebBase,
+                      Thread,
+                      InitialTeb);
+  if (!NT_SUCCESS(Status))
+    {
+      PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
+      ObDereferenceObject(Thread);
+      return(Status);
+    }
+  Thread->Tcb.Teb = TebBase;
+
+  Thread->StartAddress = NULL;
+
+  /*
+   * Maybe send a message to the process's debugger
+   */
+  DbgkCreateThread((PVOID)ThreadContext->Eip);
+
+  /*
+   * First, force the thread to be non-alertable for user-mode alerts.
+   */
+  Thread->Tcb.Alertable = FALSE;
+
+  /*
+   * If the thread is to be created suspended then queue an APC to
+   * do the suspend before we run any userspace code.
+   */
+  if (CreateSuspended)
+    {
+      KeSuspendThread(&Thread->Tcb);
+    }
+
+  /*
+   * Queue an APC to the thread that will execute the ntdll startup
+   * routine.
+   */
+  LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
+  KeInitializeApc(LdrInitApc, &Thread->Tcb, OriginalApcEnvironment, LdrInitApcKernelRoutine,
+                 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(), 
+                 UserMode, NULL);
+  KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT);
+  
+  /* 
+   * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE. 
+   * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call,
+   * doing so is a blatant and erronous hack.
+   */
+  Thread->Tcb.ApcState.UserApcPending = TRUE;
+  Thread->Tcb.Alerted[KernelMode] = TRUE;
+
+  oldIrql = KeAcquireDispatcherDatabaseLock ();
+  KiUnblockThread(&Thread->Tcb, NULL, 0);
+  KeReleaseDispatcherDatabaseLock(oldIrql);
+
+  Status = ObInsertObject((PVOID)Thread,
+                         NULL,
+                         DesiredAccess,
+                         0,
+                         NULL,
+                         &hThread);
+  if(NT_SUCCESS(Status))
+  {
+    _SEH_TRY
+    {
+      if(ClientId != NULL)
+      {
+        *ClientId = Thread->Cid;
+      }
+      *ThreadHandle = hThread;
+    }
+    _SEH_HANDLE
+    {
+      Status = _SEH_GetExceptionCode();
+    }
+    _SEH_END;
+  }
+  
+  return Status;
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 PsCreateSystemThread(PHANDLE ThreadHandle,
                     ACCESS_MASK DesiredAccess,
@@ -648,46 +529,90 @@ PsCreateSystemThread(PHANDLE ThreadHandle,
 {
    PETHREAD Thread;
    NTSTATUS Status;
+   KIRQL oldIrql;
+   
+   PAGED_CODE();
    
    DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
            ThreadHandle,ProcessHandle);
    
-   Status = PsInitializeThread(ProcessHandle,
+   Status = PsInitializeThread(NULL,
                               &Thread,
-                              ThreadHandle,
-                              DesiredAccess,
                               ObjectAttributes,
+                              KernelMode,
                               FALSE);
    if (!NT_SUCCESS(Status))
      {
        return(Status);
      }
-   
-   Thread->StartAddress=StartRoutine;
-   Status = Ke386InitThread(&Thread->Tcb,
-                           StartRoutine,
-                           StartContext);
+
+    /* Set the thread as a system thread */
+    Thread->SystemThread = TRUE;
+     
+   Status = PsCreateCidHandle(Thread,
+                              PsThreadType,
+                              &Thread->Cid.UniqueThread);
+   if(!NT_SUCCESS(Status))
+   {
+     ObDereferenceObject(Thread);
+     return Status;
+   }
+   
+   Thread->StartAddress = StartRoutine;
+   Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
    if (!NT_SUCCESS(Status))
      {
-       return(Status);
+        ObDereferenceObject(Thread);
+        return(Status);
      }
 
-   if (ClientId!=NULL)
+   if (ClientId != NULL)
      {
        *ClientId=Thread->Cid;
      }
 
-   PsUnblockThread(Thread, NULL);
+   oldIrql = KeAcquireDispatcherDatabaseLock ();
+   KiUnblockThread(&Thread->Tcb, NULL, 0);
+   KeReleaseDispatcherDatabaseLock(oldIrql);
+   
+   Status = ObInsertObject((PVOID)Thread,
+                          NULL,
+                          DesiredAccess,
+                          0,
+                          NULL,
+                          ThreadHandle);
    
-   return(STATUS_SUCCESS);
+   /* don't dereference the thread, the initial reference serves as the keep-alive
+      reference which will be removed by the thread reaper */
+   
+   return Status;
+}
+
+
+VOID STDCALL
+PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread,
+                                BOOLEAN Create)
+{
+  ULONG i;
+  CLIENT_ID Cid = CurrentThread->Cid;
+
+  for (i = 0; i < PiThreadNotifyRoutineCount; i++)
+    {
+      PiThreadNotifyRoutine[i](Cid.UniqueProcess, Cid.UniqueThread, Create);
+    }
 }
 
 
+/*
+ * @implemented
+ */
 NTSTATUS STDCALL
 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
 {
   if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
-    return(STATUS_INSUFFICIENT_RESOURCES);
+    {
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
 
   PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
   PiThreadNotifyRoutineCount++;