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)
10 /* INCLUDES ****************************************************************/
16 /* GLOBALS ******************************************************************/
18 extern BOOLEAN CcPfEnablePrefetcher
;
19 extern ULONG MmReadClusterSize
;
20 POBJECT_TYPE PsThreadType
= NULL
;
22 /* PRIVATE FUNCTIONS *********************************************************/
26 PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine
,
27 IN PVOID StartContext
)
31 BOOLEAN DeadThread
= FALSE
;
34 PSTRACE(PS_THREAD_DEBUG
,
35 "StartRoutine: %p StartContext: %p\n", StartRoutine
, StartContext
);
37 /* Go to Passive Level */
38 KeLowerIrql(PASSIVE_LEVEL
);
39 Thread
= PsGetCurrentThread();
41 /* Check if the thread is dead */
42 if (Thread
->DeadThread
)
44 /* Remember that we're dead */
49 /* Get the Locale ID and save Preferred Proc */
51 Teb
->CurrentLocale
= MmGetSessionLocaleId();
52 Teb
->IdealProcessor
= Thread
->Tcb
.IdealProcessor
;
55 /* Check if this is a dead thread, or if we're hiding */
56 if (!(Thread
->DeadThread
) && !(Thread
->HideFromDebugger
))
58 /* We're not, so notify the debugger */
59 DbgkCreateThread(Thread
, StartContext
);
62 /* Make sure we're not already dead */
65 /* Check if the Prefetcher is enabled */
66 if (CcPfEnablePrefetcher
)
68 /* FIXME: Prepare to prefetch this process */
72 KeRaiseIrql(APC_LEVEL
, &OldIrql
);
74 /* Queue the User APC */
75 KiInitializeUserApc(NULL
,
76 (PVOID
)((ULONG_PTR
)Thread
->Tcb
.InitialStack
-
79 PspSystemDllEntryPoint
,
84 /* Lower it back to passive */
85 KeLowerIrql(PASSIVE_LEVEL
);
89 /* We're dead, kill us now */
90 PspTerminateThreadByPointer(Thread
,
91 STATUS_THREAD_IS_TERMINATING
,
95 /* Do we have a cookie set yet? */
96 if (!SharedUserData
->Cookie
)
98 LARGE_INTEGER SystemTime
;
102 /* Generate a new cookie */
103 KeQuerySystemTime(&SystemTime
);
104 Prcb
= KeGetCurrentPrcb();
105 NewCookie
= Prcb
->MmPageFaultCount
^ Prcb
->InterruptTime
^
106 SystemTime
.u
.LowPart
^ SystemTime
.u
.HighPart
^
107 (ULONG_PTR
)&SystemTime
;
109 /* Set the new cookie*/
110 InterlockedCompareExchange((LONG
*)&SharedUserData
->Cookie
,
118 PspUnhandledExceptionInSystemThread(PEXCEPTION_POINTERS ExceptionPointers
)
120 /* Print debugging information */
121 DPRINT1("PS: Unhandled Kernel Mode Exception Pointers = 0x%p\n",
123 DPRINT1("Code %x Addr %p Info0 %p Info1 %p Info2 %p Info3 %p\n",
124 ExceptionPointers
->ExceptionRecord
->ExceptionCode
,
125 ExceptionPointers
->ExceptionRecord
->ExceptionAddress
,
126 ExceptionPointers
->ExceptionRecord
->ExceptionInformation
[0],
127 ExceptionPointers
->ExceptionRecord
->ExceptionInformation
[1],
128 ExceptionPointers
->ExceptionRecord
->ExceptionInformation
[2],
129 ExceptionPointers
->ExceptionRecord
->ExceptionInformation
[3]);
131 /* Bugcheck the system */
132 KeBugCheckEx(SYSTEM_THREAD_EXCEPTION_NOT_HANDLED
,
133 ExceptionPointers
->ExceptionRecord
->ExceptionCode
,
134 (ULONG_PTR
)ExceptionPointers
->ExceptionRecord
->ExceptionAddress
,
135 (ULONG_PTR
)ExceptionPointers
->ExceptionRecord
,
136 (ULONG_PTR
)ExceptionPointers
->ContextRecord
);
142 PspSystemThreadStartup(IN PKSTART_ROUTINE StartRoutine
,
143 IN PVOID StartContext
)
146 PSTRACE(PS_THREAD_DEBUG
,
147 "StartRoutine: %p StartContext: %p\n", StartRoutine
, StartContext
);
149 /* Unlock the dispatcher Database */
150 KeLowerIrql(PASSIVE_LEVEL
);
151 Thread
= PsGetCurrentThread();
153 /* Make sure the thread isn't gone */
156 if (!(Thread
->Terminated
) && !(Thread
->DeadThread
))
158 /* Call the Start Routine */
159 StartRoutine(StartContext
);
162 _SEH2_EXCEPT(PspUnhandledExceptionInSystemThread(_SEH2_GetExceptionInformation()))
164 /* Bugcheck if we got here */
165 KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED
);
169 /* Exit the thread */
170 PspTerminateThreadByPointer(Thread
, STATUS_SUCCESS
, TRUE
);
175 PspCreateThread(OUT PHANDLE ThreadHandle
,
176 IN ACCESS_MASK DesiredAccess
,
177 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
178 IN HANDLE ProcessHandle
,
179 IN PEPROCESS TargetProcess
,
180 OUT PCLIENT_ID ClientId
,
181 IN PCONTEXT ThreadContext
,
182 IN PINITIAL_TEB InitialTeb
,
183 IN BOOLEAN CreateSuspended
,
184 IN PKSTART_ROUTINE StartRoutine OPTIONAL
,
185 IN PVOID StartContext OPTIONAL
)
191 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
192 NTSTATUS Status
, AccessStatus
;
193 HANDLE_TABLE_ENTRY CidEntry
;
194 ACCESS_STATE LocalAccessState
;
195 PACCESS_STATE AccessState
= &LocalAccessState
;
196 AUX_ACCESS_DATA AuxData
;
197 BOOLEAN Result
, SdAllocated
;
198 PSECURITY_DESCRIPTOR SecurityDescriptor
;
199 SECURITY_SUBJECT_CONTEXT SubjectContext
;
201 PSTRACE(PS_THREAD_DEBUG
,
202 "ThreadContext: %p TargetProcess: %p ProcessHandle: %p\n",
203 ThreadContext
, TargetProcess
, ProcessHandle
);
205 /* If we were called from PsCreateSystemThread, then we're kernel mode */
206 if (StartRoutine
) PreviousMode
= KernelMode
;
208 /* Reference the Process by handle or pointer, depending on what we got */
211 /* Normal thread or System Thread */
212 Status
= ObReferenceObjectByHandle(ProcessHandle
,
213 PROCESS_CREATE_THREAD
,
222 /* System thread inside System Process, or Normal Thread with a bug */
225 /* Reference the Process by Pointer */
226 ObReferenceObject(TargetProcess
);
227 Process
= TargetProcess
;
228 Status
= STATUS_SUCCESS
;
232 /* Fake ObReference returning this */
233 Status
= STATUS_INVALID_HANDLE
;
237 /* Check for success */
238 if (!NT_SUCCESS(Status
)) return Status
;
240 /* Also make sure that User-Mode isn't trying to create a system thread */
241 if ((PreviousMode
!= KernelMode
) && (Process
== PsInitialSystemProcess
))
244 ObDereferenceObject(Process
);
245 return STATUS_INVALID_HANDLE
;
248 /* Create Thread Object */
249 Status
= ObCreateObject(PreviousMode
,
258 if (!NT_SUCCESS(Status
))
260 /* We failed; dereference the process and exit */
261 ObDereferenceObject(Process
);
265 /* Zero the Object entirely */
266 RtlZeroMemory(Thread
, sizeof(ETHREAD
));
268 /* Initialize rundown protection */
269 ExInitializeRundownProtection(&Thread
->RundownProtect
);
271 /* Initialize exit code */
272 Thread
->ExitStatus
= STATUS_PENDING
;
274 /* Set the Process CID */
275 Thread
->ThreadsProcess
= Process
;
276 Thread
->Cid
.UniqueProcess
= Process
->UniqueProcessId
;
278 /* Create Cid Handle */
279 CidEntry
.Object
= Thread
;
280 CidEntry
.GrantedAccess
= 0;
281 Thread
->Cid
.UniqueThread
= ExCreateHandle(PspCidTable
, &CidEntry
);
282 if (!Thread
->Cid
.UniqueThread
)
284 /* We couldn't create the CID, dereference the thread and fail */
285 ObDereferenceObject(Thread
);
286 return STATUS_INSUFFICIENT_RESOURCES
;
289 /* Save the read cluster size */
290 Thread
->ReadClusterSize
= MmReadClusterSize
;
292 /* Initialize the LPC Reply Semaphore */
293 KeInitializeSemaphore(&Thread
->LpcReplySemaphore
, 0, 1);
295 /* Initialize the list heads and locks */
296 InitializeListHead(&Thread
->LpcReplyChain
);
297 InitializeListHead(&Thread
->IrpList
);
298 InitializeListHead(&Thread
->PostBlockList
);
299 InitializeListHead(&Thread
->ActiveTimerListHead
);
300 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
302 /* Acquire rundown protection */
303 if (!ExAcquireRundownProtection (&Process
->RundownProtect
))
306 ObDereferenceObject(Thread
);
307 return STATUS_PROCESS_IS_TERMINATING
;
310 /* Now let the kernel initialize the context */
313 /* User-mode Thread, create Teb */
314 TebBase
= MmCreateTeb(Process
, &Thread
->Cid
, InitialTeb
);
317 /* Failed to create the TEB. Release rundown and dereference */
318 ExReleaseRundownProtection(&Process
->RundownProtect
);
319 ObDereferenceObject(Thread
);
320 return STATUS_INSUFFICIENT_RESOURCES
;
323 /* Set the Start Addresses */
325 Thread
->StartAddress
= (PVOID
)ThreadContext
->Eip
;
326 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->Eax
;
327 #elif defined(_M_PPC)
328 Thread
->StartAddress
= (PVOID
)ThreadContext
->Dr0
;
329 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->Gpr3
;
330 #elif defined(_M_MIPS)
331 Thread
->StartAddress
= (PVOID
)ThreadContext
->Psr
;
332 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->IntA0
;
333 #elif defined(_M_ARM)
334 Thread
->StartAddress
= (PVOID
)ThreadContext
->Pc
;
335 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->R0
;
336 #elif defined(_M_AMD64)
337 Thread
->StartAddress
= (PVOID
)ThreadContext
->Rip
;
338 Thread
->Win32StartAddress
= (PVOID
)ThreadContext
->Rax
;
340 #error Unknown architecture
343 /* Let the kernel intialize the Thread */
344 Status
= KeInitThread(&Thread
->Tcb
,
346 PspUserThreadStartup
,
348 Thread
->StartAddress
,
356 Thread
->StartAddress
= StartRoutine
;
357 PspSetCrossThreadFlag(Thread
, CT_SYSTEM_THREAD_BIT
);
359 /* Let the kernel intialize the Thread */
360 Status
= KeInitThread(&Thread
->Tcb
,
362 PspSystemThreadStartup
,
370 /* Check if we failed */
371 if (!NT_SUCCESS(Status
))
373 /* Delete the TEB if we had done */
374 if (TebBase
) MmDeleteTeb(Process
, TebBase
);
376 /* Release rundown and dereference */
377 ExReleaseRundownProtection(&Process
->RundownProtect
);
378 ObDereferenceObject(Thread
);
382 /* Lock the process */
383 KeEnterCriticalRegion();
384 ExAcquirePushLockExclusive(&Process
->ProcessLock
);
386 /* Make sure the proces didn't just die on us */
387 if (Process
->ProcessDelete
) goto Quickie
;
389 /* Check if the thread was ours, terminated and it was user mode */
390 if ((Thread
->Terminated
) &&
392 (Thread
->ThreadsProcess
== Process
))
394 /* Cleanup, we don't want to start it up and context switch */
399 * Insert the Thread into the Process's Thread List
400 * Note, this is the ETHREAD Thread List. It is removed in
401 * ps/kill.c!PspExitThread.
403 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
404 Process
->ActiveThreads
++;
406 /* Start the thread */
407 KeStartThread(&Thread
->Tcb
);
409 /* Release the process lock */
410 ExReleasePushLockExclusive(&Process
->ProcessLock
);
411 KeLeaveCriticalRegion();
413 /* Release rundown */
414 ExReleaseRundownProtection(&Process
->RundownProtect
);
417 //WmiTraceProcess(Process, TRUE);
418 //WmiTraceThread(Thread, InitialTeb, TRUE);
420 /* Notify Thread Creation */
421 PspRunCreateThreadNotifyRoutines(Thread
, TRUE
);
423 /* Reference ourselves as a keep-alive */
424 ObReferenceObjectEx(Thread
, 2);
426 /* Suspend the Thread if we have to */
427 if (CreateSuspended
) KeSuspendThread(&Thread
->Tcb
);
429 /* Check if we were already terminated */
430 if (Thread
->Terminated
) KeForceResumeThread(&Thread
->Tcb
);
432 /* Create an access state */
433 Status
= SeCreateAccessStateEx(NULL
,
435 PsGetCurrentProcess() : Process
,
439 &PsThreadType
->TypeInfo
.GenericMapping
);
440 if (!NT_SUCCESS(Status
))
442 /* Access state failed, thread is dead */
443 PspSetCrossThreadFlag(Thread
, CT_DEAD_THREAD_BIT
);
445 /* If we were suspended, wake it up */
446 if (CreateSuspended
) KeResumeThread(&Thread
->Tcb
);
448 /* Dispatch thread */
449 KeReadyThread(&Thread
->Tcb
);
451 /* Dereference completely to kill it */
452 ObDereferenceObjectEx(Thread
, 2);
456 /* Insert the Thread into the Object Manager */
457 Status
= ObInsertObject(Thread
,
464 /* Delete the access state if we had one */
465 if (AccessState
) SeDeleteAccessState(AccessState
);
467 /* Check for success */
468 if (NT_SUCCESS(Status
))
470 /* Wrap in SEH to protect against bad user-mode pointers */
473 /* Return Cid and Handle */
474 if (ClientId
) *ClientId
= Thread
->Cid
;
475 *ThreadHandle
= hThread
;
477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
479 /* Get the exception code */
480 Status
= _SEH2_GetExceptionCode();
482 /* Thread insertion failed, thread is dead */
483 PspSetCrossThreadFlag(Thread
, CT_DEAD_THREAD_BIT
);
485 /* If we were suspended, wake it up */
486 if (CreateSuspended
) KeResumeThread(&Thread
->Tcb
);
488 /* Dispatch thread */
489 KeReadyThread(&Thread
->Tcb
);
491 /* Dereference it, leaving only the keep-alive */
492 ObDereferenceObject(Thread
);
494 /* Close its handle, killing it */
495 ObCloseHandle(ThreadHandle
, PreviousMode
);
498 if (!NT_SUCCESS(Status
)) return Status
;
502 /* Thread insertion failed, thread is dead */
503 PspSetCrossThreadFlag(Thread
, CT_DEAD_THREAD_BIT
);
505 /* If we were suspended, wake it up */
506 if (CreateSuspended
) KeResumeThread(&Thread
->Tcb
);
509 /* Get the create time */
510 KeQuerySystemTime(&Thread
->CreateTime
);
511 ASSERT(!(Thread
->CreateTime
.HighPart
& 0xF0000000));
513 /* Make sure the thread isn't dead */
514 if (!Thread
->DeadThread
)
516 /* Get the thread's SD */
517 Status
= ObGetObjectSecurity(Thread
,
520 if (!NT_SUCCESS(Status
))
522 /* Thread insertion failed, thread is dead */
523 PspSetCrossThreadFlag(Thread
, CT_DEAD_THREAD_BIT
);
525 /* If we were suspended, wake it up */
526 if (CreateSuspended
) KeResumeThread(&Thread
->Tcb
);
528 /* Dispatch thread */
529 KeReadyThread(&Thread
->Tcb
);
531 /* Dereference it, leaving only the keep-alive */
532 ObDereferenceObject(Thread
);
534 /* Close its handle, killing it */
535 ObCloseHandle(ThreadHandle
, PreviousMode
);
539 /* Create the subject context */
540 SubjectContext
.ProcessAuditId
= Process
;
541 SubjectContext
.PrimaryToken
= PsReferencePrimaryToken(Process
);
542 SubjectContext
.ClientToken
= NULL
;
544 /* Do the access check */
545 Result
= SeAccessCheck(SecurityDescriptor
,
551 &PsThreadType
->TypeInfo
.GenericMapping
,
553 &Thread
->GrantedAccess
,
556 /* Dereference the token and let go the SD */
557 ObFastDereferenceObject(&Process
->Token
,
558 SubjectContext
.PrimaryToken
);
559 ObReleaseObjectSecurity(SecurityDescriptor
, SdAllocated
);
561 /* Remove access if it failed */
562 if (!Result
) Process
->GrantedAccess
= 0;
564 /* Set least some minimum access */
565 Thread
->GrantedAccess
|= (THREAD_TERMINATE
|
566 THREAD_SET_INFORMATION
|
567 THREAD_QUERY_INFORMATION
);
571 /* Set the thread access mask to maximum */
572 Thread
->GrantedAccess
= THREAD_ALL_ACCESS
;
575 /* Dispatch thread */
576 KeReadyThread(&Thread
->Tcb
);
578 /* Dereference it, leaving only the keep-alive */
579 ObDereferenceObject(Thread
);
584 /* Most annoying failure case ever, where we undo almost all manually */
586 /* When we get here, the process is locked, unlock it */
587 ExReleasePushLockExclusive(&Process
->ProcessLock
);
588 KeLeaveCriticalRegion();
590 /* Uninitailize it */
591 KeUninitThread(&Thread
->Tcb
);
593 /* If we had a TEB, delete it */
594 if (TebBase
) MmDeleteTeb(Process
, TebBase
);
596 /* Release rundown protection, which we also hold */
597 ExReleaseRundownProtection(&Process
->RundownProtect
);
599 /* Dereference the thread and return failure */
600 ObDereferenceObject(Thread
);
601 return STATUS_PROCESS_IS_TERMINATING
;
604 /* PUBLIC FUNCTIONS **********************************************************/
611 PsCreateSystemThread(OUT PHANDLE ThreadHandle
,
612 IN ACCESS_MASK DesiredAccess
,
613 IN POBJECT_ATTRIBUTES ObjectAttributes
,
614 IN HANDLE ProcessHandle
,
615 IN PCLIENT_ID ClientId
,
616 IN PKSTART_ROUTINE StartRoutine
,
617 IN PVOID StartContext
)
619 PEPROCESS TargetProcess
= NULL
;
620 HANDLE Handle
= ProcessHandle
;
622 PSTRACE(PS_THREAD_DEBUG
,
623 "ProcessHandle: %p StartRoutine: %p StartContext: %p\n",
624 ProcessHandle
, StartRoutine
, StartContext
);
626 /* Check if we have a handle. If not, use the System Process */
630 TargetProcess
= PsInitialSystemProcess
;
633 /* Call the shared function */
634 return PspCreateThread(ThreadHandle
,
652 PsLookupThreadByThreadId(IN HANDLE ThreadId
,
653 OUT PETHREAD
*Thread
)
655 PHANDLE_TABLE_ENTRY CidEntry
;
656 PETHREAD FoundThread
;
657 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
659 PSTRACE(PS_THREAD_DEBUG
, "ThreadId: %p\n", ThreadId
);
660 KeEnterCriticalRegion();
662 /* Get the CID Handle Entry */
663 CidEntry
= ExMapHandleToPointer(PspCidTable
, ThreadId
);
666 /* Get the Process */
667 FoundThread
= CidEntry
->Object
;
669 /* Make sure it's really a process */
670 if (FoundThread
->Tcb
.DispatcherHeader
.Type
== ThreadObject
)
672 /* Safe Reference and return it */
673 if (ObReferenceObjectSafe(FoundThread
))
675 *Thread
= FoundThread
;
676 Status
= STATUS_SUCCESS
;
680 /* Unlock the Entry */
681 ExUnlockHandleTableEntry(PspCidTable
, CidEntry
);
684 /* Return to caller */
685 KeLeaveCriticalRegion();
694 PsGetCurrentThreadId(VOID
)
696 return PsGetCurrentThread()->Cid
.UniqueThread
;
704 PsGetThreadFreezeCount(IN PETHREAD Thread
)
706 return Thread
->Tcb
.FreezeCount
;
714 PsGetThreadHardErrorsAreDisabled(IN PETHREAD Thread
)
716 return Thread
->HardErrorsAreDisabled
? TRUE
: FALSE
;
724 PsGetThreadId(IN PETHREAD Thread
)
726 return Thread
->Cid
.UniqueThread
;
734 PsGetThreadProcess(IN PETHREAD Thread
)
736 return Thread
->ThreadsProcess
;
744 PsGetThreadProcessId(IN PETHREAD Thread
)
746 return Thread
->Cid
.UniqueProcess
;
754 PsGetThreadSessionId(IN PETHREAD Thread
)
756 return (HANDLE
)Thread
->ThreadsProcess
->Session
;
764 PsGetThreadTeb(IN PETHREAD Thread
)
766 return Thread
->Tcb
.Teb
;
774 PsGetThreadWin32Thread(IN PETHREAD Thread
)
776 return Thread
->Tcb
.Win32Thread
;
784 PsGetCurrentThreadPreviousMode(VOID
)
786 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;
794 PsGetCurrentThreadStackBase(VOID
)
796 return PsGetCurrentThread()->Tcb
.StackBase
;
804 PsGetCurrentThreadStackLimit(VOID
)
806 return (PVOID
)PsGetCurrentThread()->Tcb
.StackLimit
;
814 PsIsThreadTerminating(IN PETHREAD Thread
)
816 return Thread
->Terminated
? TRUE
: FALSE
;
824 PsIsSystemThread(IN PETHREAD Thread
)
826 return Thread
->SystemThread
? TRUE
: FALSE
;
834 PsIsThreadImpersonating(IN PETHREAD Thread
)
836 return Thread
->ActiveImpersonationInfo
? TRUE
: FALSE
;
844 PsSetThreadHardErrorsAreDisabled(IN PETHREAD Thread
,
845 IN BOOLEAN HardErrorsAreDisabled
)
847 Thread
->HardErrorsAreDisabled
= HardErrorsAreDisabled
;
855 PsGetCurrentThreadWin32Thread(VOID
)
857 return PsGetCurrentThread()->Tcb
.Win32Thread
;
865 PsSetThreadWin32Thread(IN PETHREAD Thread
,
866 IN PVOID Win32Thread
)
868 Thread
->Tcb
.Win32Thread
= Win32Thread
;
873 NtCreateThread(OUT PHANDLE ThreadHandle
,
874 IN ACCESS_MASK DesiredAccess
,
875 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL
,
876 IN HANDLE ProcessHandle
,
877 OUT PCLIENT_ID ClientId
,
878 IN PCONTEXT ThreadContext
,
879 IN PINITIAL_TEB InitialTeb
,
880 IN BOOLEAN CreateSuspended
)
882 INITIAL_TEB SafeInitialTeb
;
883 NTSTATUS Status
= STATUS_SUCCESS
;
885 PSTRACE(PS_THREAD_DEBUG
,
886 "ProcessHandle: %p Context: %p\n", ProcessHandle
, ThreadContext
);
888 /* Check if this was from user-mode */
889 if (KeGetPreviousMode() != KernelMode
)
891 /* Make sure that we got a context */
892 if (!ThreadContext
) return STATUS_INVALID_PARAMETER
;
897 /* Make sure the handle pointer we got is valid */
898 ProbeForWriteHandle(ThreadHandle
);
900 /* Check if the caller wants a client id */
903 /* Make sure we can write to it */
904 ProbeForWrite(ClientId
, sizeof(CLIENT_ID
), sizeof(ULONG
));
907 /* Make sure that the entire context is readable */
908 ProbeForRead(ThreadContext
, sizeof(CONTEXT
), sizeof(ULONG
));
910 /* Check the Initial TEB */
911 ProbeForRead(InitialTeb
, sizeof(INITIAL_TEB
), sizeof(ULONG
));
912 SafeInitialTeb
= *InitialTeb
;
914 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
916 Status
= _SEH2_GetExceptionCode();
919 if (!NT_SUCCESS(Status
)) return Status
;
923 /* Use the Initial TEB as is */
924 SafeInitialTeb
= *InitialTeb
;
927 /* Call the shared function */
928 return PspCreateThread(ThreadHandle
,
946 NtOpenThread(OUT PHANDLE ThreadHandle
,
947 IN ACCESS_MASK DesiredAccess
,
948 IN POBJECT_ATTRIBUTES ObjectAttributes
,
949 IN PCLIENT_ID ClientId OPTIONAL
)
951 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
952 CLIENT_ID SafeClientId
;
953 ULONG Attributes
= 0;
954 HANDLE hThread
= NULL
;
955 NTSTATUS Status
= STATUS_SUCCESS
;
957 BOOLEAN HasObjectName
= FALSE
;
958 ACCESS_STATE AccessState
;
959 AUX_ACCESS_DATA AuxData
;
961 PSTRACE(PS_THREAD_DEBUG
,
962 "ClientId: %p ObjectAttributes: %p\n", ClientId
, ObjectAttributes
);
964 /* Check if we were called from user mode */
965 if (PreviousMode
!= KernelMode
)
967 /* Enter SEH for probing */
970 /* Probe the thread handle */
971 ProbeForWriteHandle(ThreadHandle
);
973 /* Check for a CID structure */
976 /* Probe and capture it */
977 ProbeForRead(ClientId
, sizeof(CLIENT_ID
), sizeof(ULONG
));
978 SafeClientId
= *ClientId
;
979 ClientId
= &SafeClientId
;
983 * Just probe the object attributes structure, don't capture it
984 * completely. This is done later if necessary
986 ProbeForRead(ObjectAttributes
,
987 sizeof(OBJECT_ATTRIBUTES
),
989 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
990 Attributes
= ObjectAttributes
->Attributes
;
992 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
994 /* Get the exception code */
995 Status
= _SEH2_GetExceptionCode();
998 if (!NT_SUCCESS(Status
)) return Status
;
1002 /* Otherwise just get the data directly */
1003 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
1004 Attributes
= ObjectAttributes
->Attributes
;
1007 /* Can't pass both, fail */
1008 if ((HasObjectName
) && (ClientId
)) return STATUS_INVALID_PARAMETER_MIX
;
1010 /* Create an access state */
1011 Status
= SeCreateAccessState(&AccessState
,
1014 &PsProcessType
->TypeInfo
.GenericMapping
);
1015 if (!NT_SUCCESS(Status
)) return Status
;
1017 /* Check if this is a debugger */
1018 if (SeSinglePrivilegeCheck(SeDebugPrivilege
, PreviousMode
))
1020 /* Did he want full access? */
1021 if (AccessState
.RemainingDesiredAccess
& MAXIMUM_ALLOWED
)
1023 /* Give it to him */
1024 AccessState
.PreviouslyGrantedAccess
|= THREAD_ALL_ACCESS
;
1028 /* Otherwise just give every other access he could want */
1029 AccessState
.PreviouslyGrantedAccess
|=
1030 AccessState
.RemainingDesiredAccess
;
1033 /* The caller desires nothing else now */
1034 AccessState
.RemainingDesiredAccess
= 0;
1037 /* Open by name if one was given */
1041 Status
= ObOpenObjectByName(ObjectAttributes
,
1049 /* Get rid of the access state */
1050 SeDeleteAccessState(&AccessState
);
1054 /* Open by Thread ID */
1055 if (ClientId
->UniqueProcess
)
1057 /* Get the Process */
1058 Status
= PsLookupProcessThreadByCid(ClientId
, NULL
, &Thread
);
1062 /* Get the Process */
1063 Status
= PsLookupThreadByThreadId(ClientId
->UniqueThread
, &Thread
);
1066 /* Check if we didn't find anything */
1067 if (!NT_SUCCESS(Status
))
1069 /* Get rid of the access state and return */
1070 SeDeleteAccessState(&AccessState
);
1074 /* Open the Thread Object */
1075 Status
= ObOpenObjectByPointer(Thread
,
1083 /* Delete the access state and dereference the thread */
1084 SeDeleteAccessState(&AccessState
);
1085 ObDereferenceObject(Thread
);
1089 /* Neither an object name nor a client id was passed */
1090 return STATUS_INVALID_PARAMETER_MIX
;
1093 /* Check for success */
1094 if (NT_SUCCESS(Status
))
1096 /* Protect against bad user-mode pointers */
1099 /* Write back the handle */
1100 *ThreadHandle
= hThread
;
1102 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1104 /* Get the exception code */
1105 Status
= _SEH2_GetExceptionCode();