3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/create.c
6 * PURPOSE: Thread managment
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
25 #include <internal/debug.h>
27 /* GLOBAL *******************************************************************/
29 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
31 static ULONG PiThreadNotifyRoutineCount
= 0;
32 static PCREATE_THREAD_NOTIFY_ROUTINE
33 PiThreadNotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
35 /* FUNCTIONS ***************************************************************/
41 PsAssignImpersonationToken(PETHREAD Thread
,
45 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
;
48 if (TokenHandle
!= NULL
)
50 Status
= ObReferenceObjectByHandle(TokenHandle
,
56 if (!NT_SUCCESS(Status
))
60 ImpersonationLevel
= SeTokenImpersonationLevel(Token
);
65 ImpersonationLevel
= 0;
68 PsImpersonateClient(Thread
,
75 ObDereferenceObject(Token
);
78 return(STATUS_SUCCESS
);
88 PsRevertThreadToSelf(PsGetCurrentThread());
100 if (Thread
->ActiveImpersonationInfo
== TRUE
)
102 ObDereferenceObject (Thread
->ImpersonationInfo
->Token
);
103 Thread
->ActiveImpersonationInfo
= FALSE
;
111 PsImpersonateClient (IN PETHREAD Thread
,
112 IN PACCESS_TOKEN Token
,
113 IN BOOLEAN CopyOnOpen
,
114 IN BOOLEAN EffectiveOnly
,
115 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
119 if (Thread
->ActiveImpersonationInfo
== TRUE
)
121 Thread
->ActiveImpersonationInfo
= FALSE
;
122 if (Thread
->ImpersonationInfo
->Token
!= NULL
)
124 ObDereferenceObject (Thread
->ImpersonationInfo
->Token
);
130 if (Thread
->ImpersonationInfo
== NULL
)
132 Thread
->ImpersonationInfo
= ExAllocatePool (NonPagedPool
,
133 sizeof(PS_IMPERSONATION_INFORMATION
));
136 Thread
->ImpersonationInfo
->ImpersonationLevel
= ImpersonationLevel
;
137 Thread
->ImpersonationInfo
->CopyOnOpen
= CopyOnOpen
;
138 Thread
->ImpersonationInfo
->EffectiveOnly
= EffectiveOnly
;
139 Thread
->ImpersonationInfo
->Token
= Token
;
140 ObReferenceObjectByPointer (Token
,
144 Thread
->ActiveImpersonationInfo
= TRUE
;
149 PsReferenceEffectiveToken(PETHREAD Thread
,
150 PTOKEN_TYPE TokenType
,
151 PBOOLEAN EffectiveOnly
,
152 PSECURITY_IMPERSONATION_LEVEL Level
)
157 if (Thread
->ActiveImpersonationInfo
== FALSE
)
159 Process
= Thread
->ThreadsProcess
;
160 *TokenType
= TokenPrimary
;
161 *EffectiveOnly
= FALSE
;
162 Token
= Process
->Token
;
166 Token
= Thread
->ImpersonationInfo
->Token
;
167 *TokenType
= TokenImpersonation
;
168 *EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
169 *Level
= Thread
->ImpersonationInfo
->ImpersonationLevel
;
176 NtImpersonateThread(IN HANDLE ThreadHandle
,
177 IN HANDLE ThreadToImpersonateHandle
,
178 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
)
180 SECURITY_QUALITY_OF_SERVICE SafeServiceQoS
;
181 SECURITY_CLIENT_CONTEXT ClientContext
;
183 PETHREAD ThreadToImpersonate
;
184 KPROCESSOR_MODE PreviousMode
;
185 NTSTATUS Status
= STATUS_SUCCESS
;
189 PreviousMode
= ExGetPreviousMode();
191 if(PreviousMode
!= KernelMode
)
195 ProbeForRead(SecurityQualityOfService
,
196 sizeof(SECURITY_QUALITY_OF_SERVICE
),
198 SafeServiceQoS
= *SecurityQualityOfService
;
199 SecurityQualityOfService
= &SafeServiceQoS
;
203 Status
= _SEH_GetExceptionCode();
207 if(!NT_SUCCESS(Status
))
213 Status
= ObReferenceObjectByHandle(ThreadHandle
,
219 if(NT_SUCCESS(Status
))
221 Status
= ObReferenceObjectByHandle(ThreadToImpersonateHandle
,
222 THREAD_DIRECT_IMPERSONATION
,
225 (PVOID
*)&ThreadToImpersonate
,
227 if(NT_SUCCESS(Status
))
229 Status
= SeCreateClientSecurity(ThreadToImpersonate
,
230 SecurityQualityOfService
,
233 if(NT_SUCCESS(Status
))
235 SeImpersonateClient(&ClientContext
,
237 if(ClientContext
.ClientToken
!= NULL
)
239 ObDereferenceObject (ClientContext
.ClientToken
);
243 ObDereferenceObject(ThreadToImpersonate
);
245 ObDereferenceObject(Thread
);
254 PACCESS_TOKEN STDCALL
255 PsReferenceImpersonationToken(IN PETHREAD Thread
,
256 OUT PBOOLEAN CopyOnOpen
,
257 OUT PBOOLEAN EffectiveOnly
,
258 OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
260 if (Thread
->ActiveImpersonationInfo
== FALSE
)
265 *ImpersonationLevel
= Thread
->ImpersonationInfo
->ImpersonationLevel
;
266 *CopyOnOpen
= Thread
->ImpersonationInfo
->CopyOnOpen
;
267 *EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
268 ObReferenceObjectByPointer (Thread
->ImpersonationInfo
->Token
,
273 return Thread
->ImpersonationInfo
->Token
;
276 #ifdef PsDereferencePrimaryToken
277 #undef PsDereferenceImpersonationToken
284 PsDereferenceImpersonationToken(
285 IN PACCESS_TOKEN ImpersonationToken
288 if (ImpersonationToken
) {
289 ObDereferenceObject(ImpersonationToken
);
293 #ifdef PsDereferencePrimaryToken
294 #undef PsDereferencePrimaryToken
301 PsDereferencePrimaryToken(
302 IN PACCESS_TOKEN PrimaryToken
305 ObDereferenceObject(PrimaryToken
);
313 PsDisableImpersonation(
315 IN PSE_IMPERSONATION_STATE ImpersonationState
318 if (Thread
->ActiveImpersonationInfo
== FALSE
)
320 ImpersonationState
->Token
= NULL
;
321 ImpersonationState
->CopyOnOpen
= FALSE
;
322 ImpersonationState
->EffectiveOnly
= FALSE
;
323 ImpersonationState
->Level
= 0;
328 /* ExfAcquirePushLockExclusive(&Thread->ThreadLock); */
330 Thread
->ActiveImpersonationInfo
= FALSE
;
331 ImpersonationState
->Token
= Thread
->ImpersonationInfo
->Token
;
332 ImpersonationState
->CopyOnOpen
= Thread
->ImpersonationInfo
->CopyOnOpen
;
333 ImpersonationState
->EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
334 ImpersonationState
->Level
= Thread
->ImpersonationInfo
->ImpersonationLevel
;
337 /* ExfReleasePushLock(&Thread->ThreadLock); */
347 PsRestoreImpersonation(
349 IN PSE_IMPERSONATION_STATE ImpersonationState
352 PsImpersonateClient(Thread
, ImpersonationState
->Token
,
353 ImpersonationState
->CopyOnOpen
,
354 ImpersonationState
->EffectiveOnly
,
355 ImpersonationState
->Level
);
356 ObfDereferenceObject(ImpersonationState
->Token
);
360 PiBeforeBeginThread(CONTEXT c
)
362 KeLowerIrql(PASSIVE_LEVEL
);
367 PiDeleteThread(PVOID ObjectBody
)
372 Thread
= (PETHREAD
)ObjectBody
;
374 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody
);
376 Process
= Thread
->ThreadsProcess
;
377 Thread
->ThreadsProcess
= NULL
;
379 if(Thread
->Cid
.UniqueThread
!= NULL
)
381 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
384 if(Thread
->Tcb
.Win32Thread
!= NULL
)
386 /* Free the W32THREAD structure if present */
387 ExFreePool (Thread
->Tcb
.Win32Thread
);
390 KeReleaseThread(ETHREAD_TO_KTHREAD(Thread
));
392 ObDereferenceObject(Process
);
394 DPRINT("PiDeleteThread() finished\n");
399 PsInitializeThread(PEPROCESS Process
,
401 POBJECT_ATTRIBUTES ObjectAttributes
,
402 KPROCESSOR_MODE AccessMode
,
413 Process
= PsInitialSystemProcess
;
417 * Create and initialize thread
419 Status
= ObCreateObject(AccessMode
,
428 if (!NT_SUCCESS(Status
))
436 ObReferenceObjectByPointer(Process
,
437 PROCESS_CREATE_THREAD
,
441 Thread
->ThreadsProcess
= Process
;
442 Thread
->Cid
.UniqueThread
= NULL
;
443 Thread
->Cid
.UniqueProcess
= (HANDLE
)Thread
->ThreadsProcess
->UniqueProcessId
;
445 DPRINT("Thread = %x\n",Thread
);
447 KeInitializeThread(&Process
->Pcb
, &Thread
->Tcb
, First
);
448 InitializeListHead(&Thread
->TerminationPortList
);
449 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
450 InitializeListHead(&Thread
->IrpList
);
451 Thread
->DeadThread
= FALSE
;
452 Thread
->HasTerminated
= FALSE
;
453 Thread
->Tcb
.Win32Thread
= NULL
;
454 DPRINT("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
457 Thread
->Tcb
.BasePriority
= (CHAR
)Process
->Pcb
.BasePriority
;
458 Thread
->Tcb
.Priority
= Thread
->Tcb
.BasePriority
;
461 * Local Procedure Call facility (LPC)
463 KeInitializeSemaphore (& Thread
->LpcReplySemaphore
, 0, LONG_MAX
);
464 Thread
->LpcReplyMessage
= NULL
;
465 Thread
->LpcReplyMessageId
= 0; /* not valid */
466 /* Thread->LpcReceiveMessageId = 0; */
467 Thread
->LpcExitThreadCalled
= FALSE
;
468 Thread
->LpcReceivedMsgIdValid
= FALSE
;
470 oldIrql
= KeAcquireDispatcherDatabaseLock();
471 InsertTailList(&Process
->ThreadListHead
,
472 &Thread
->ThreadListEntry
);
473 KeReleaseDispatcherDatabaseLock(oldIrql
);
477 return STATUS_SUCCESS
;
482 PsCreateTeb(HANDLE ProcessHandle
,
485 PINITIAL_TEB InitialTeb
)
499 if (NULL
== Thread
->ThreadsProcess
)
501 /* We'll be allocating a 64k block here and only use 4k of it, but this
502 path should almost never be taken. Actually, I never saw it was taken,
503 so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and
506 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
510 MEM_RESERVE
| MEM_COMMIT
| MEM_TOP_DOWN
,
512 if (! NT_SUCCESS(Status
))
514 DPRINT1("Failed to allocate virtual memory for TEB\n");
520 Process
= Thread
->ThreadsProcess
;
521 ExAcquireFastMutex(&Process
->TebLock
);
522 if (NULL
== Process
->TebBlock
||
523 Process
->TebBlock
== Process
->TebLastAllocated
)
525 Process
->TebBlock
= NULL
;
526 RegionSize
= MM_VIRTMEM_GRANULARITY
;
527 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
531 MEM_RESERVE
| MEM_TOP_DOWN
,
533 if (! NT_SUCCESS(Status
))
535 ExReleaseFastMutex(&Process
->TebLock
);
536 DPRINT1("Failed to reserve virtual memory for TEB\n");
539 Process
->TebLastAllocated
= (PVOID
) ((char *) Process
->TebBlock
+ RegionSize
);
541 TebBase
= (PVOID
) ((char *) Process
->TebLastAllocated
- PAGE_SIZE
);
542 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
548 if (! NT_SUCCESS(Status
))
550 DPRINT1("Failed to commit virtual memory for TEB\n");
553 Process
->TebLastAllocated
= TebBase
;
554 ExReleaseFastMutex(&Process
->TebLock
);
557 DPRINT ("TebBase %p TebSize %lu\n", TebBase
, TebSize
);
558 ASSERT(NULL
!= TebBase
&& PAGE_SIZE
<= TebSize
);
560 RtlZeroMemory(&Teb
, sizeof(TEB
));
561 /* set all pointers to and from the TEB */
562 Teb
.Tib
.Self
= TebBase
;
563 if (Thread
->ThreadsProcess
)
565 Teb
.Peb
= Thread
->ThreadsProcess
->Peb
; /* No PEB yet!! */
567 DPRINT("Teb.Peb %x\n", Teb
.Peb
);
569 /* store stack information from InitialTeb */
570 if(InitialTeb
!= NULL
)
572 /* fixed-size stack */
573 if(InitialTeb
->StackBase
&& InitialTeb
->StackLimit
)
575 Teb
.Tib
.StackBase
= InitialTeb
->StackBase
;
576 Teb
.Tib
.StackLimit
= InitialTeb
->StackLimit
;
577 Teb
.DeallocationStack
= InitialTeb
->StackLimit
;
579 /* expandable stack */
582 Teb
.Tib
.StackBase
= InitialTeb
->StackCommit
;
583 Teb
.Tib
.StackLimit
= InitialTeb
->StackCommitMax
;
584 Teb
.DeallocationStack
= InitialTeb
->StackReserved
;
588 /* more initialization */
589 Teb
.Cid
.UniqueThread
= Thread
->Cid
.UniqueThread
;
590 Teb
.Cid
.UniqueProcess
= Thread
->Cid
.UniqueProcess
;
591 Teb
.CurrentLocale
= PsDefaultThreadLocaleId
;
593 /* Terminate the exception handler list */
594 Teb
.Tib
.ExceptionList
= (PVOID
)-1;
596 DPRINT("sizeof(TEB) %x\n", sizeof(TEB
));
598 /* write TEB data into teb page */
599 Status
= NtWriteVirtualMemory(ProcessHandle
,
605 if (!NT_SUCCESS(Status
))
608 DPRINT1 ("Writing TEB failed!\n");
611 NtFreeVirtualMemory(ProcessHandle
,
621 *TebPtr
= (PTEB
)TebBase
;
624 DPRINT("TEB allocated at %p\n", TebBase
);
631 LdrInitApcRundownRoutine(PKAPC Apc
)
638 LdrInitApcKernelRoutine(PKAPC Apc
,
639 PKNORMAL_ROUTINE
* NormalRoutine
,
640 PVOID
* NormalContext
,
641 PVOID
* SystemArgument1
,
642 PVOID
* SystemArgument2
)
649 NtCreateThread(OUT PHANDLE ThreadHandle
,
650 IN ACCESS_MASK DesiredAccess
,
651 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
652 IN HANDLE ProcessHandle
,
653 OUT PCLIENT_ID ClientId
,
654 IN PCONTEXT ThreadContext
,
655 IN PINITIAL_TEB InitialTeb
,
656 IN BOOLEAN CreateSuspended
)
660 INITIAL_TEB SafeInitialTeb
;
666 KPROCESSOR_MODE PreviousMode
;
667 NTSTATUS Status
= STATUS_SUCCESS
;
671 if(ThreadContext
== NULL
)
673 return STATUS_INVALID_PARAMETER
;
676 PreviousMode
= ExGetPreviousMode();
678 if(PreviousMode
!= KernelMode
)
682 ProbeForWrite(ThreadHandle
,
687 ProbeForWrite(ClientId
,
691 ProbeForRead(ThreadContext
,
694 SafeContext
= *ThreadContext
;
695 ThreadContext
= &SafeContext
;
696 ProbeForRead(InitialTeb
,
699 SafeInitialTeb
= *InitialTeb
;
700 InitialTeb
= &SafeInitialTeb
;
704 Status
= _SEH_GetExceptionCode();
708 if(!NT_SUCCESS(Status
))
714 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
715 ThreadHandle
,ThreadContext
);
717 Status
= ObReferenceObjectByHandle(ProcessHandle
,
718 PROCESS_CREATE_THREAD
,
723 if(!NT_SUCCESS(Status
))
728 Status
= PsInitializeThread(Process
,
734 ObDereferenceObject(Process
);
736 if (!NT_SUCCESS(Status
))
741 /* create a client id handle */
742 Status
= PsCreateCidHandle(Thread
, PsThreadType
, &Thread
->Cid
.UniqueThread
);
743 if (!NT_SUCCESS(Status
))
745 ObDereferenceObject(Thread
);
749 Status
= KiArchInitThreadWithContext(&Thread
->Tcb
, ThreadContext
);
750 if (!NT_SUCCESS(Status
))
752 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
753 ObDereferenceObject(Thread
);
757 Status
= PsCreateTeb(ProcessHandle
,
761 if (!NT_SUCCESS(Status
))
763 PsDeleteCidHandle(Thread
->Cid
.UniqueThread
, PsThreadType
);
764 ObDereferenceObject(Thread
);
767 Thread
->Tcb
.Teb
= TebBase
;
769 Thread
->StartAddress
= NULL
;
772 * Maybe send a message to the process's debugger
774 DbgkCreateThread((PVOID
)ThreadContext
->Eip
);
777 * First, force the thread to be non-alertable for user-mode alerts.
779 Thread
->Tcb
.Alertable
= FALSE
;
782 * If the thread is to be created suspended then queue an APC to
783 * do the suspend before we run any userspace code.
787 PsSuspendThread(Thread
, NULL
);
791 * Queue an APC to the thread that will execute the ntdll startup
794 LdrInitApc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
795 KeInitializeApc(LdrInitApc
, &Thread
->Tcb
, OriginalApcEnvironment
, LdrInitApcKernelRoutine
,
796 LdrInitApcRundownRoutine
, LdrpGetSystemDllEntryPoint(),
798 KeInsertQueueApc(LdrInitApc
, NULL
, NULL
, IO_NO_INCREMENT
);
801 * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE.
802 * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call,
803 * doing so is a blatant and erronous hack.
805 Thread
->Tcb
.ApcState
.UserApcPending
= TRUE
;
806 Thread
->Tcb
.Alerted
[KernelMode
] = TRUE
;
808 oldIrql
= KeAcquireDispatcherDatabaseLock ();
809 PsUnblockThread(Thread
, NULL
, 0);
810 KeReleaseDispatcherDatabaseLock(oldIrql
);
812 Status
= ObInsertObject((PVOID
)Thread
,
818 if(NT_SUCCESS(Status
))
824 *ClientId
= Thread
->Cid
;
826 *ThreadHandle
= hThread
;
830 Status
= _SEH_GetExceptionCode();
843 PsCreateSystemThread(PHANDLE ThreadHandle
,
844 ACCESS_MASK DesiredAccess
,
845 POBJECT_ATTRIBUTES ObjectAttributes
,
846 HANDLE ProcessHandle
,
848 PKSTART_ROUTINE StartRoutine
,
851 * FUNCTION: Creates a thread which executes in kernel mode
853 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
855 * DesiredAccess = Requested access to the thread
856 * ObjectAttributes = Object attributes (optional)
857 * ProcessHandle = Handle of process thread will run in
858 * NULL to use system process
859 * ClientId (OUT) = Caller supplied storage for the returned client id
860 * of the thread (optional)
861 * StartRoutine = Entry point for the thread
862 * StartContext = Argument supplied to the thread when it begins
864 * RETURNS: Success or failure status
873 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
874 ThreadHandle
,ProcessHandle
);
876 Status
= PsInitializeThread(NULL
,
881 if (!NT_SUCCESS(Status
))
886 Status
= PsCreateCidHandle(Thread
,
888 &Thread
->Cid
.UniqueThread
);
889 if(!NT_SUCCESS(Status
))
891 ObDereferenceObject(Thread
);
895 Thread
->StartAddress
= StartRoutine
;
896 Status
= KiArchInitThread(&Thread
->Tcb
, StartRoutine
, StartContext
);
897 if (!NT_SUCCESS(Status
))
899 ObDereferenceObject(Thread
);
903 if (ClientId
!= NULL
)
905 *ClientId
=Thread
->Cid
;
908 oldIrql
= KeAcquireDispatcherDatabaseLock ();
909 PsUnblockThread(Thread
, NULL
, 0);
910 KeReleaseDispatcherDatabaseLock(oldIrql
);
912 Status
= ObInsertObject((PVOID
)Thread
,
919 /* don't dereference the thread, the initial reference serves as the keep-alive
920 reference which will be removed by the thread reaper */
927 PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread
,
931 CLIENT_ID Cid
= CurrentThread
->Cid
;
933 for (i
= 0; i
< PiThreadNotifyRoutineCount
; i
++)
935 PiThreadNotifyRoutine
[i
](Cid
.UniqueProcess
, Cid
.UniqueThread
, Create
);
944 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
)
946 if (PiThreadNotifyRoutineCount
>= MAX_THREAD_NOTIFY_ROUTINE_COUNT
)
948 return(STATUS_INSUFFICIENT_RESOURCES
);
951 PiThreadNotifyRoutine
[PiThreadNotifyRoutineCount
] = NotifyRoutine
;
952 PiThreadNotifyRoutineCount
++;
954 return(STATUS_SUCCESS
);