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
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/se.h>
28 #include <internal/id.h>
29 #include <internal/dbg.h>
30 #include <internal/ldr.h>
33 #include <internal/debug.h>
35 /* GLOBAL *******************************************************************/
37 static ULONG PiNextThreadUniqueId = 0;
39 extern KSPIN_LOCK PiThreadListLock;
40 extern ULONG PiNrThreads;
42 extern LIST_ENTRY PiThreadListHead;
44 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
46 static ULONG PiThreadNotifyRoutineCount = 0;
47 static PCREATE_THREAD_NOTIFY_ROUTINE
48 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
50 /* FUNCTIONS ***************************************************************/
53 PsAssignImpersonationToken(PETHREAD Thread,
57 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
60 if (TokenHandle != NULL)
62 Status = ObReferenceObjectByHandle(TokenHandle,
68 if (!NT_SUCCESS(Status))
72 ImpersonationLevel = Token->ImpersonationLevel;
77 ImpersonationLevel = 0;
80 PsImpersonateClient(Thread,
87 ObDereferenceObject(Token);
89 return(STATUS_SUCCESS);
97 Thread = PsGetCurrentThread();
99 if (Thread->ActiveImpersonationInfo != 0)
101 ObDereferenceObject(Thread->ImpersonationInfo->Token);
102 Thread->ActiveImpersonationInfo = 0;
107 PsImpersonateClient(PETHREAD Thread,
111 SECURITY_IMPERSONATION_LEVEL Level)
115 if (Thread->ActiveImpersonationInfo != 0)
117 Thread->ActiveImpersonationInfo = 0;
118 if (Thread->ImpersonationInfo->Token != NULL)
120 ObDereferenceObject(Thread->ImpersonationInfo->Token);
125 if (Thread->ActiveImpersonationInfo == 0 ||
126 Thread->ImpersonationInfo == NULL)
128 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
129 sizeof(PS_IMPERSONATION_INFO));
131 Thread->ImpersonationInfo->Level = Level;
132 Thread->ImpersonationInfo->Unknown2 = c;
133 Thread->ImpersonationInfo->Unknown1 = b;
134 Thread->ImpersonationInfo->Token = Token;
135 ObReferenceObjectByPointer(Token,
139 Thread->ActiveImpersonationInfo = 1;
143 PsReferenceEffectiveToken(PETHREAD Thread,
144 PTOKEN_TYPE TokenType,
146 PSECURITY_IMPERSONATION_LEVEL Level)
151 if (Thread->ActiveImpersonationInfo == 0)
153 Process = Thread->ThreadsProcess;
154 *TokenType = TokenPrimary;
156 Token = Process->Token;
160 Token = Thread->ImpersonationInfo->Token;
161 *TokenType = TokenImpersonation;
162 *b = Thread->ImpersonationInfo->Unknown2;
163 *Level = Thread->ImpersonationInfo->Level;
169 NtImpersonateThread(IN HANDLE ThreadHandle,
170 IN HANDLE ThreadToImpersonateHandle,
171 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
174 PETHREAD ThreadToImpersonate;
176 SECURITY_CLIENT_CONTEXT ClientContext;
178 Status = ObReferenceObjectByHandle(ThreadHandle,
184 if (!NT_SUCCESS(Status))
189 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
193 (PVOID*)&ThreadToImpersonate,
195 if (!NT_SUCCESS(Status))
197 ObDereferenceObject(Thread);
201 Status = SeCreateClientSecurity(ThreadToImpersonate,
202 SecurityQualityOfService,
205 if (!NT_SUCCESS(Status))
207 ObDereferenceObject(Thread);
208 ObDereferenceObject(ThreadToImpersonate);
212 SeImpersonateClient(&ClientContext, Thread);
213 if (ClientContext.Token != NULL)
215 ObDereferenceObject(ClientContext.Token);
217 return(STATUS_SUCCESS);
221 NtOpenThreadToken(IN HANDLE ThreadHandle,
222 IN ACCESS_MASK DesiredAccess,
223 IN BOOLEAN OpenAsSelf,
224 OUT PHANDLE TokenHandle)
231 Status = ObReferenceObjectByHandle(ThreadHandle,
237 if (!NT_SUCCESS(Status))
242 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
243 SepCreateImpersonationTokenDacl(Token);
245 return(STATUS_UNSUCCESSFUL);
248 PACCESS_TOKEN STDCALL
249 PsReferenceImpersonationToken(PETHREAD Thread,
252 SECURITY_IMPERSONATION_LEVEL* Level)
254 if (Thread->ActiveImpersonationInfo == 0)
259 *Level = Thread->ImpersonationInfo->Level;
260 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
261 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
262 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
266 return(Thread->ImpersonationInfo->Token);
270 PiBeforeBeginThread(CONTEXT c)
272 KeLowerIrql(PASSIVE_LEVEL);
276 PiDeleteThread(PVOID ObjectBody)
281 Thread = (PETHREAD)ObjectBody;
283 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
285 ObDereferenceObject(Thread->ThreadsProcess);
286 Thread->ThreadsProcess = NULL;
288 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
290 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
292 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
293 Thread->Cid.UniqueThread,
297 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
298 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
299 KeReleaseThread(Thread);
300 DPRINT("PiDeleteThread() finished\n");
304 PsInitializeThread(HANDLE ProcessHandle,
306 PHANDLE ThreadHandle,
307 ACCESS_MASK DesiredAccess,
308 POBJECT_ATTRIBUTES ThreadAttributes,
320 if (ProcessHandle != NULL)
322 Status = ObReferenceObjectByHandle(ProcessHandle,
323 PROCESS_CREATE_THREAD,
328 if (Status != STATUS_SUCCESS)
330 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
333 DPRINT( "Creating thread in process %x\n", Process );
337 Process = PsInitialSystemProcess;
338 ObReferenceObjectByPointer(Process,
339 PROCESS_CREATE_THREAD,
345 * Create and initialize thread
347 Status = ObCreateObject(ThreadHandle,
352 if (!NT_SUCCESS(Status))
357 DPRINT("Thread = %x\n",Thread);
361 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
362 Thread->ThreadsProcess = Process;
364 * FIXME: What lock protects this?
366 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
367 &Thread->Tcb.ProcessThreadListEntry);
368 InitializeListHead(&Thread->TerminationPortList);
369 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
370 InitializeListHead(&Thread->IrpList);
371 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
372 (LONG *)&PiNextThreadUniqueId);
373 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
374 Thread->DeadThread = 0;
375 Thread->Win32Thread = 0;
376 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
380 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
381 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
382 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
384 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
385 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
387 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
389 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
390 Thread->Cid.UniqueThread,
394 return(STATUS_SUCCESS);
399 PsCreateTeb(HANDLE ProcessHandle,
402 PUSER_STACK UserStack)
404 MEMORY_BASIC_INFORMATION Info;
413 TebBase = (PVOID)0x7FFDE000;
418 Status = NtQueryVirtualMemory(ProcessHandle,
420 MemoryBasicInformation,
424 if (!NT_SUCCESS(Status))
426 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
429 /* FIXME: Race between this and the above check */
430 if (Info.State == MEM_FREE)
432 /* The TEB must reside in user space */
433 Status = NtAllocateVirtualMemory(ProcessHandle,
437 MEM_RESERVE | MEM_COMMIT,
439 if (NT_SUCCESS(Status))
445 TebBase = TebBase - TebSize;
448 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
450 RtlZeroMemory(&Teb, sizeof(TEB));
451 /* set all pointers to and from the TEB */
452 Teb.Tib.Self = TebBase;
453 if (Thread->ThreadsProcess)
455 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
457 DPRINT("Teb.Peb %x\n", Teb.Peb);
459 /* store stack information from UserStack */
460 if(UserStack != NULL)
462 /* fixed-size stack */
463 if(UserStack->FixedStackBase && UserStack->FixedStackLimit)
465 Teb.Tib.StackBase = UserStack->FixedStackBase;
466 Teb.Tib.StackLimit = UserStack->FixedStackLimit;
467 Teb.DeallocationStack = UserStack->FixedStackLimit;
469 /* expandable stack */
472 Teb.Tib.StackBase = UserStack->ExpandableStackBase;
473 Teb.Tib.StackLimit = UserStack->ExpandableStackLimit;
474 Teb.DeallocationStack = UserStack->ExpandableStackBottom;
478 /* more initialization */
479 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
480 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
481 Teb.CurrentLocale = PsDefaultThreadLocaleId;
483 /* Terminate the exception handler list */
484 Teb.Tib.ExceptionList = (PVOID)-1;
486 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
488 /* write TEB data into teb page */
489 Status = NtWriteVirtualMemory(ProcessHandle,
495 if (!NT_SUCCESS(Status))
498 DPRINT1 ("Writing TEB failed!\n");
501 NtFreeVirtualMemory(ProcessHandle,
511 *TebPtr = (PTEB)TebBase;
514 DPRINT("TEB allocated at %p\n", TebBase);
520 LdrInitApcRundownRoutine(PKAPC Apc)
526 LdrInitApcKernelRoutine(PKAPC Apc,
527 PKNORMAL_ROUTINE* NormalRoutine,
528 PVOID* NormalContext,
529 PVOID* SystemArgument1,
530 PVOID* SystemArgument2)
536 NtCreateThread(PHANDLE ThreadHandle,
537 ACCESS_MASK DesiredAccess,
538 POBJECT_ATTRIBUTES ObjectAttributes,
539 HANDLE ProcessHandle,
541 PCONTEXT ThreadContext,
542 PUSER_STACK UserStack,
543 BOOLEAN CreateSuspended)
550 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
551 ThreadHandle,ThreadContext);
553 Status = PsInitializeThread(ProcessHandle,
559 if (!NT_SUCCESS(Status))
564 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
565 if (!NT_SUCCESS(Status))
570 Status = PsCreateTeb(ProcessHandle,
574 if (!NT_SUCCESS(Status))
578 Thread->Tcb.Teb = TebBase;
580 Thread->StartAddress = NULL;
584 *Client = Thread->Cid;
588 * Maybe send a message to the process's debugger
590 DbgkCreateThread((PVOID)ThreadContext->Eip);
593 * First, force the thread to be non-alertable for user-mode alerts.
595 Thread->Tcb.Alertable = FALSE;
598 * If the thread is to be created suspended then queue an APC to
599 * do the suspend before we run any userspace code.
603 PsSuspendThread(Thread, NULL);
607 * Queue an APC to the thread that will execute the ntdll startup
610 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
611 KeInitializeApc(LdrInitApc, &Thread->Tcb, 0, LdrInitApcKernelRoutine,
612 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
614 KeInsertQueueApc(LdrInitApc, NULL, NULL, UserMode);
617 * Start the thread running and force it to execute the APC(s) we just
618 * queued before it runs anything else in user-mode.
620 Thread->Tcb.Alertable = TRUE;
621 Thread->Tcb.Alerted[0] = 1;
622 PsUnblockThread(Thread, NULL);
624 return(STATUS_SUCCESS);
629 PsCreateSystemThread(PHANDLE ThreadHandle,
630 ACCESS_MASK DesiredAccess,
631 POBJECT_ATTRIBUTES ObjectAttributes,
632 HANDLE ProcessHandle,
634 PKSTART_ROUTINE StartRoutine,
637 * FUNCTION: Creates a thread which executes in kernel mode
639 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
641 * DesiredAccess = Requested access to the thread
642 * ObjectAttributes = Object attributes (optional)
643 * ProcessHandle = Handle of process thread will run in
644 * NULL to use system process
645 * ClientId (OUT) = Caller supplied storage for the returned client id
646 * of the thread (optional)
647 * StartRoutine = Entry point for the thread
648 * StartContext = Argument supplied to the thread when it begins
650 * RETURNS: Success or failure status
656 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
657 ThreadHandle,ProcessHandle);
659 Status = PsInitializeThread(ProcessHandle,
665 if (!NT_SUCCESS(Status))
670 Thread->StartAddress = StartRoutine;
671 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
672 if (!NT_SUCCESS(Status))
677 if (ClientId != NULL)
679 *ClientId=Thread->Cid;
682 PsUnblockThread(Thread, NULL);
684 return(STATUS_SUCCESS);
689 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
691 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
692 return(STATUS_INSUFFICIENT_RESOURCES);
694 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
695 PiThreadNotifyRoutineCount++;
697 return(STATUS_SUCCESS);