1 /* $Id: create.c,v 1.75 2004/07/13 11:48:32 ekohl 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 #define NTOS_MODE_KERNEL
28 #include <internal/ke.h>
29 #include <internal/ob.h>
30 #include <internal/ps.h>
31 #include <internal/se.h>
32 #include <internal/id.h>
33 #include <internal/dbg.h>
34 #include <internal/ldr.h>
37 #include <internal/debug.h>
39 /* GLOBAL *******************************************************************/
41 static ULONG PiNextThreadUniqueId
= 0;
43 extern KSPIN_LOCK PiThreadListLock
;
44 extern ULONG PiNrThreads
;
46 extern LIST_ENTRY PiThreadListHead
;
48 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
50 static ULONG PiThreadNotifyRoutineCount
= 0;
51 static PCREATE_THREAD_NOTIFY_ROUTINE
52 PiThreadNotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
54 /* FUNCTIONS ***************************************************************/
57 PsAssignImpersonationToken(PETHREAD Thread
,
61 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
;
64 if (TokenHandle
!= NULL
)
66 Status
= ObReferenceObjectByHandle(TokenHandle
,
72 if (!NT_SUCCESS(Status
))
76 ImpersonationLevel
= Token
->ImpersonationLevel
;
81 ImpersonationLevel
= 0;
84 PsImpersonateClient(Thread
,
91 ObDereferenceObject(Token
);
94 return(STATUS_SUCCESS
);
102 PsRevertToSelf (VOID
)
106 Thread
= PsGetCurrentThread ();
108 if (Thread
->ActiveImpersonationInfo
== TRUE
)
110 ObDereferenceObject (Thread
->ImpersonationInfo
->Token
);
111 Thread
->ActiveImpersonationInfo
= FALSE
;
120 PsImpersonateClient (IN PETHREAD Thread
,
121 IN PACCESS_TOKEN Token
,
122 IN BOOLEAN CopyOnOpen
,
123 IN BOOLEAN EffectiveOnly
,
124 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
128 if (Thread
->ActiveImpersonationInfo
== TRUE
)
130 Thread
->ActiveImpersonationInfo
= FALSE
;
131 if (Thread
->ImpersonationInfo
->Token
!= NULL
)
133 ObDereferenceObject (Thread
->ImpersonationInfo
->Token
);
139 if (Thread
->ImpersonationInfo
== NULL
)
141 Thread
->ImpersonationInfo
= ExAllocatePool (NonPagedPool
,
142 sizeof(PS_IMPERSONATION_INFO
));
145 Thread
->ImpersonationInfo
->Level
= ImpersonationLevel
;
146 Thread
->ImpersonationInfo
->CopyOnOpen
= CopyOnOpen
;
147 Thread
->ImpersonationInfo
->EffectiveOnly
= EffectiveOnly
;
148 Thread
->ImpersonationInfo
->Token
= Token
;
149 ObReferenceObjectByPointer (Token
,
153 Thread
->ActiveImpersonationInfo
= TRUE
;
158 PsReferenceEffectiveToken(PETHREAD Thread
,
159 PTOKEN_TYPE TokenType
,
160 PBOOLEAN EffectiveOnly
,
161 PSECURITY_IMPERSONATION_LEVEL Level
)
166 if (Thread
->ActiveImpersonationInfo
== FALSE
)
168 Process
= Thread
->ThreadsProcess
;
169 *TokenType
= TokenPrimary
;
170 *EffectiveOnly
= FALSE
;
171 Token
= Process
->Token
;
175 Token
= Thread
->ImpersonationInfo
->Token
;
176 *TokenType
= TokenImpersonation
;
177 *EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
178 *Level
= Thread
->ImpersonationInfo
->Level
;
185 NtImpersonateThread(IN HANDLE ThreadHandle
,
186 IN HANDLE ThreadToImpersonateHandle
,
187 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
)
189 SECURITY_CLIENT_CONTEXT ClientContext
;
191 PETHREAD ThreadToImpersonate
;
194 Status
= ObReferenceObjectByHandle (ThreadHandle
,
200 if (!NT_SUCCESS (Status
))
205 Status
= ObReferenceObjectByHandle (ThreadToImpersonateHandle
,
209 (PVOID
*)&ThreadToImpersonate
,
211 if (!NT_SUCCESS(Status
))
213 ObDereferenceObject (Thread
);
217 Status
= SeCreateClientSecurity (ThreadToImpersonate
,
218 SecurityQualityOfService
,
221 if (!NT_SUCCESS(Status
))
223 ObDereferenceObject (ThreadToImpersonate
);
224 ObDereferenceObject (Thread
);
228 SeImpersonateClient (&ClientContext
,
230 if (ClientContext
.Token
!= NULL
)
232 ObDereferenceObject (ClientContext
.Token
);
235 ObDereferenceObject (ThreadToImpersonate
);
236 ObDereferenceObject (Thread
);
238 return STATUS_SUCCESS
;
243 NtOpenThreadToken (IN HANDLE ThreadHandle
,
244 IN ACCESS_MASK DesiredAccess
,
245 IN BOOLEAN OpenAsSelf
,
246 OUT PHANDLE TokenHandle
)
252 Status
= ObReferenceObjectByHandle (ThreadHandle
,
258 if (!NT_SUCCESS(Status
))
265 Token
= Thread
->ThreadsProcess
->Token
;
269 if (Thread
->ActiveImpersonationInfo
== FALSE
)
271 ObDereferenceObject (Thread
);
272 return STATUS_NO_TOKEN
;
275 Token
= Thread
->ImpersonationInfo
->Token
;
280 ObDereferenceObject (Thread
);
281 return STATUS_NO_TOKEN
;
284 Status
= ObCreateHandle (PsGetCurrentProcess(),
290 ObDereferenceObject (Thread
);
299 PACCESS_TOKEN STDCALL
300 PsReferenceImpersonationToken(IN PETHREAD Thread
,
301 OUT PBOOLEAN CopyOnOpen
,
302 OUT PBOOLEAN EffectiveOnly
,
303 OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
305 if (Thread
->ActiveImpersonationInfo
== FALSE
)
310 *ImpersonationLevel
= Thread
->ImpersonationInfo
->Level
;
311 *CopyOnOpen
= Thread
->ImpersonationInfo
->CopyOnOpen
;
312 *EffectiveOnly
= Thread
->ImpersonationInfo
->EffectiveOnly
;
313 ObReferenceObjectByPointer (Thread
->ImpersonationInfo
->Token
,
318 return Thread
->ImpersonationInfo
->Token
;
323 PiBeforeBeginThread(CONTEXT c
)
325 KeLowerIrql(PASSIVE_LEVEL
);
330 PiDeleteThread(PVOID ObjectBody
)
335 Thread
= (PETHREAD
)ObjectBody
;
337 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody
);
339 ObDereferenceObject(Thread
->ThreadsProcess
);
340 Thread
->ThreadsProcess
= NULL
;
342 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
344 RemoveEntryList(&Thread
->Tcb
.ThreadListEntry
);
345 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
347 KeReleaseThread(Thread
);
348 DPRINT("PiDeleteThread() finished\n");
353 PsInitializeThread(HANDLE ProcessHandle
,
355 PHANDLE ThreadHandle
,
356 ACCESS_MASK DesiredAccess
,
357 POBJECT_ATTRIBUTES ThreadAttributes
,
368 if (ProcessHandle
!= NULL
)
370 Status
= ObReferenceObjectByHandle(ProcessHandle
,
371 PROCESS_CREATE_THREAD
,
376 if (Status
!= STATUS_SUCCESS
)
378 DPRINT("Failed at %s:%d\n",__FILE__
,__LINE__
);
381 DPRINT( "Creating thread in process %x\n", Process
);
385 Process
= PsInitialSystemProcess
;
386 ObReferenceObjectByPointer(Process
,
387 PROCESS_CREATE_THREAD
,
393 * Create and initialize thread
395 Status
= ObCreateObject(UserMode
,
404 if (!NT_SUCCESS(Status
))
409 Status
= ObInsertObject ((PVOID
)Thread
,
415 if (!NT_SUCCESS(Status
))
417 ObDereferenceObject (Thread
);
421 DPRINT("Thread = %x\n",Thread
);
425 KeInitializeThread(&Process
->Pcb
, &Thread
->Tcb
, First
);
426 Thread
->ThreadsProcess
= Process
;
428 * FIXME: What lock protects this?
430 InsertTailList(&Thread
->ThreadsProcess
->ThreadListHead
,
431 &Thread
->Tcb
.ProcessThreadListEntry
);
432 InitializeListHead(&Thread
->TerminationPortList
);
433 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
434 InitializeListHead(&Thread
->IrpList
);
435 Thread
->Cid
.UniqueThread
= (HANDLE
)InterlockedIncrement(
436 (LONG
*)&PiNextThreadUniqueId
);
437 Thread
->Cid
.UniqueProcess
= (HANDLE
)Thread
->ThreadsProcess
->UniqueProcessId
;
438 Thread
->DeadThread
= 0;
439 Thread
->Win32Thread
= 0;
440 DPRINT("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
444 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
445 InsertTailList(&PiThreadListHead
, &Thread
->Tcb
.ThreadListEntry
);
446 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
448 Thread
->Tcb
.BasePriority
= (CHAR
)Thread
->ThreadsProcess
->Pcb
.BasePriority
;
449 Thread
->Tcb
.Priority
= Thread
->Tcb
.BasePriority
;
452 * Local Procedure Call facility (LPC)
454 KeInitializeSemaphore (& Thread
->LpcReplySemaphore
, 0, LONG_MAX
);
455 Thread
->LpcReplyMessage
= NULL
;
456 Thread
->LpcReplyMessageId
= 0; /* not valid */
457 /* Thread->LpcReceiveMessageId = 0; */
458 Thread
->LpcExitThreadCalled
= FALSE
;
459 Thread
->LpcReceivedMsgIdValid
= FALSE
;
461 return(STATUS_SUCCESS
);
466 PsCreateTeb(HANDLE ProcessHandle
,
469 PUSER_STACK UserStack
)
471 MEMORY_BASIC_INFORMATION Info
;
480 TebBase
= (PVOID
)0x7FFDE000;
485 Status
= ZwQueryVirtualMemory(ProcessHandle
,
487 MemoryBasicInformation
,
491 if (!NT_SUCCESS(Status
))
493 CPRINT("ZwQueryVirtualMemory (Status %x)\n", Status
);
496 /* FIXME: Race between this and the above check */
497 if (Info
.State
== MEM_FREE
)
499 /* The TEB must reside in user space */
500 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
504 MEM_RESERVE
| MEM_COMMIT
,
506 if (NT_SUCCESS(Status
))
512 TebBase
= (char*)TebBase
- TebSize
;
515 DPRINT ("TebBase %p TebSize %lu\n", TebBase
, TebSize
);
517 RtlZeroMemory(&Teb
, sizeof(TEB
));
518 /* set all pointers to and from the TEB */
519 Teb
.Tib
.Self
= TebBase
;
520 if (Thread
->ThreadsProcess
)
522 Teb
.Peb
= Thread
->ThreadsProcess
->Peb
; /* No PEB yet!! */
524 DPRINT("Teb.Peb %x\n", Teb
.Peb
);
526 /* store stack information from UserStack */
527 if(UserStack
!= NULL
)
529 /* fixed-size stack */
530 if(UserStack
->FixedStackBase
&& UserStack
->FixedStackLimit
)
532 Teb
.Tib
.StackBase
= UserStack
->FixedStackBase
;
533 Teb
.Tib
.StackLimit
= UserStack
->FixedStackLimit
;
534 Teb
.DeallocationStack
= UserStack
->FixedStackLimit
;
536 /* expandable stack */
539 Teb
.Tib
.StackBase
= UserStack
->ExpandableStackBase
;
540 Teb
.Tib
.StackLimit
= UserStack
->ExpandableStackLimit
;
541 Teb
.DeallocationStack
= UserStack
->ExpandableStackBottom
;
545 /* more initialization */
546 Teb
.Cid
.UniqueThread
= Thread
->Cid
.UniqueThread
;
547 Teb
.Cid
.UniqueProcess
= Thread
->Cid
.UniqueProcess
;
548 Teb
.CurrentLocale
= PsDefaultThreadLocaleId
;
550 /* Terminate the exception handler list */
551 Teb
.Tib
.ExceptionList
= (PVOID
)-1;
553 DPRINT("sizeof(TEB) %x\n", sizeof(TEB
));
555 /* write TEB data into teb page */
556 Status
= NtWriteVirtualMemory(ProcessHandle
,
562 if (!NT_SUCCESS(Status
))
565 DPRINT1 ("Writing TEB failed!\n");
568 NtFreeVirtualMemory(ProcessHandle
,
578 *TebPtr
= (PTEB
)TebBase
;
581 DPRINT("TEB allocated at %p\n", TebBase
);
588 LdrInitApcRundownRoutine(PKAPC Apc
)
595 LdrInitApcKernelRoutine(PKAPC Apc
,
596 PKNORMAL_ROUTINE
* NormalRoutine
,
597 PVOID
* NormalContext
,
598 PVOID
* SystemArgument1
,
599 PVOID
* SystemArgument2
)
606 NtCreateThread(PHANDLE ThreadHandle
,
607 ACCESS_MASK DesiredAccess
,
608 POBJECT_ATTRIBUTES ObjectAttributes
,
609 HANDLE ProcessHandle
,
611 PCONTEXT ThreadContext
,
612 PUSER_STACK UserStack
,
613 BOOLEAN CreateSuspended
)
620 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
621 ThreadHandle
,ThreadContext
);
623 Status
= PsInitializeThread(ProcessHandle
,
629 if (!NT_SUCCESS(Status
))
634 Status
= KiArchInitThreadWithContext(&Thread
->Tcb
, ThreadContext
);
635 if (!NT_SUCCESS(Status
))
640 Status
= PsCreateTeb(ProcessHandle
,
644 if (!NT_SUCCESS(Status
))
648 Thread
->Tcb
.Teb
= TebBase
;
650 Thread
->StartAddress
= NULL
;
654 *Client
= Thread
->Cid
;
658 * Maybe send a message to the process's debugger
660 DbgkCreateThread((PVOID
)ThreadContext
->Eip
);
663 * First, force the thread to be non-alertable for user-mode alerts.
665 Thread
->Tcb
.Alertable
= FALSE
;
668 * If the thread is to be created suspended then queue an APC to
669 * do the suspend before we run any userspace code.
673 PsSuspendThread(Thread
, NULL
);
677 * Queue an APC to the thread that will execute the ntdll startup
680 LdrInitApc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
681 KeInitializeApc(LdrInitApc
, &Thread
->Tcb
, OriginalApcEnvironment
, LdrInitApcKernelRoutine
,
682 LdrInitApcRundownRoutine
, LdrpGetSystemDllEntryPoint(),
684 KeInsertQueueApc(LdrInitApc
, NULL
, NULL
, IO_NO_INCREMENT
);
687 * Start the thread running and force it to execute the APC(s) we just
688 * queued before it runs anything else in user-mode.
690 Thread
->Tcb
.Alertable
= TRUE
;
691 Thread
->Tcb
.Alerted
[0] = 1;
692 PsUnblockThread(Thread
, NULL
);
694 return(STATUS_SUCCESS
);
702 PsCreateSystemThread(PHANDLE ThreadHandle
,
703 ACCESS_MASK DesiredAccess
,
704 POBJECT_ATTRIBUTES ObjectAttributes
,
705 HANDLE ProcessHandle
,
707 PKSTART_ROUTINE StartRoutine
,
710 * FUNCTION: Creates a thread which executes in kernel mode
712 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
714 * DesiredAccess = Requested access to the thread
715 * ObjectAttributes = Object attributes (optional)
716 * ProcessHandle = Handle of process thread will run in
717 * NULL to use system process
718 * ClientId (OUT) = Caller supplied storage for the returned client id
719 * of the thread (optional)
720 * StartRoutine = Entry point for the thread
721 * StartContext = Argument supplied to the thread when it begins
723 * RETURNS: Success or failure status
729 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
730 ThreadHandle
,ProcessHandle
);
732 Status
= PsInitializeThread(ProcessHandle
,
738 if (!NT_SUCCESS(Status
))
743 Thread
->StartAddress
= StartRoutine
;
744 Status
= KiArchInitThread(&Thread
->Tcb
, StartRoutine
, StartContext
);
745 if (!NT_SUCCESS(Status
))
750 if (ClientId
!= NULL
)
752 *ClientId
=Thread
->Cid
;
755 PsUnblockThread(Thread
, NULL
);
757 return(STATUS_SUCCESS
);
762 PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread
,
766 CLIENT_ID Cid
= CurrentThread
->Cid
;
768 for (i
= 0; i
< PiThreadNotifyRoutineCount
; i
++)
770 PiThreadNotifyRoutine
[i
](Cid
.UniqueProcess
, Cid
.UniqueThread
, Create
);
779 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
)
781 if (PiThreadNotifyRoutineCount
>= MAX_THREAD_NOTIFY_ROUTINE_COUNT
)
783 return(STATUS_INSUFFICIENT_RESOURCES
);
786 PiThreadNotifyRoutine
[PiThreadNotifyRoutineCount
] = NotifyRoutine
;
787 PiThreadNotifyRoutineCount
++;
789 return(STATUS_SUCCESS
);