2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Process Manager: Thread Management
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org)
12 * - MAJOR: Use Process Rundown
13 * - MAJOR: Use Process Pushlock Locks
14 * - MAJOR: Use Safe Referencing in PsGetNextProcess/Thread.
15 * - MAJOR: Use Guarded Mutex instead of Fast Mutex for Active Process Locks.
16 * - Generate process cookie for user-more thread.
17 * - Add security calls where necessary.
18 * - KeInit/StartThread for better isolation of code
21 /* INCLUDES ****************************************************************/
25 #include <internal/debug.h>
27 /* GLOBALS ******************************************************************/
29 extern LIST_ENTRY PsActiveProcessHead
;
30 extern PEPROCESS PsIdleProcess
;
31 extern PVOID PspSystemDllEntryPoint
;
32 extern PVOID PspSystemDllBase
;
33 extern PHANDLE_TABLE PspCidTable
;
34 extern BOOLEAN CcPfEnablePrefetcher
;
35 extern ULONG MmReadClusterSize
;
36 POBJECT_TYPE PsThreadType
= NULL
;
38 /* FUNCTIONS ***************************************************************/
42 PspUserThreadStartup(PKSTART_ROUTINE StartRoutine
,
47 BOOLEAN DeadThread
= FALSE
;
50 /* Go to Passive Level */
51 KeLowerIrql(PASSIVE_LEVEL
);
52 Thread
= PsGetCurrentThread();
54 /* Check if the thread is dead */
55 if (Thread
->DeadThread
)
57 /* Remember that we're dead */
62 /* Get the Locale ID and save Preferred Proc */
64 Teb
->CurrentLocale
= MmGetSessionLocaleId();
65 Teb
->IdealProcessor
= Thread
->Tcb
.IdealProcessor
;
68 /* Check if this is a system thread, or if we're hiding */
69 if (!(Thread
->SystemThread
) && !(Thread
->HideFromDebugger
))
71 /* We're not, so notify the debugger */
72 DbgkCreateThread(StartContext
);
75 /* Make sure we're not already dead */
78 /* Check if the Prefetcher is enabled */
79 if (CcPfEnablePrefetcher
)
81 /* FIXME: Prepare to prefetch this process */
85 KfRaiseIrql(APC_LEVEL
);
87 /* Queue the User APC */
88 KiInitializeUserApc(NULL
,
89 (PVOID
)((ULONG_PTR
)Thread
->Tcb
.InitialStack
-
91 sizeof(FX_SAVE_AREA
)),
92 PspSystemDllEntryPoint
,
97 /* Lower it back to passive */
98 KeLowerIrql(PASSIVE_LEVEL
);
102 /* We're dead, kill us now */
103 PspTerminateThreadByPointer(Thread
,
104 STATUS_THREAD_IS_TERMINATING
,
108 /* Do we have a cookie set yet? */
109 if (!SharedUserData
->Cookie
)
112 * FIXME: Generate cookie
113 * Formula (roughly): Per-CPU Page Fault ^ Per-CPU Interrupt Time ^
114 * Global System Time ^ Stack Address of where
115 * the LARGE_INTEGER containing the Global System
123 PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine
,
128 /* Unlock the dispatcher Database */
129 KeLowerIrql(PASSIVE_LEVEL
);
130 Thread
= PsGetCurrentThread();
132 /* Make sure the thread isn't gone */
133 if (!(Thread
->Terminated
) || !(Thread
->DeadThread
))
135 /* Call it the Start Routine */
136 StartRoutine(StartContext
);
139 /* Exit the thread */
140 PspTerminateThreadByPointer(Thread
, STATUS_SUCCESS
, TRUE
);
145 PspCreateThread(OUT PHANDLE ThreadHandle
,
146 IN ACCESS_MASK DesiredAccess
,
147 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
148 IN HANDLE ProcessHandle
,
149 IN PEPROCESS TargetProcess
,
150 OUT PCLIENT_ID ClientId
,
151 IN PCONTEXT ThreadContext
,
152 IN PINITIAL_TEB InitialTeb
,
153 IN BOOLEAN CreateSuspended
,
154 IN PKSTART_ROUTINE StartRoutine OPTIONAL
,
155 IN PVOID StartContext OPTIONAL
)
162 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
164 HANDLE_TABLE_ENTRY CidEntry
;
165 ULONG_PTR KernelStack
;
168 /* If we were called from PsCreateSystemThread, then we're kernel mode */
169 if (StartRoutine
) PreviousMode
= KernelMode
;
171 /* Reference the Process by handle or pointer, depending on what we got */
174 /* Normal thread or System Thread */
175 Status
= ObReferenceObjectByHandle(ProcessHandle
,
176 PROCESS_CREATE_THREAD
,
184 /* System thread inside System Process, or Normal Thread with a bug */
187 /* Reference the Process by Pointer */
188 ObReferenceObject(TargetProcess
);
189 Process
= TargetProcess
;
190 Status
= STATUS_SUCCESS
;
194 /* Fake ObReference returning this */
195 Status
= STATUS_INVALID_HANDLE
;
199 /* Check for success */
200 if (!NT_SUCCESS(Status
)) return Status
;
202 /* Also make sure that User-Mode isn't trying to create a system thread */
203 if ((PreviousMode
!= KernelMode
) && (Process
== PsInitialSystemProcess
))
205 ObDereferenceObject(Process
);
206 return STATUS_INVALID_HANDLE
;
209 /* Create Thread Object */
210 Status
= ObCreateObject(PreviousMode
,
219 if (!NT_SUCCESS(Status
))
221 /* We failed; dereference the process and exit */
222 ObDereferenceObject(Process
);
226 /* Zero the Object entirely */
227 RtlZeroMemory(Thread
, sizeof(ETHREAD
));
229 /* Initialize rundown protection */
230 ExInitializeRundownProtection(&Thread
->RundownProtect
);
232 /* Set the Process CID */
233 Thread
->ThreadsProcess
= Process
;
234 Thread
->Cid
.UniqueProcess
= Process
->UniqueProcessId
;
236 /* Create Cid Handle */
237 CidEntry
.Object
= Thread
;
238 CidEntry
.GrantedAccess
= 0;
239 Thread
->Cid
.UniqueThread
= ExCreateHandle(PspCidTable
, &CidEntry
);
240 if (!Thread
->Cid
.UniqueThread
)
242 /* We couldn't create the CID, dereference the thread and fail */
243 ObDereferenceObject(Thread
);
244 return STATUS_INSUFFICIENT_RESOURCES
;
247 /* Save the read cluster size */
248 Thread
->ReadClusterSize
= MmReadClusterSize
;
250 /* Initialize the LPC Reply Semaphore */
251 KeInitializeSemaphore(&Thread
->LpcReplySemaphore
, 0, 1);
253 /* Initialize the list heads and locks */
254 InitializeListHead(&Thread
->LpcReplyChain
);
255 InitializeListHead(&Thread
->IrpList
);
256 InitializeListHead(&Thread
->PostBlockList
);
257 InitializeListHead(&Thread
->ActiveTimerListHead
);
258 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
260 /* Acquire rundown protection */
261 ExAcquireRundownProtection(&Process
->RundownProtect
);
263 /* Allocate Stack for non-GUI Thread */
264 KernelStack
= (ULONG_PTR
)MmCreateKernelStack(FALSE
) + KERNEL_STACK_SIZE
;
266 /* Now let the kernel initialize the context */
269 /* User-mode Thread, create Teb */
270 TebBase
= MmCreateTeb(Process
, &Thread
->Cid
, InitialTeb
);
273 /* Failed to create the TEB. Release rundown and dereference */
274 ExReleaseRundownProtection(&Process
->RundownProtect
);
275 ObDereferenceObject(Thread
);
276 return STATUS_INSUFFICIENT_RESOURCES
;
279 /* Set the Start Addresses */
280 Thread
->StartAddress
= (PVOID
)ThreadContext
->Eip
;
281 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->Eax
;
283 /* Let the kernel intialize the Thread */
284 KeInitializeThread(&Process
->Pcb
,
286 PspUserThreadStartup
,
296 Thread
->StartAddress
= StartRoutine
;
297 InterlockedOr((PLONG
)&Thread
->CrossThreadFlags
, CT_SYSTEM_THREAD_BIT
);
299 /* Let the kernel intialize the Thread */
300 KeInitializeThread(&Process
->Pcb
,
302 PspSystemThreadStartup
,
311 * Insert the Thread into the Process's Thread List
312 * Note, this is the ETHREAD Thread List. It is removed in
313 * ps/kill.c!PspExitThread.
315 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
316 Process
->ActiveThreads
++;
318 /* Release rundown */
319 ExReleaseRundownProtection(&Process
->RundownProtect
);
322 //WmiTraceProcess(Process, TRUE);
323 //WmiTraceThread(Thread, InitialTeb, TRUE);
325 /* Notify Thread Creation */
326 PspRunCreateThreadNotifyRoutines(Thread
, TRUE
);
328 /* Suspend the Thread if we have to */
331 KeSuspendThread(&Thread
->Tcb
);
334 /* Check if we were already terminated */
335 if (Thread
->Terminated
)
337 /* Force us to wake up to terminate */
338 KeForceResumeThread(&Thread
->Tcb
);
341 /* Reference ourselves as a keep-alive */
342 ObReferenceObject(Thread
);
344 /* Insert the Thread into the Object Manager */
345 Status
= ObInsertObject((PVOID
)Thread
,
351 if(NT_SUCCESS(Status
))
353 /* Wrap in SEH to protect against bad user-mode pointers */
356 /* Return Cid and Handle */
357 if(ClientId
) *ClientId
= Thread
->Cid
;
358 *ThreadHandle
= hThread
;
362 Status
= _SEH_GetExceptionCode();
367 /* FIXME: SECURITY */
369 /* Dispatch thread */
370 OldIrql
= KeAcquireDispatcherDatabaseLock ();
371 KiReadyThread(&Thread
->Tcb
);
372 KeReleaseDispatcherDatabaseLock(OldIrql
);
383 PsCreateSystemThread(PHANDLE ThreadHandle
,
384 ACCESS_MASK DesiredAccess
,
385 POBJECT_ATTRIBUTES ObjectAttributes
,
386 HANDLE ProcessHandle
,
388 PKSTART_ROUTINE StartRoutine
,
391 PEPROCESS TargetProcess
= NULL
;
392 HANDLE Handle
= ProcessHandle
;
395 /* Check if we have a handle. If not, use the System Process */
399 TargetProcess
= PsInitialSystemProcess
;
402 /* Call the shared function */
403 return PspCreateThread(ThreadHandle
,
421 PsLookupThreadByThreadId(IN HANDLE ThreadId
,
422 OUT PETHREAD
*Thread
)
424 PHANDLE_TABLE_ENTRY CidEntry
;
425 PETHREAD FoundThread
;
426 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
428 KeEnterCriticalRegion();
430 /* Get the CID Handle Entry */
431 if ((CidEntry
= ExMapHandleToPointer(PspCidTable
,
434 /* Get the Process */
435 FoundThread
= CidEntry
->Object
;
437 /* Make sure it's really a process */
438 if (FoundThread
->Tcb
.DispatcherHeader
.Type
== ThreadObject
)
440 /* Reference and return it */
441 ObReferenceObject(FoundThread
);
442 *Thread
= FoundThread
;
443 Status
= STATUS_SUCCESS
;
446 /* Unlock the Entry */
447 ExUnlockHandleTableEntry(PspCidTable
, CidEntry
);
450 /* Return to caller */
451 KeLeaveCriticalRegion();
460 PsGetCurrentThreadId(VOID
)
462 return(PsGetCurrentThread()->Cid
.UniqueThread
);
470 PsGetThreadFreezeCount(PETHREAD Thread
)
472 return Thread
->Tcb
.FreezeCount
;
480 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread
)
482 return Thread
->HardErrorsAreDisabled
;
490 PsGetThreadId(PETHREAD Thread
)
492 return Thread
->Cid
.UniqueThread
;
500 PsGetThreadProcess(PETHREAD Thread
)
502 return Thread
->ThreadsProcess
;
510 PsGetThreadProcessId(PETHREAD Thread
)
512 return Thread
->Cid
.UniqueProcess
;
520 PsGetThreadSessionId(PETHREAD Thread
)
522 return (HANDLE
)Thread
->ThreadsProcess
->Session
;
530 PsGetThreadTeb(PETHREAD Thread
)
532 return Thread
->Tcb
.Teb
;
540 PsGetThreadWin32Thread(PETHREAD Thread
)
542 return Thread
->Tcb
.Win32Thread
;
550 PsGetCurrentThreadPreviousMode(VOID
)
552 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;
560 PsGetCurrentThreadStackBase(VOID
)
562 return PsGetCurrentThread()->Tcb
.StackBase
;
570 PsGetCurrentThreadStackLimit(VOID
)
572 return (PVOID
)PsGetCurrentThread()->Tcb
.StackLimit
;
580 PsIsThreadTerminating(IN PETHREAD Thread
)
582 return (Thread
->Terminated
? TRUE
: FALSE
);
590 PsIsSystemThread(PETHREAD Thread
)
592 return (Thread
->SystemThread
? TRUE
: FALSE
);
600 PsIsThreadImpersonating(PETHREAD Thread
)
602 return Thread
->ActiveImpersonationInfo
;
610 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread
,
611 BOOLEAN HardErrorsAreDisabled
)
613 Thread
->HardErrorsAreDisabled
= HardErrorsAreDisabled
;
621 PsSetThreadWin32Thread(PETHREAD Thread
,
624 Thread
->Tcb
.Win32Thread
= Win32Thread
;
629 NtCreateThread(OUT PHANDLE ThreadHandle
,
630 IN ACCESS_MASK DesiredAccess
,
631 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
632 IN HANDLE ProcessHandle
,
633 OUT PCLIENT_ID ClientId
,
634 IN PCONTEXT ThreadContext
,
635 IN PINITIAL_TEB InitialTeb
,
636 IN BOOLEAN CreateSuspended
)
638 INITIAL_TEB SafeInitialTeb
;
639 NTSTATUS Status
= STATUS_SUCCESS
;
642 /* Check if this was from user-mode */
643 if(KeGetPreviousMode() != KernelMode
)
645 /* Make sure that we got a context */
646 if (!ThreadContext
) return STATUS_INVALID_PARAMETER
;
651 /* Make sure the handle pointer we got is valid */
652 ProbeForWriteHandle(ThreadHandle
);
654 /* Check if the caller wants a client id */
657 /* Make sure we can write to it */
658 ProbeForWrite(ClientId
, sizeof(CLIENT_ID
), sizeof(ULONG
));
661 /* Make sure that the entire context is readable */
662 ProbeForRead(ThreadContext
, sizeof(CONTEXT
), sizeof(ULONG
));
664 /* Check the Initial TEB */
665 ProbeForRead(InitialTeb
, sizeof(INITIAL_TEB
), sizeof(ULONG
));
666 SafeInitialTeb
= *InitialTeb
;
670 Status
= _SEH_GetExceptionCode();
674 /* Handle any failures in our SEH checks */
675 if (!NT_SUCCESS(Status
)) return Status
;
679 /* Use the Initial TEB as is */
680 SafeInitialTeb
= *InitialTeb
;
683 /* Call the shared function */
684 return PspCreateThread(ThreadHandle
,
702 NtOpenThread(OUT PHANDLE ThreadHandle
,
703 IN ACCESS_MASK DesiredAccess
,
704 IN POBJECT_ATTRIBUTES ObjectAttributes
,
705 IN PCLIENT_ID ClientId OPTIONAL
)
707 KPROCESSOR_MODE PreviousMode
;
708 CLIENT_ID SafeClientId
;
709 ULONG Attributes
= 0;
710 HANDLE hThread
= NULL
;
711 NTSTATUS Status
= STATUS_SUCCESS
;
713 BOOLEAN HasObjectName
= FALSE
;
717 PreviousMode
= KeGetPreviousMode();
719 /* Probe the paraemeters */
720 if(PreviousMode
!= KernelMode
)
724 ProbeForWriteHandle(ThreadHandle
);
728 ProbeForRead(ClientId
,
732 SafeClientId
= *ClientId
;
733 ClientId
= &SafeClientId
;
736 /* just probe the object attributes structure, don't capture it
737 completely. This is done later if necessary */
738 ProbeForRead(ObjectAttributes
,
739 sizeof(OBJECT_ATTRIBUTES
),
741 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
742 Attributes
= ObjectAttributes
->Attributes
;
746 Status
= _SEH_GetExceptionCode();
750 if(!NT_SUCCESS(Status
)) return Status
;
754 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
755 Attributes
= ObjectAttributes
->Attributes
;
758 if (HasObjectName
&& ClientId
!= NULL
)
760 /* can't pass both, n object name and a client id */
761 return STATUS_INVALID_PARAMETER_MIX
;
764 /* Open by name if one was given */
768 Status
= ObOpenObjectByName(ObjectAttributes
,
778 /* Open by Thread ID */
779 if (ClientId
->UniqueProcess
)
781 /* Get the Process */
782 Status
= PsLookupProcessThreadByCid(ClientId
,
788 /* Get the Process */
789 Status
= PsLookupThreadByThreadId(ClientId
->UniqueThread
,
793 /* Fail if we didn't find anything */
794 if(!NT_SUCCESS(Status
)) return Status
;
796 /* Open the Thread Object */
797 Status
= ObOpenObjectByPointer(Thread
,
805 /* Dereference the thread */
806 ObDereferenceObject(Thread
);
810 /* neither an object name nor a client id was passed */
811 return STATUS_INVALID_PARAMETER_MIX
;
814 /* Check for success */
815 if (NT_SUCCESS(Status
))
817 /* Protect against bad user-mode pointers */
820 /* Write back the handle */
821 *ThreadHandle
= hThread
;
825 Status
= _SEH_GetExceptionCode();
836 NtYieldExecution(VOID
)
838 KiDispatchThread(Ready
);
839 return(STATUS_SUCCESS
);
846 /* Check and Alert Thread if needed */
847 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED
: STATUS_SUCCESS
;
855 ExGetPreviousMode (VOID
)
857 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;