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 ****************************************************************/
24 #define NTOS_MODE_KERNEL
26 #include <internal/ke.h>
27 #include <internal/ob.h>
28 #include <internal/ps.h>
29 #include <internal/ex.h>
30 #include <internal/se.h>
31 #include <internal/id.h>
32 #include <internal/dbg.h>
33 #include <internal/ldr.h>
36 #include <internal/debug.h>
38 /* GLOBAL *******************************************************************/
40 static ULONG PiNextThreadUniqueId = 0;
42 extern KSPIN_LOCK PiThreadListLock;
43 extern ULONG PiNrThreads;
45 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];
53 #endif /* LIBCAPTIVE */
55 /* FUNCTIONS ***************************************************************/
60 PsAssignImpersonationToken(PETHREAD Thread,
64 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
67 if (TokenHandle != NULL)
69 Status = ObReferenceObjectByHandle(TokenHandle,
75 if (!NT_SUCCESS(Status))
79 ImpersonationLevel = Token->ImpersonationLevel;
84 ImpersonationLevel = 0;
87 PsImpersonateClient(Thread,
94 ObDereferenceObject(Token);
96 return(STATUS_SUCCESS);
107 Thread = PsGetCurrentThread();
109 if (Thread->ActiveImpersonationInfo != 0)
111 ObDereferenceObject(Thread->ImpersonationInfo->Token);
112 Thread->ActiveImpersonationInfo = 0;
120 PsImpersonateClient(PETHREAD Thread,
124 SECURITY_IMPERSONATION_LEVEL Level)
128 if (Thread->ActiveImpersonationInfo != 0)
130 Thread->ActiveImpersonationInfo = 0;
131 if (Thread->ImpersonationInfo->Token != NULL)
133 ObDereferenceObject(Thread->ImpersonationInfo->Token);
138 if (Thread->ActiveImpersonationInfo == 0 ||
139 Thread->ImpersonationInfo == NULL)
141 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
142 sizeof(PS_IMPERSONATION_INFO));
144 Thread->ImpersonationInfo->Level = Level;
145 Thread->ImpersonationInfo->Unknown2 = c;
146 Thread->ImpersonationInfo->Unknown1 = b;
147 Thread->ImpersonationInfo->Token = Token;
148 ObReferenceObjectByPointer(Token,
152 Thread->ActiveImpersonationInfo = 1;
156 PsReferenceEffectiveToken(PETHREAD Thread,
157 PTOKEN_TYPE TokenType,
159 PSECURITY_IMPERSONATION_LEVEL Level)
164 if (Thread->ActiveImpersonationInfo == 0)
166 Process = Thread->ThreadsProcess;
167 *TokenType = TokenPrimary;
169 Token = Process->Token;
173 Token = Thread->ImpersonationInfo->Token;
174 *TokenType = TokenImpersonation;
175 *b = Thread->ImpersonationInfo->Unknown2;
176 *Level = Thread->ImpersonationInfo->Level;
182 NtImpersonateThread(IN HANDLE ThreadHandle,
183 IN HANDLE ThreadToImpersonateHandle,
184 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
187 PETHREAD ThreadToImpersonate;
189 SECURITY_CLIENT_CONTEXT ClientContext;
191 Status = ObReferenceObjectByHandle(ThreadHandle,
197 if (!NT_SUCCESS(Status))
202 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
206 (PVOID*)&ThreadToImpersonate,
208 if (!NT_SUCCESS(Status))
210 ObDereferenceObject(Thread);
214 Status = SeCreateClientSecurity(ThreadToImpersonate,
215 SecurityQualityOfService,
218 if (!NT_SUCCESS(Status))
220 ObDereferenceObject(Thread);
221 ObDereferenceObject(ThreadToImpersonate);
225 SeImpersonateClient(&ClientContext, Thread);
226 if (ClientContext.Token != NULL)
228 ObDereferenceObject(ClientContext.Token);
230 return(STATUS_SUCCESS);
234 NtOpenThreadToken(IN HANDLE ThreadHandle,
235 IN ACCESS_MASK DesiredAccess,
236 IN BOOLEAN OpenAsSelf,
237 OUT PHANDLE TokenHandle)
244 Status = ObReferenceObjectByHandle(ThreadHandle,
250 if (!NT_SUCCESS(Status))
255 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
256 SepCreateImpersonationTokenDacl(Token);
258 return(STATUS_UNSUCCESSFUL);
261 #endif /* LIBCAPTIVE */
266 PACCESS_TOKEN STDCALL
267 PsReferenceImpersonationToken(PETHREAD Thread,
270 SECURITY_IMPERSONATION_LEVEL* Level)
272 if (Thread->ActiveImpersonationInfo == 0)
281 #else /* !LIBCAPTIVE */
282 *Level = Thread->ImpersonationInfo->Level;
283 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
284 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
285 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
289 return(Thread->ImpersonationInfo->Token);
290 #endif /* LIBCAPTIVE */
296 PiBeforeBeginThread(CONTEXT c)
298 KeLowerIrql(PASSIVE_LEVEL);
302 PiDeleteThread(PVOID ObjectBody)
307 PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
308 ULONG NotifyRoutineCount;
310 Thread = (PETHREAD)ObjectBody;
312 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
314 /* Terminate Win32 thread */
315 PsTerminateWin32Thread (Thread);
317 ObDereferenceObject(Thread->ThreadsProcess);
318 Thread->ThreadsProcess = NULL;
320 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
321 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
323 NotifyRoutine[i] = PiThreadNotifyRoutine[i];
325 NotifyRoutineCount = PiThreadNotifyRoutineCount;
327 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
328 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
329 ExpSwapThreadEventPair(Thread, NULL); /* Release the associated eventpair object, if there was one */
331 for (i = 0; i < NotifyRoutineCount; i++)
333 //must be called below DISPATCH_LVL
334 NotifyRoutine[i](Thread->Cid.UniqueProcess,
335 Thread->Cid.UniqueThread,
339 KeReleaseThread(Thread);
340 DPRINT("PiDeleteThread() finished\n");
343 #endif /* LIBCAPTIVE */
346 PsInitializeThread(HANDLE ProcessHandle,
348 PHANDLE ThreadHandle,
349 ACCESS_MASK DesiredAccess,
350 POBJECT_ATTRIBUTES ThreadAttributes,
359 ULONG NotifyRoutineCount;
360 PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
361 #endif /* LIBCAPTIVE */
366 if (ProcessHandle != NULL)
368 Status = ObReferenceObjectByHandle(ProcessHandle,
369 PROCESS_CREATE_THREAD,
374 if (Status != STATUS_SUCCESS)
376 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
379 DPRINT( "Creating thread in process %x\n", Process );
383 Process = PsInitialSystemProcess;
384 ObReferenceObjectByPointer(Process,
385 PROCESS_CREATE_THREAD,
391 * Create and initialize thread
393 Status = ObRosCreateObject(ThreadHandle,
398 if (!NT_SUCCESS(Status))
403 DPRINT("Thread = %x\n",Thread);
407 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
408 Thread->ThreadsProcess = Process;
410 * FIXME: What lock protects this?
412 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
413 &Thread->Tcb.ProcessThreadListEntry);
414 InitializeListHead(&Thread->TerminationPortList);
415 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
416 InitializeListHead(&Thread->IrpList);
417 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
418 (LONG *)&PiNextThreadUniqueId);
419 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
420 Thread->DeadThread = 0;
421 Thread->Win32Thread = 0;
422 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
426 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
427 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
429 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
431 NotifyRoutine[i] = PiThreadNotifyRoutine[i];
433 NotifyRoutineCount = PiThreadNotifyRoutineCount;
434 #endif /* LIBCAPTIVE */
435 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
437 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
438 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
441 for (i = 0; i < NotifyRoutineCount; i++)
443 //must be called below DISPATCH_LVL
444 NotifyRoutine[i](Thread->Cid.UniqueProcess,
445 Thread->Cid.UniqueThread,
448 #endif /* LIBCAPTIVE */
450 return(STATUS_SUCCESS);
456 PsCreateTeb(HANDLE ProcessHandle,
459 PUSER_STACK UserStack)
461 MEMORY_BASIC_INFORMATION Info;
470 TebBase = (PVOID)0x7FFDE000;
475 Status = NtQueryVirtualMemory(ProcessHandle,
477 MemoryBasicInformation,
481 if (!NT_SUCCESS(Status))
483 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
486 /* FIXME: Race between this and the above check */
487 if (Info.State == MEM_FREE)
489 /* The TEB must reside in user space */
490 Status = NtAllocateVirtualMemory(ProcessHandle,
494 MEM_RESERVE | MEM_COMMIT,
496 if (NT_SUCCESS(Status))
502 TebBase = TebBase - TebSize;
505 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
507 RtlZeroMemory(&Teb, sizeof(TEB));
508 /* set all pointers to and from the TEB */
509 Teb.Tib.Self = TebBase;
510 if (Thread->ThreadsProcess)
512 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
514 DPRINT("Teb.Peb %x\n", Teb.Peb);
516 /* store stack information from UserStack */
517 if(UserStack != NULL)
519 /* fixed-size stack */
520 if(UserStack->FixedStackBase && UserStack->FixedStackLimit)
522 Teb.Tib.StackBase = UserStack->FixedStackBase;
523 Teb.Tib.StackLimit = UserStack->FixedStackLimit;
524 Teb.DeallocationStack = UserStack->FixedStackLimit;
526 /* expandable stack */
529 Teb.Tib.StackBase = UserStack->ExpandableStackBase;
530 Teb.Tib.StackLimit = UserStack->ExpandableStackLimit;
531 Teb.DeallocationStack = UserStack->ExpandableStackBottom;
535 /* more initialization */
536 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
537 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
538 Teb.CurrentLocale = PsDefaultThreadLocaleId;
540 /* Terminate the exception handler list */
541 Teb.Tib.ExceptionList = (PVOID)-1;
543 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
545 /* write TEB data into teb page */
546 Status = NtWriteVirtualMemory(ProcessHandle,
552 if (!NT_SUCCESS(Status))
555 DPRINT1 ("Writing TEB failed!\n");
558 NtFreeVirtualMemory(ProcessHandle,
568 *TebPtr = (PTEB)TebBase;
571 DPRINT("TEB allocated at %p\n", TebBase);
577 LdrInitApcRundownRoutine(PKAPC Apc)
583 LdrInitApcKernelRoutine(PKAPC Apc,
584 PKNORMAL_ROUTINE* NormalRoutine,
585 PVOID* NormalContext,
586 PVOID* SystemArgument1,
587 PVOID* SystemArgument2)
593 NtCreateThread(PHANDLE ThreadHandle,
594 ACCESS_MASK DesiredAccess,
595 POBJECT_ATTRIBUTES ObjectAttributes,
596 HANDLE ProcessHandle,
598 PCONTEXT ThreadContext,
599 PUSER_STACK UserStack,
600 BOOLEAN CreateSuspended)
607 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
608 ThreadHandle,ThreadContext);
610 Status = PsInitializeThread(ProcessHandle,
616 if (!NT_SUCCESS(Status))
621 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
622 if (!NT_SUCCESS(Status))
627 Status = PsCreateTeb(ProcessHandle,
631 if (!NT_SUCCESS(Status))
635 Thread->Tcb.Teb = TebBase;
637 Thread->StartAddress = NULL;
641 *Client = Thread->Cid;
645 * Maybe send a message to the process's debugger
647 DbgkCreateThread((PVOID)ThreadContext->Eip);
650 * First, force the thread to be non-alertable for user-mode alerts.
652 Thread->Tcb.Alertable = FALSE;
655 * If the thread is to be created suspended then queue an APC to
656 * do the suspend before we run any userspace code.
660 PsSuspendThread(Thread, NULL);
664 * Queue an APC to the thread that will execute the ntdll startup
667 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
668 KeInitializeApc(LdrInitApc, &Thread->Tcb, OriginalApcEnvironment, LdrInitApcKernelRoutine,
669 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
671 KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT);
674 * Start the thread running and force it to execute the APC(s) we just
675 * queued before it runs anything else in user-mode.
677 Thread->Tcb.Alertable = TRUE;
678 Thread->Tcb.Alerted[0] = 1;
679 PsUnblockThread(Thread, NULL);
681 return(STATUS_SUCCESS);
689 PsCreateSystemThread(PHANDLE ThreadHandle,
690 ACCESS_MASK DesiredAccess,
691 POBJECT_ATTRIBUTES ObjectAttributes,
692 HANDLE ProcessHandle,
694 PKSTART_ROUTINE StartRoutine,
697 * FUNCTION: Creates a thread which executes in kernel mode
699 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
701 * DesiredAccess = Requested access to the thread
702 * ObjectAttributes = Object attributes (optional)
703 * ProcessHandle = Handle of process thread will run in
704 * NULL to use system process
705 * ClientId (OUT) = Caller supplied storage for the returned client id
706 * of the thread (optional)
707 * StartRoutine = Entry point for the thread
708 * StartContext = Argument supplied to the thread when it begins
710 * RETURNS: Success or failure status
716 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
717 ThreadHandle,ProcessHandle);
719 Status = PsInitializeThread(ProcessHandle,
725 if (!NT_SUCCESS(Status))
730 Thread->StartAddress = StartRoutine;
731 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
732 if (!NT_SUCCESS(Status))
737 if (ClientId != NULL)
739 *ClientId=Thread->Cid;
742 PsUnblockThread(Thread, NULL);
744 return(STATUS_SUCCESS);
752 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
756 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
757 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
759 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
760 return(STATUS_INSUFFICIENT_RESOURCES);
763 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
764 PiThreadNotifyRoutineCount++;
765 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
767 return(STATUS_SUCCESS);
770 #endif /* LIBCAPTIVE */