1 /* $Id: create.c,v 1.79 2004/08/15 16:39:10 chorns Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/thread.c
6 * PURPOSE: Thread managment
7 * PROGRAMMER: David Welch (welch@mcmail.com)
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
11 * 09/08/03: Skywing: ThreadEventPair support (delete)
17 * All of the routines that manipulate the thread queue synchronize on
22 /* INCLUDES ****************************************************************/
26 #include <internal/debug.h>
28 /* GLOBAL *******************************************************************/
30 static ULONG PiNextThreadUniqueId
= 0;
32 extern KSPIN_LOCK PiThreadListLock
;
33 extern ULONG PiNrThreads
;
35 extern LIST_ENTRY PiThreadListHead
;
37 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
39 static ULONG PiThreadNotifyRoutineCount
= 0;
40 static PCREATE_THREAD_NOTIFY_ROUTINE
41 PiThreadNotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
43 /* FUNCTIONS ***************************************************************/
49 PsAssignImpersonationToken(PETHREAD Thread
,
53 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
;
56 if (TokenHandle
!= NULL
)
58 Status
= ObReferenceObjectByHandle(TokenHandle
,
64 if (!NT_SUCCESS(Status
))
68 ImpersonationLevel
= Token
->ImpersonationLevel
;
73 ImpersonationLevel
= 0;
76 PsImpersonateClient(Thread
,
83 ObDereferenceObject(Token
);
86 return(STATUS_SUCCESS
);
96 PsRevertThreadToSelf(PsGetCurrentThread());
104 PsRevertThreadToSelf(
108 if (Thread
->ActiveImpersonationInfo
== TRUE
)
110 ObDereferenceObject (Thread
->ImpersonationInfo
->Token
);
111 Thread
->ActiveImpersonationInfo
= FALSE
;
119 PsImpersonateClient (IN PETHREAD Thread
,
120 IN PACCESS_TOKEN Token
,
121 IN BOOLEAN CopyOnOpen
,
122 IN BOOLEAN EffectiveOnly
,
123 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
127 if (Thread
->ActiveImpersonationInfo
== TRUE
)
129 Thread
->ActiveImpersonationInfo
= FALSE
;
130 if (Thread
->ImpersonationInfo
->Token
!= NULL
)
132 ObDereferenceObject (Thread
->ImpersonationInfo
->Token
);
138 if (Thread
->ImpersonationInfo
== NULL
)
140 Thread
->ImpersonationInfo
= ExAllocatePool (NonPagedPool
,
141 sizeof(PS_IMPERSONATION_INFO
));
144 Thread
->ImpersonationInfo
->Level
= ImpersonationLevel
;
145 Thread
->ImpersonationInfo
->CopyOnOpen
= CopyOnOpen
;
146 Thread
->ImpersonationInfo
->EffectiveOnly
= EffectiveOnly
;
147 Thread
->ImpersonationInfo
->Token
= Token
;
148 ObReferenceObjectByPointer (Token
,
152 Thread
->ActiveImpersonationInfo
= TRUE
;
157 PsReferenceEffectiveToken(PETHREAD Thread
,
158 PTOKEN_TYPE TokenType
,
159 PBOOLEAN EffectiveOnly
,
160 PSECURITY_IMPERSONATION_LEVEL Level
)
165 if (Thread
->ActiveImpersonationInfo
== FALSE
)
167 Process
= Thread
->ThreadsProcess
;
168 *TokenType
= TokenPrimary
;
169 *EffectiveOnly
= FALSE
;
170 Token
= Process
->Token
;
174 Token
= Thread
->ImpersonationInfo
->Token
;
175 *TokenType
= TokenImpersonation
;
176 *EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
177 *Level
= Thread
->ImpersonationInfo
->Level
;
184 NtImpersonateThread(IN HANDLE ThreadHandle
,
185 IN HANDLE ThreadToImpersonateHandle
,
186 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
)
188 SECURITY_CLIENT_CONTEXT ClientContext
;
190 PETHREAD ThreadToImpersonate
;
193 Status
= ObReferenceObjectByHandle (ThreadHandle
,
199 if (!NT_SUCCESS (Status
))
204 Status
= ObReferenceObjectByHandle (ThreadToImpersonateHandle
,
208 (PVOID
*)&ThreadToImpersonate
,
210 if (!NT_SUCCESS(Status
))
212 ObDereferenceObject (Thread
);
216 Status
= SeCreateClientSecurity (ThreadToImpersonate
,
217 SecurityQualityOfService
,
220 if (!NT_SUCCESS(Status
))
222 ObDereferenceObject (ThreadToImpersonate
);
223 ObDereferenceObject (Thread
);
227 SeImpersonateClient (&ClientContext
,
229 if (ClientContext
.Token
!= NULL
)
231 ObDereferenceObject (ClientContext
.Token
);
234 ObDereferenceObject (ThreadToImpersonate
);
235 ObDereferenceObject (Thread
);
237 return STATUS_SUCCESS
;
244 NtOpenThreadToken (IN HANDLE ThreadHandle
,
245 IN ACCESS_MASK DesiredAccess
,
246 IN BOOLEAN OpenAsSelf
,
247 OUT PHANDLE TokenHandle
)
253 Status
= ObReferenceObjectByHandle (ThreadHandle
,
259 if (!NT_SUCCESS(Status
))
266 Token
= Thread
->ThreadsProcess
->Token
;
270 if (Thread
->ActiveImpersonationInfo
== FALSE
)
272 ObDereferenceObject (Thread
);
273 return STATUS_NO_TOKEN
;
276 Token
= Thread
->ImpersonationInfo
->Token
;
281 ObDereferenceObject (Thread
);
282 return STATUS_NO_TOKEN
;
285 Status
= ObCreateHandle (PsGetCurrentProcess(),
291 ObDereferenceObject (Thread
);
300 PACCESS_TOKEN STDCALL
301 PsReferenceImpersonationToken(IN PETHREAD Thread
,
302 OUT PBOOLEAN CopyOnOpen
,
303 OUT PBOOLEAN EffectiveOnly
,
304 OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
306 if (Thread
->ActiveImpersonationInfo
== FALSE
)
311 *ImpersonationLevel
= Thread
->ImpersonationInfo
->Level
;
312 *CopyOnOpen
= Thread
->ImpersonationInfo
->CopyOnOpen
;
313 *EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
314 ObReferenceObjectByPointer (Thread
->ImpersonationInfo
->Token
,
319 return Thread
->ImpersonationInfo
->Token
;
327 PsDereferenceImpersonationToken(
328 IN PACCESS_TOKEN ImpersonationToken
339 PsDereferencePrimaryToken(
340 IN PACCESS_TOKEN PrimaryToken
351 PsDisableImpersonation(
353 IN PSE_IMPERSONATION_STATE ImpersonationState
365 PsRestoreImpersonation(
367 IN PSE_IMPERSONATION_STATE ImpersonationState
374 PiBeforeBeginThread(CONTEXT c
)
376 KeLowerIrql(PASSIVE_LEVEL
);
381 PiDeleteThread(PVOID ObjectBody
)
386 Thread
= (PETHREAD
)ObjectBody
;
388 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody
);
390 ObDereferenceObject(Thread
->ThreadsProcess
);
391 Thread
->ThreadsProcess
= NULL
;
393 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
395 RemoveEntryList(&Thread
->Tcb
.ThreadListEntry
);
396 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
398 KeReleaseThread(Thread
);
399 DPRINT("PiDeleteThread() finished\n");
404 PsInitializeThread(HANDLE ProcessHandle
,
406 PHANDLE ThreadHandle
,
407 ACCESS_MASK DesiredAccess
,
408 POBJECT_ATTRIBUTES ThreadAttributes
,
419 if (ProcessHandle
!= NULL
)
421 Status
= ObReferenceObjectByHandle(ProcessHandle
,
422 PROCESS_CREATE_THREAD
,
427 if (Status
!= STATUS_SUCCESS
)
429 DPRINT("Failed at %s:%d\n",__FILE__
,__LINE__
);
432 DPRINT( "Creating thread in process %x\n", Process
);
436 Process
= PsInitialSystemProcess
;
437 ObReferenceObjectByPointer(Process
,
438 PROCESS_CREATE_THREAD
,
444 * Create and initialize thread
446 Status
= ObCreateObject(UserMode
,
455 if (!NT_SUCCESS(Status
))
460 Status
= ObInsertObject ((PVOID
)Thread
,
466 if (!NT_SUCCESS(Status
))
468 ObDereferenceObject (Thread
);
472 DPRINT("Thread = %x\n",Thread
);
476 KeInitializeThread(&Process
->Pcb
, &Thread
->Tcb
, First
);
477 Thread
->ThreadsProcess
= Process
;
479 * FIXME: What lock protects this?
481 InsertTailList(&Thread
->ThreadsProcess
->ThreadListHead
,
482 &Thread
->Tcb
.ProcessThreadListEntry
);
483 InitializeListHead(&Thread
->TerminationPortList
);
484 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
485 InitializeListHead(&Thread
->IrpList
);
486 Thread
->Cid
.UniqueThread
= (HANDLE
)InterlockedIncrement(
487 (LONG
*)&PiNextThreadUniqueId
);
488 Thread
->Cid
.UniqueProcess
= (HANDLE
)Thread
->ThreadsProcess
->UniqueProcessId
;
489 Thread
->DeadThread
= 0;
490 Thread
->Win32Thread
= 0;
491 DPRINT("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
495 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
496 InsertTailList(&PiThreadListHead
, &Thread
->Tcb
.ThreadListEntry
);
497 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
499 Thread
->Tcb
.BasePriority
= (CHAR
)Thread
->ThreadsProcess
->Pcb
.BasePriority
;
500 Thread
->Tcb
.Priority
= Thread
->Tcb
.BasePriority
;
503 * Local Procedure Call facility (LPC)
505 KeInitializeSemaphore (& Thread
->LpcReplySemaphore
, 0, LONG_MAX
);
506 Thread
->LpcReplyMessage
= NULL
;
507 Thread
->LpcReplyMessageId
= 0; /* not valid */
508 /* Thread->LpcReceiveMessageId = 0; */
509 Thread
->LpcExitThreadCalled
= FALSE
;
510 Thread
->LpcReceivedMsgIdValid
= FALSE
;
512 return(STATUS_SUCCESS
);
517 PsCreateTeb(HANDLE ProcessHandle
,
520 PUSER_STACK UserStack
)
522 MEMORY_BASIC_INFORMATION Info
;
531 TebBase
= (PVOID
)0x7FFDE000;
536 Status
= ZwQueryVirtualMemory(ProcessHandle
,
538 MemoryBasicInformation
,
542 if (!NT_SUCCESS(Status
))
544 CPRINT("ZwQueryVirtualMemory (Status %x)\n", Status
);
547 /* FIXME: Race between this and the above check */
548 if (Info
.State
== MEM_FREE
)
550 /* The TEB must reside in user space */
551 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
555 MEM_RESERVE
| MEM_COMMIT
,
557 if (NT_SUCCESS(Status
))
563 TebBase
= (char*)TebBase
- TebSize
;
566 DPRINT ("TebBase %p TebSize %lu\n", TebBase
, TebSize
);
568 RtlZeroMemory(&Teb
, sizeof(TEB
));
569 /* set all pointers to and from the TEB */
570 Teb
.Tib
.Self
= TebBase
;
571 if (Thread
->ThreadsProcess
)
573 Teb
.Peb
= Thread
->ThreadsProcess
->Peb
; /* No PEB yet!! */
575 DPRINT("Teb.Peb %x\n", Teb
.Peb
);
577 /* store stack information from UserStack */
578 if(UserStack
!= NULL
)
580 /* fixed-size stack */
581 if(UserStack
->FixedStackBase
&& UserStack
->FixedStackLimit
)
583 Teb
.Tib
.StackBase
= UserStack
->FixedStackBase
;
584 Teb
.Tib
.StackLimit
= UserStack
->FixedStackLimit
;
585 Teb
.DeallocationStack
= UserStack
->FixedStackLimit
;
587 /* expandable stack */
590 Teb
.Tib
.StackBase
= UserStack
->ExpandableStackBase
;
591 Teb
.Tib
.StackLimit
= UserStack
->ExpandableStackLimit
;
592 Teb
.DeallocationStack
= UserStack
->ExpandableStackBottom
;
596 /* more initialization */
597 Teb
.Cid
.UniqueThread
= Thread
->Cid
.UniqueThread
;
598 Teb
.Cid
.UniqueProcess
= Thread
->Cid
.UniqueProcess
;
599 Teb
.CurrentLocale
= PsDefaultThreadLocaleId
;
601 /* Terminate the exception handler list */
602 Teb
.Tib
.ExceptionList
= (PVOID
)-1;
604 DPRINT("sizeof(TEB) %x\n", sizeof(TEB
));
606 /* write TEB data into teb page */
607 Status
= NtWriteVirtualMemory(ProcessHandle
,
613 if (!NT_SUCCESS(Status
))
616 DPRINT1 ("Writing TEB failed!\n");
619 NtFreeVirtualMemory(ProcessHandle
,
629 *TebPtr
= (PTEB
)TebBase
;
632 DPRINT("TEB allocated at %p\n", TebBase
);
639 LdrInitApcRundownRoutine(PKAPC Apc
)
646 LdrInitApcKernelRoutine(PKAPC Apc
,
647 PKNORMAL_ROUTINE
* NormalRoutine
,
648 PVOID
* NormalContext
,
649 PVOID
* SystemArgument1
,
650 PVOID
* SystemArgument2
)
657 NtCreateThread(PHANDLE ThreadHandle
,
658 ACCESS_MASK DesiredAccess
,
659 POBJECT_ATTRIBUTES ObjectAttributes
,
660 HANDLE ProcessHandle
,
662 PCONTEXT ThreadContext
,
663 PUSER_STACK UserStack
,
664 BOOLEAN CreateSuspended
)
671 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
672 ThreadHandle
,ThreadContext
);
674 Status
= PsInitializeThread(ProcessHandle
,
680 if (!NT_SUCCESS(Status
))
685 Status
= KiArchInitThreadWithContext(&Thread
->Tcb
, ThreadContext
);
686 if (!NT_SUCCESS(Status
))
691 Status
= PsCreateTeb(ProcessHandle
,
695 if (!NT_SUCCESS(Status
))
699 Thread
->Tcb
.Teb
= TebBase
;
701 Thread
->StartAddress
= NULL
;
705 *Client
= Thread
->Cid
;
709 * Maybe send a message to the process's debugger
711 DbgkCreateThread((PVOID
)ThreadContext
->Eip
);
714 * First, force the thread to be non-alertable for user-mode alerts.
716 Thread
->Tcb
.Alertable
= FALSE
;
719 * If the thread is to be created suspended then queue an APC to
720 * do the suspend before we run any userspace code.
724 PsSuspendThread(Thread
, NULL
);
728 * Queue an APC to the thread that will execute the ntdll startup
731 LdrInitApc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
732 KeInitializeApc(LdrInitApc
, &Thread
->Tcb
, OriginalApcEnvironment
, LdrInitApcKernelRoutine
,
733 LdrInitApcRundownRoutine
, LdrpGetSystemDllEntryPoint(),
735 KeInsertQueueApc(LdrInitApc
, NULL
, NULL
, IO_NO_INCREMENT
);
738 * Start the thread running and force it to execute the APC(s) we just
739 * queued before it runs anything else in user-mode.
741 Thread
->Tcb
.Alertable
= TRUE
;
742 Thread
->Tcb
.Alerted
[0] = 1;
743 PsUnblockThread(Thread
, NULL
);
745 return(STATUS_SUCCESS
);
753 PsCreateSystemThread(PHANDLE ThreadHandle
,
754 ACCESS_MASK DesiredAccess
,
755 POBJECT_ATTRIBUTES ObjectAttributes
,
756 HANDLE ProcessHandle
,
758 PKSTART_ROUTINE StartRoutine
,
761 * FUNCTION: Creates a thread which executes in kernel mode
763 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
765 * DesiredAccess = Requested access to the thread
766 * ObjectAttributes = Object attributes (optional)
767 * ProcessHandle = Handle of process thread will run in
768 * NULL to use system process
769 * ClientId (OUT) = Caller supplied storage for the returned client id
770 * of the thread (optional)
771 * StartRoutine = Entry point for the thread
772 * StartContext = Argument supplied to the thread when it begins
774 * RETURNS: Success or failure status
780 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
781 ThreadHandle
,ProcessHandle
);
783 Status
= PsInitializeThread(ProcessHandle
,
789 if (!NT_SUCCESS(Status
))
794 Thread
->StartAddress
= StartRoutine
;
795 Status
= KiArchInitThread(&Thread
->Tcb
, StartRoutine
, StartContext
);
796 if (!NT_SUCCESS(Status
))
801 if (ClientId
!= NULL
)
803 *ClientId
=Thread
->Cid
;
806 PsUnblockThread(Thread
, NULL
);
808 return(STATUS_SUCCESS
);
813 PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread
,
817 CLIENT_ID Cid
= CurrentThread
->Cid
;
819 for (i
= 0; i
< PiThreadNotifyRoutineCount
; i
++)
821 PiThreadNotifyRoutine
[i
](Cid
.UniqueProcess
, Cid
.UniqueThread
, Create
);
830 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
)
832 if (PiThreadNotifyRoutineCount
>= MAX_THREAD_NOTIFY_ROUTINE_COUNT
)
834 return(STATUS_INSUFFICIENT_RESOURCES
);
837 PiThreadNotifyRoutine
[PiThreadNotifyRoutineCount
] = NotifyRoutine
;
838 PiThreadNotifyRoutineCount
++;
840 return(STATUS_SUCCESS
);