xref: /reactos/ntoskrnl/ps/thread.c (revision 9c8a8cf2)
1 /*
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)
8  */
9 
10 /* INCLUDES ****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ******************************************************************/
17 
18 extern BOOLEAN CcPfEnablePrefetcher;
19 extern ULONG MmReadClusterSize;
20 POBJECT_TYPE PsThreadType = NULL;
21 
22 /* PRIVATE FUNCTIONS *********************************************************/
23 
24 VOID
25 NTAPI
PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,IN PVOID StartContext)26 PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,
27                      IN PVOID StartContext)
28 {
29     PETHREAD Thread;
30     PTEB Teb;
31     BOOLEAN DeadThread = FALSE;
32     KIRQL OldIrql;
33     PAGED_CODE();
34     PSTRACE(PS_THREAD_DEBUG,
35             "StartRoutine: %p StartContext: %p\n", StartRoutine, StartContext);
36 
37     /* Go to Passive Level */
38     KeLowerIrql(PASSIVE_LEVEL);
39     Thread = PsGetCurrentThread();
40 
41     /* Check if the thread is dead */
42     if (Thread->DeadThread)
43     {
44         /* Remember that we're dead */
45         DeadThread = TRUE;
46     }
47     else
48     {
49         /* Get the Locale ID and save Preferred Proc */
50         Teb =  NtCurrentTeb();
51         Teb->CurrentLocale = MmGetSessionLocaleId();
52         Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
53     }
54 
55     /* Check if this is a dead thread, or if we're hiding */
56     if (!(Thread->DeadThread) && !(Thread->HideFromDebugger))
57     {
58         /* We're not, so notify the debugger */
59         DbgkCreateThread(Thread, StartContext);
60     }
61 
62     /* Make sure we're not already dead */
63     if (!DeadThread)
64     {
65         /* Check if the Prefetcher is enabled */
66         if (CcPfEnablePrefetcher)
67         {
68             /* FIXME: Prepare to prefetch this process */
69         }
70 
71         /* Raise to APC */
72         KeRaiseIrql(APC_LEVEL, &OldIrql);
73 
74         /* Queue the User APC */
75         KiInitializeUserApc(KeGetExceptionFrame(&Thread->Tcb),
76                             KeGetTrapFrame(&Thread->Tcb),
77                             PspSystemDllEntryPoint,
78                             NULL,
79                             PspSystemDllBase,
80                             NULL);
81 
82         /* Lower it back to passive */
83         KeLowerIrql(PASSIVE_LEVEL);
84     }
85     else
86     {
87         /* We're dead, kill us now */
88         PspTerminateThreadByPointer(Thread,
89                                     STATUS_THREAD_IS_TERMINATING,
90                                     TRUE);
91     }
92 
93     /* Do we have a cookie set yet? */
94     while (!SharedUserData->Cookie)
95     {
96         LARGE_INTEGER SystemTime;
97         ULONG NewCookie;
98         PKPRCB Prcb;
99 
100         /* Generate a new cookie */
101         KeQuerySystemTime(&SystemTime);
102         Prcb = KeGetCurrentPrcb();
103         NewCookie = (Prcb->MmPageFaultCount ^ Prcb->InterruptTime ^
104                     SystemTime.u.LowPart ^ SystemTime.u.HighPart ^
105                     (ULONG)(ULONG_PTR)&SystemTime);
106 
107         /* Set the new cookie*/
108         InterlockedCompareExchange((LONG*)&SharedUserData->Cookie,
109                                    NewCookie,
110                                    0);
111     }
112 }
113 
114 LONG
PspUnhandledExceptionInSystemThread(PEXCEPTION_POINTERS ExceptionPointers)115 PspUnhandledExceptionInSystemThread(PEXCEPTION_POINTERS ExceptionPointers)
116 {
117     /* Print debugging information */
118     DPRINT1("PS: Unhandled Kernel Mode Exception Pointers = 0x%p\n",
119             ExceptionPointers);
120     DPRINT1("Code %x Addr %p Info0 %p Info1 %p Info2 %p Info3 %p\n",
121             ExceptionPointers->ExceptionRecord->ExceptionCode,
122             ExceptionPointers->ExceptionRecord->ExceptionAddress,
123             ExceptionPointers->ExceptionRecord->ExceptionInformation[0],
124             ExceptionPointers->ExceptionRecord->ExceptionInformation[1],
125             ExceptionPointers->ExceptionRecord->ExceptionInformation[2],
126             ExceptionPointers->ExceptionRecord->ExceptionInformation[3]);
127 
128     /* Bugcheck the system */
129     KeBugCheckEx(SYSTEM_THREAD_EXCEPTION_NOT_HANDLED,
130                  ExceptionPointers->ExceptionRecord->ExceptionCode,
131                  (ULONG_PTR)ExceptionPointers->ExceptionRecord->ExceptionAddress,
132                  (ULONG_PTR)ExceptionPointers->ExceptionRecord,
133                  (ULONG_PTR)ExceptionPointers->ContextRecord);
134     return 0;
135 }
136 
137 VOID
138 NTAPI
PspSystemThreadStartup(IN PKSTART_ROUTINE StartRoutine,IN PVOID StartContext)139 PspSystemThreadStartup(IN PKSTART_ROUTINE StartRoutine,
140                        IN PVOID StartContext)
141 {
142     PETHREAD Thread;
143     PSTRACE(PS_THREAD_DEBUG,
144             "StartRoutine: %p StartContext: %p\n", StartRoutine, StartContext);
145 
146     /* Unlock the dispatcher Database */
147     KeLowerIrql(PASSIVE_LEVEL);
148     Thread = PsGetCurrentThread();
149 
150     /* Make sure the thread isn't gone */
151     _SEH2_TRY
152     {
153         if (!(Thread->Terminated) && !(Thread->DeadThread))
154         {
155             /* Call the Start Routine */
156             StartRoutine(StartContext);
157         }
158     }
159     _SEH2_EXCEPT(PspUnhandledExceptionInSystemThread(_SEH2_GetExceptionInformation()))
160     {
161         /* Bugcheck if we got here */
162         KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED);
163     }
164     _SEH2_END;
165 
166     /* Exit the thread */
167     PspTerminateThreadByPointer(Thread, STATUS_SUCCESS, TRUE);
168 }
169 
170 NTSTATUS
171 NTAPI
PspCreateThread(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,IN HANDLE ProcessHandle,IN PEPROCESS TargetProcess,OUT PCLIENT_ID ClientId,IN PCONTEXT ThreadContext,IN PINITIAL_TEB InitialTeb,IN BOOLEAN CreateSuspended,IN PKSTART_ROUTINE StartRoutine OPTIONAL,IN PVOID StartContext OPTIONAL)172 PspCreateThread(OUT PHANDLE ThreadHandle,
173                 IN ACCESS_MASK DesiredAccess,
174                 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
175                 IN HANDLE ProcessHandle,
176                 IN PEPROCESS TargetProcess,
177                 OUT PCLIENT_ID ClientId,
178                 IN PCONTEXT ThreadContext,
179                 IN PINITIAL_TEB InitialTeb,
180                 IN BOOLEAN CreateSuspended,
181                 IN PKSTART_ROUTINE StartRoutine OPTIONAL,
182                 IN PVOID StartContext OPTIONAL)
183 {
184     HANDLE hThread;
185     PEPROCESS Process;
186     PETHREAD Thread;
187     PTEB TebBase = NULL;
188     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
189     NTSTATUS Status, AccessStatus;
190     HANDLE_TABLE_ENTRY CidEntry;
191     ACCESS_STATE LocalAccessState;
192     PACCESS_STATE AccessState = &LocalAccessState;
193     AUX_ACCESS_DATA AuxData;
194     BOOLEAN Result, SdAllocated;
195     PSECURITY_DESCRIPTOR SecurityDescriptor;
196     SECURITY_SUBJECT_CONTEXT SubjectContext;
197     PAGED_CODE();
198     PSTRACE(PS_THREAD_DEBUG,
199             "ThreadContext: %p TargetProcess: %p ProcessHandle: %p\n",
200             ThreadContext, TargetProcess, ProcessHandle);
201 
202     /* If we were called from PsCreateSystemThread, then we're kernel mode */
203     if (StartRoutine) PreviousMode = KernelMode;
204 
205     /* Reference the Process by handle or pointer, depending on what we got */
206     if (ProcessHandle)
207     {
208         /* Normal thread or System Thread */
209         Status = ObReferenceObjectByHandle(ProcessHandle,
210                                            PROCESS_CREATE_THREAD,
211                                            PsProcessType,
212                                            PreviousMode,
213                                            (PVOID*)&Process,
214                                            NULL);
215         PSREFTRACE(Process);
216     }
217     else
218     {
219         /* System thread inside System Process, or Normal Thread with a bug */
220         if (StartRoutine)
221         {
222             /* Reference the Process by Pointer */
223             ObReferenceObject(TargetProcess);
224             Process = TargetProcess;
225             Status = STATUS_SUCCESS;
226         }
227         else
228         {
229             /* Fake ObReference returning this */
230             Status = STATUS_INVALID_HANDLE;
231         }
232     }
233 
234     /* Check for success */
235     if (!NT_SUCCESS(Status)) return Status;
236 
237     /* Also make sure that User-Mode isn't trying to create a system thread */
238     if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess))
239     {
240         /* Fail */
241         ObDereferenceObject(Process);
242         return STATUS_INVALID_HANDLE;
243     }
244 
245     /* Create Thread Object */
246     Status = ObCreateObject(PreviousMode,
247                             PsThreadType,
248                             ObjectAttributes,
249                             PreviousMode,
250                             NULL,
251                             sizeof(ETHREAD),
252                             0,
253                             0,
254                             (PVOID*)&Thread);
255     if (!NT_SUCCESS(Status))
256     {
257         /* We failed; dereference the process and exit */
258         ObDereferenceObject(Process);
259         return Status;
260     }
261 
262     /* Zero the Object entirely */
263     RtlZeroMemory(Thread, sizeof(ETHREAD));
264 
265     /* Initialize rundown protection */
266     ExInitializeRundownProtection(&Thread->RundownProtect);
267 
268     /* Initialize exit code */
269     Thread->ExitStatus = STATUS_PENDING;
270 
271     /* Set the Process CID */
272     Thread->ThreadsProcess = Process;
273     Thread->Cid.UniqueProcess = Process->UniqueProcessId;
274 
275     /* Create Cid Handle */
276     CidEntry.Object = Thread;
277     CidEntry.GrantedAccess = 0;
278     Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
279     if (!Thread->Cid.UniqueThread)
280     {
281         /* We couldn't create the CID, dereference the thread and fail */
282         ObDereferenceObject(Thread);
283         return STATUS_INSUFFICIENT_RESOURCES;
284     }
285 
286     /* Save the read cluster size */
287     Thread->ReadClusterSize = MmReadClusterSize;
288 
289     /* Initialize the LPC Reply Semaphore */
290     KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, 1);
291 
292     /* Initialize the list heads and locks */
293     InitializeListHead(&Thread->LpcReplyChain);
294     InitializeListHead(&Thread->IrpList);
295     InitializeListHead(&Thread->PostBlockList);
296     InitializeListHead(&Thread->ActiveTimerListHead);
297     KeInitializeSpinLock(&Thread->ActiveTimerListLock);
298 
299     /* Acquire rundown protection */
300     if (!ExAcquireRundownProtection (&Process->RundownProtect))
301     {
302         /* Fail */
303         ObDereferenceObject(Thread);
304         return STATUS_PROCESS_IS_TERMINATING;
305     }
306 
307     /* Now let the kernel initialize the context */
308     if (ThreadContext)
309     {
310         /* User-mode Thread, create Teb */
311         Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase);
312         if (!NT_SUCCESS(Status))
313         {
314             /* Failed to create the TEB. Release rundown and dereference */
315             ExReleaseRundownProtection(&Process->RundownProtect);
316             ObDereferenceObject(Thread);
317             return Status;
318         }
319 
320         /* Set the Start Addresses from the untrusted ThreadContext */
321         _SEH2_TRY
322         {
323             Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext);
324             Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext);
325         }
326         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
327         {
328             Status = _SEH2_GetExceptionCode();
329         }
330         _SEH2_END;
331 
332         /* Let the kernel intialize the Thread */
333         if (NT_SUCCESS(Status))
334         {
335             Status = KeInitThread(&Thread->Tcb,
336                                   NULL,
337                                   PspUserThreadStartup,
338                                   NULL,
339                                   Thread->StartAddress,
340                                   ThreadContext,
341                                   TebBase,
342                                   &Process->Pcb);
343         }
344     }
345     else
346     {
347         /* System Thread */
348         Thread->StartAddress = StartRoutine;
349         PspSetCrossThreadFlag(Thread, CT_SYSTEM_THREAD_BIT);
350 
351         /* Let the kernel intialize the Thread */
352         Status = KeInitThread(&Thread->Tcb,
353                               NULL,
354                               PspSystemThreadStartup,
355                               StartRoutine,
356                               StartContext,
357                               NULL,
358                               NULL,
359                               &Process->Pcb);
360     }
361 
362     /* Check if we failed */
363     if (!NT_SUCCESS(Status))
364     {
365         /* Delete the TEB if we had done */
366         if (TebBase) MmDeleteTeb(Process, TebBase);
367 
368         /* Release rundown and dereference */
369         ExReleaseRundownProtection(&Process->RundownProtect);
370         ObDereferenceObject(Thread);
371         return Status;
372     }
373 
374     /* Lock the process */
375     KeEnterCriticalRegion();
376     ExAcquirePushLockExclusive(&Process->ProcessLock);
377 
378     /* Make sure the process didn't just die on us */
379     if (Process->ProcessDelete) goto Quickie;
380 
381     /* Check if the thread was ours, terminated and it was user mode */
382     if ((Thread->Terminated) &&
383         (ThreadContext) &&
384         (Thread->ThreadsProcess == Process))
385     {
386         /* Cleanup, we don't want to start it up and context switch */
387         goto Quickie;
388     }
389 
390     /*
391      * Insert the Thread into the Process's Thread List
392      * Note, this is the ETHREAD Thread List. It is removed in
393      * ps/kill.c!PspExitThread.
394      */
395     InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
396     Process->ActiveThreads++;
397 
398     /* Start the thread */
399     KeStartThread(&Thread->Tcb);
400 
401     /* Release the process lock */
402     ExReleasePushLockExclusive(&Process->ProcessLock);
403     KeLeaveCriticalRegion();
404 
405     /* Release rundown */
406     ExReleaseRundownProtection(&Process->RundownProtect);
407 
408     /* Notify WMI */
409     //WmiTraceProcess(Process, TRUE);
410     //WmiTraceThread(Thread, InitialTeb, TRUE);
411 
412     /* Notify Thread Creation */
413     PspRunCreateThreadNotifyRoutines(Thread, TRUE);
414 
415     /* Reference ourselves as a keep-alive */
416     ObReferenceObjectEx(Thread, 2);
417 
418     /* Suspend the Thread if we have to */
419     if (CreateSuspended) KeSuspendThread(&Thread->Tcb);
420 
421     /* Check if we were already terminated */
422     if (Thread->Terminated) KeForceResumeThread(&Thread->Tcb);
423 
424     /* Create an access state */
425     Status = SeCreateAccessStateEx(NULL,
426                                    ThreadContext ?
427                                    PsGetCurrentProcess() : Process,
428                                    &LocalAccessState,
429                                    &AuxData,
430                                    DesiredAccess,
431                                    &PsThreadType->TypeInfo.GenericMapping);
432     if (!NT_SUCCESS(Status))
433     {
434         /* Access state failed, thread is dead */
435         PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
436 
437         /* If we were suspended, wake it up */
438         if (CreateSuspended) KeResumeThread(&Thread->Tcb);
439 
440         /* Dispatch thread */
441         KeReadyThread(&Thread->Tcb);
442 
443         /* Dereference completely to kill it */
444         ObDereferenceObjectEx(Thread, 2);
445         return Status;
446     }
447 
448     /* Insert the Thread into the Object Manager */
449     Status = ObInsertObject(Thread,
450                             AccessState,
451                             DesiredAccess,
452                             0,
453                             NULL,
454                             &hThread);
455 
456     /* Delete the access state if we had one */
457     if (AccessState) SeDeleteAccessState(AccessState);
458 
459     /* Check for success */
460     if (NT_SUCCESS(Status))
461     {
462         /* Wrap in SEH to protect against bad user-mode pointers */
463         _SEH2_TRY
464         {
465             /* Return Cid and Handle */
466             if (ClientId) *ClientId = Thread->Cid;
467             *ThreadHandle = hThread;
468         }
469         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
470         {
471             /* Thread insertion failed, thread is dead */
472             PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
473 
474             /* If we were suspended, wake it up */
475             if (CreateSuspended) KeResumeThread(&Thread->Tcb);
476 
477             /* Dispatch thread */
478             KeReadyThread(&Thread->Tcb);
479 
480             /* Dereference it, leaving only the keep-alive */
481             ObDereferenceObject(Thread);
482 
483             /* Close its handle, killing it */
484             ObCloseHandle(hThread, PreviousMode);
485 
486             /* Return the exception code */
487             _SEH2_YIELD(return _SEH2_GetExceptionCode());
488         }
489         _SEH2_END;
490     }
491     else
492     {
493         /* Thread insertion failed, thread is dead */
494         PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
495 
496         /* If we were suspended, wake it up */
497         if (CreateSuspended) KeResumeThread(&Thread->Tcb);
498     }
499 
500     /* Get the create time */
501     KeQuerySystemTime(&Thread->CreateTime);
502     ASSERT(!(Thread->CreateTime.HighPart & 0xF0000000));
503 
504     /* Make sure the thread isn't dead */
505     if (!Thread->DeadThread)
506     {
507         /* Get the thread's SD */
508         Status = ObGetObjectSecurity(Thread,
509                                      &SecurityDescriptor,
510                                      &SdAllocated);
511         if (!NT_SUCCESS(Status))
512         {
513             /* Thread insertion failed, thread is dead */
514             PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
515 
516             /* If we were suspended, wake it up */
517             if (CreateSuspended) KeResumeThread(&Thread->Tcb);
518 
519             /* Dispatch thread */
520             KeReadyThread(&Thread->Tcb);
521 
522             /* Dereference it, leaving only the keep-alive */
523             ObDereferenceObject(Thread);
524 
525             /* Close its handle, killing it */
526             ObCloseHandle(hThread, PreviousMode);
527             return Status;
528         }
529 
530         /* Create the subject context */
531         SubjectContext.ProcessAuditId = Process;
532         SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
533         SubjectContext.ClientToken = NULL;
534 
535         /* Do the access check */
536         Result = SeAccessCheck(SecurityDescriptor,
537                                &SubjectContext,
538                                FALSE,
539                                MAXIMUM_ALLOWED,
540                                0,
541                                NULL,
542                                &PsThreadType->TypeInfo.GenericMapping,
543                                PreviousMode,
544                                &Thread->GrantedAccess,
545                                &AccessStatus);
546 
547         /* Dereference the token and let go the SD */
548         ObFastDereferenceObject(&Process->Token,
549                                 SubjectContext.PrimaryToken);
550         ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
551 
552         /* Remove access if it failed */
553         if (!Result) Process->GrantedAccess = 0;
554 
555         /* Set least some minimum access */
556         Thread->GrantedAccess |= (THREAD_TERMINATE |
557                                   THREAD_SET_INFORMATION |
558                                   THREAD_QUERY_INFORMATION);
559     }
560     else
561     {
562         /* Set the thread access mask to maximum */
563         Thread->GrantedAccess = THREAD_ALL_ACCESS;
564     }
565 
566     /* Dispatch thread */
567     KeReadyThread(&Thread->Tcb);
568 
569     /* Dereference it, leaving only the keep-alive */
570     ObDereferenceObject(Thread);
571 
572     /* Return */
573     return Status;
574 
575     /* Most annoying failure case ever, where we undo almost all manually */
576 Quickie:
577     /* When we get here, the process is locked, unlock it */
578     ExReleasePushLockExclusive(&Process->ProcessLock);
579     KeLeaveCriticalRegion();
580 
581     /* Uninitailize it */
582     KeUninitThread(&Thread->Tcb);
583 
584     /* If we had a TEB, delete it */
585     if (TebBase) MmDeleteTeb(Process, TebBase);
586 
587     /* Release rundown protection, which we also hold */
588     ExReleaseRundownProtection(&Process->RundownProtect);
589 
590     /* Dereference the thread and return failure */
591     ObDereferenceObject(Thread);
592     return STATUS_PROCESS_IS_TERMINATING;
593 }
594 
595 /* PUBLIC FUNCTIONS **********************************************************/
596 
597 /*
598  * @implemented
599  */
600 NTSTATUS
601 NTAPI
PsCreateSystemThread(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN HANDLE ProcessHandle,IN PCLIENT_ID ClientId,IN PKSTART_ROUTINE StartRoutine,IN PVOID StartContext)602 PsCreateSystemThread(OUT PHANDLE ThreadHandle,
603                      IN ACCESS_MASK DesiredAccess,
604                      IN POBJECT_ATTRIBUTES ObjectAttributes,
605                      IN HANDLE ProcessHandle,
606                      IN PCLIENT_ID ClientId,
607                      IN PKSTART_ROUTINE StartRoutine,
608                      IN PVOID StartContext)
609 {
610     PEPROCESS TargetProcess = NULL;
611     HANDLE Handle = ProcessHandle;
612     PAGED_CODE();
613     PSTRACE(PS_THREAD_DEBUG,
614             "ProcessHandle: %p StartRoutine: %p StartContext: %p\n",
615             ProcessHandle, StartRoutine, StartContext);
616 
617     /* Check if we have a handle. If not, use the System Process */
618     if (!ProcessHandle)
619     {
620         Handle = NULL;
621         TargetProcess = PsInitialSystemProcess;
622     }
623 
624     /* Call the shared function */
625     return PspCreateThread(ThreadHandle,
626                            DesiredAccess,
627                            ObjectAttributes,
628                            Handle,
629                            TargetProcess,
630                            ClientId,
631                            NULL,
632                            NULL,
633                            FALSE,
634                            StartRoutine,
635                            StartContext);
636 }
637 
638 /*
639  * @implemented
640  */
641 NTSTATUS
642 NTAPI
PsLookupThreadByThreadId(IN HANDLE ThreadId,OUT PETHREAD * Thread)643 PsLookupThreadByThreadId(IN HANDLE ThreadId,
644                          OUT PETHREAD *Thread)
645 {
646     PHANDLE_TABLE_ENTRY CidEntry;
647     PETHREAD FoundThread;
648     NTSTATUS Status = STATUS_INVALID_PARAMETER;
649     PAGED_CODE();
650     PSTRACE(PS_THREAD_DEBUG, "ThreadId: %p\n", ThreadId);
651     KeEnterCriticalRegion();
652 
653     /* Get the CID Handle Entry */
654     CidEntry = ExMapHandleToPointer(PspCidTable, ThreadId);
655     if (CidEntry)
656     {
657         /* Get the Thread */
658         FoundThread = CidEntry->Object;
659 
660         /* Make sure it's really a thread */
661         if (FoundThread->Tcb.Header.Type == ThreadObject)
662         {
663             /* Safe Reference and return it */
664             if (ObReferenceObjectSafe(FoundThread))
665             {
666                 *Thread = FoundThread;
667                 Status = STATUS_SUCCESS;
668             }
669         }
670 
671         /* Unlock the Entry */
672         ExUnlockHandleTableEntry(PspCidTable, CidEntry);
673     }
674 
675     /* Return to caller */
676     KeLeaveCriticalRegion();
677     return Status;
678 }
679 
680 /*
681  * @implemented
682  */
683 ULONG
684 NTAPI
PsGetThreadFreezeCount(IN PETHREAD Thread)685 PsGetThreadFreezeCount(IN PETHREAD Thread)
686 {
687     return Thread->Tcb.FreezeCount;
688 }
689 
690 /*
691  * @implemented
692  */
693 BOOLEAN
694 NTAPI
PsGetThreadHardErrorsAreDisabled(IN PETHREAD Thread)695 PsGetThreadHardErrorsAreDisabled(IN PETHREAD Thread)
696 {
697     return Thread->HardErrorsAreDisabled ? TRUE : FALSE;
698 }
699 
700 /*
701  * @implemented
702  */
703 HANDLE
704 NTAPI
PsGetThreadId(IN PETHREAD Thread)705 PsGetThreadId(IN PETHREAD Thread)
706 {
707     return Thread->Cid.UniqueThread;
708 }
709 
710 /*
711  * @implemented
712  */
713 HANDLE
714 NTAPI
PsGetCurrentThreadId(VOID)715 PsGetCurrentThreadId(VOID)
716 {
717     return PsGetCurrentThread()->Cid.UniqueThread;
718 }
719 
720 /*
721  * @implemented
722  */
723 PEPROCESS
724 NTAPI
PsGetThreadProcess(IN PETHREAD Thread)725 PsGetThreadProcess(IN PETHREAD Thread)
726 {
727     return Thread->ThreadsProcess;
728 }
729 
730 /*
731  * @implemented
732  */
733 PEPROCESS
734 NTAPI
PsGetCurrentThreadProcess(VOID)735 PsGetCurrentThreadProcess(VOID)
736 {
737     return PsGetCurrentThread()->ThreadsProcess;
738 }
739 
740 /*
741  * @implemented
742  */
743 HANDLE
744 NTAPI
PsGetThreadProcessId(IN PETHREAD Thread)745 PsGetThreadProcessId(IN PETHREAD Thread)
746 {
747     return Thread->Cid.UniqueProcess;
748 }
749 
750 /*
751  * @implemented
752  */
753 HANDLE
754 NTAPI
PsGetCurrentThreadProcessId(VOID)755 PsGetCurrentThreadProcessId(VOID)
756 {
757     return PsGetCurrentThread()->Cid.UniqueProcess;
758 }
759 
760 /*
761  * @implemented
762  */
763 ULONG
764 NTAPI
PsGetThreadSessionId(IN PETHREAD Thread)765 PsGetThreadSessionId(IN PETHREAD Thread)
766 {
767     return MmGetSessionId(Thread->ThreadsProcess);
768 }
769 
770 /*
771  * @implemented
772  */
773 PTEB
774 NTAPI
PsGetThreadTeb(IN PETHREAD Thread)775 PsGetThreadTeb(IN PETHREAD Thread)
776 {
777     return Thread->Tcb.Teb;
778 }
779 
780 /*
781  * @implemented
782  */
783 PVOID
784 NTAPI
PsGetCurrentThreadTeb(VOID)785 PsGetCurrentThreadTeb(VOID)
786 {
787     return PsGetCurrentThread()->Tcb.Teb;
788 }
789 
790 /*
791  * @implemented
792  */
793 PVOID
794 NTAPI
PsGetThreadWin32Thread(IN PETHREAD Thread)795 PsGetThreadWin32Thread(IN PETHREAD Thread)
796 {
797     return Thread->Tcb.Win32Thread;
798 }
799 
800 /*
801  * @implemented
802  */
803 PVOID
804 NTAPI
PsGetCurrentThreadWin32Thread(VOID)805 PsGetCurrentThreadWin32Thread(VOID)
806 {
807     return PsGetCurrentThread()->Tcb.Win32Thread;
808 }
809 
810 /*
811  * @implemented
812  */
813 PVOID
814 NTAPI
PsGetCurrentThreadWin32ThreadAndEnterCriticalRegion(_Out_ HANDLE * OutProcessId)815 PsGetCurrentThreadWin32ThreadAndEnterCriticalRegion(
816     _Out_ HANDLE* OutProcessId)
817 {
818     PETHREAD CurrentThread;
819 
820     /* Get the current thread */
821     CurrentThread = PsGetCurrentThread();
822 
823     /* Return the process id */
824     *OutProcessId = CurrentThread->Cid.UniqueProcess;
825 
826     /* Enter critical region */
827     KeEnterCriticalRegion();
828 
829     /* Return the win32 thread */
830     return CurrentThread->Tcb.Win32Thread;
831 }
832 
833 /*
834  * @implemented
835  */
836 KPROCESSOR_MODE
837 NTAPI
PsGetCurrentThreadPreviousMode(VOID)838 PsGetCurrentThreadPreviousMode(VOID)
839 {
840     return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
841 }
842 
843 /*
844  * @implemented
845  */
846 PVOID
847 NTAPI
PsGetCurrentThreadStackBase(VOID)848 PsGetCurrentThreadStackBase(VOID)
849 {
850     return PsGetCurrentThread()->Tcb.StackBase;
851 }
852 
853 /*
854  * @implemented
855  */
856 PVOID
857 NTAPI
PsGetCurrentThreadStackLimit(VOID)858 PsGetCurrentThreadStackLimit(VOID)
859 {
860     return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
861 }
862 
863 /*
864  * @implemented
865  */
866 BOOLEAN
867 NTAPI
PsIsThreadTerminating(IN PETHREAD Thread)868 PsIsThreadTerminating(IN PETHREAD Thread)
869 {
870     return Thread->Terminated ? TRUE : FALSE;
871 }
872 
873 /*
874  * @implemented
875  */
876 BOOLEAN
877 NTAPI
PsIsSystemThread(IN PETHREAD Thread)878 PsIsSystemThread(IN PETHREAD Thread)
879 {
880     return Thread->SystemThread ? TRUE: FALSE;
881 }
882 
883 /*
884  * @implemented
885  */
886 BOOLEAN
887 NTAPI
PsIsThreadImpersonating(IN PETHREAD Thread)888 PsIsThreadImpersonating(IN PETHREAD Thread)
889 {
890     return Thread->ActiveImpersonationInfo ? TRUE : FALSE;
891 }
892 
893 /*
894  * @implemented
895  */
896 VOID
897 NTAPI
PsSetThreadHardErrorsAreDisabled(IN PETHREAD Thread,IN BOOLEAN HardErrorsAreDisabled)898 PsSetThreadHardErrorsAreDisabled(IN PETHREAD Thread,
899                                  IN BOOLEAN HardErrorsAreDisabled)
900 {
901     Thread->HardErrorsAreDisabled = HardErrorsAreDisabled;
902 }
903 
904 /*
905  * @implemented
906  */
907 PVOID
908 NTAPI
PsSetThreadWin32Thread(_Inout_ PETHREAD Thread,_In_ PVOID Win32Thread,_In_ PVOID OldWin32Thread)909 PsSetThreadWin32Thread(
910     _Inout_ PETHREAD Thread,
911     _In_ PVOID Win32Thread,
912     _In_ PVOID OldWin32Thread)
913 {
914     /* Are we setting the win32 process? */
915     if (Win32Thread != NULL)
916     {
917         /* Just exchange it */
918         return InterlockedExchangePointer(&Thread->Tcb.Win32Thread,
919                                           Win32Thread);
920     }
921     else
922     {
923         /* We are resetting, only exchange when the old win32 thread matches */
924         return InterlockedCompareExchangePointer(&Thread->Tcb.Win32Thread,
925                                                  Win32Thread,
926                                                  OldWin32Thread);
927     }
928 }
929 
930 NTSTATUS
931 NTAPI
PsWrapApcWow64Thread(IN OUT PVOID * ApcContext,IN OUT PVOID * ApcRoutine)932 PsWrapApcWow64Thread(IN OUT PVOID *ApcContext,
933                      IN OUT PVOID *ApcRoutine)
934 {
935     UNIMPLEMENTED;
936     return STATUS_NOT_IMPLEMENTED;
937 }
938 
939 NTSTATUS
940 NTAPI
NtCreateThread(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,IN HANDLE ProcessHandle,OUT PCLIENT_ID ClientId,IN PCONTEXT ThreadContext,IN PINITIAL_TEB InitialTeb,IN BOOLEAN CreateSuspended)941 NtCreateThread(OUT PHANDLE ThreadHandle,
942                IN ACCESS_MASK DesiredAccess,
943                IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
944                IN HANDLE ProcessHandle,
945                OUT PCLIENT_ID ClientId,
946                IN PCONTEXT ThreadContext,
947                IN PINITIAL_TEB InitialTeb,
948                IN BOOLEAN CreateSuspended)
949 {
950     INITIAL_TEB SafeInitialTeb;
951     PAGED_CODE();
952     PSTRACE(PS_THREAD_DEBUG,
953             "ProcessHandle: %p Context: %p\n", ProcessHandle, ThreadContext);
954 
955     /* Check if this was from user-mode */
956     if (KeGetPreviousMode() != KernelMode)
957     {
958         /* Make sure that we got a context */
959         if (!ThreadContext) return STATUS_INVALID_PARAMETER;
960 
961         /* Protect checks */
962         _SEH2_TRY
963         {
964             /* Make sure the handle pointer we got is valid */
965             ProbeForWriteHandle(ThreadHandle);
966 
967             /* Check if the caller wants a client id */
968             if (ClientId)
969             {
970                 /* Make sure we can write to it */
971                 ProbeForWrite(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
972             }
973 
974             /* Make sure that the entire context is readable */
975             ProbeForRead(ThreadContext, sizeof(CONTEXT), sizeof(ULONG));
976 
977             /* Check the Initial TEB */
978             ProbeForRead(InitialTeb, sizeof(INITIAL_TEB), sizeof(ULONG));
979             SafeInitialTeb = *InitialTeb;
980         }
981         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
982         {
983             /* Return the exception code */
984             _SEH2_YIELD(return _SEH2_GetExceptionCode());
985         }
986         _SEH2_END;
987     }
988     else
989     {
990         /* Use the Initial TEB as is */
991         SafeInitialTeb = *InitialTeb;
992     }
993 
994     /* Call the shared function */
995     return PspCreateThread(ThreadHandle,
996                            DesiredAccess,
997                            ObjectAttributes,
998                            ProcessHandle,
999                            NULL,
1000                            ClientId,
1001                            ThreadContext,
1002                            &SafeInitialTeb,
1003                            CreateSuspended,
1004                            NULL,
1005                            NULL);
1006 }
1007 
1008 /*
1009  * @implemented
1010  */
1011 NTSTATUS
1012 NTAPI
NtOpenThread(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)1013 NtOpenThread(OUT PHANDLE ThreadHandle,
1014              IN ACCESS_MASK DesiredAccess,
1015              IN POBJECT_ATTRIBUTES ObjectAttributes,
1016              IN PCLIENT_ID ClientId OPTIONAL)
1017 {
1018     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1019     CLIENT_ID SafeClientId;
1020     ULONG Attributes = 0;
1021     HANDLE hThread = NULL;
1022     NTSTATUS Status;
1023     PETHREAD Thread;
1024     BOOLEAN HasObjectName = FALSE;
1025     ACCESS_STATE AccessState;
1026     AUX_ACCESS_DATA AuxData;
1027     PAGED_CODE();
1028     PSTRACE(PS_THREAD_DEBUG,
1029             "ClientId: %p ObjectAttributes: %p\n", ClientId, ObjectAttributes);
1030 
1031     /* Check if we were called from user mode */
1032     if (PreviousMode != KernelMode)
1033     {
1034         /* Enter SEH for probing */
1035         _SEH2_TRY
1036         {
1037             /* Probe the thread handle */
1038             ProbeForWriteHandle(ThreadHandle);
1039 
1040             /* Check for a CID structure */
1041             if (ClientId)
1042             {
1043                 /* Probe and capture it */
1044                 ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
1045                 SafeClientId = *ClientId;
1046                 ClientId = &SafeClientId;
1047             }
1048 
1049             /*
1050              * Just probe the object attributes structure, don't capture it
1051              * completely. This is done later if necessary
1052              */
1053             ProbeForRead(ObjectAttributes,
1054                          sizeof(OBJECT_ATTRIBUTES),
1055                          sizeof(ULONG));
1056             HasObjectName = (ObjectAttributes->ObjectName != NULL);
1057 
1058             /* Validate user attributes */
1059             Attributes = ObpValidateAttributes(ObjectAttributes->Attributes, PreviousMode);
1060         }
1061         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1062         {
1063             /* Return the exception code */
1064             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1065         }
1066         _SEH2_END;
1067     }
1068     else
1069     {
1070         /* Otherwise just get the data directly */
1071         HasObjectName = (ObjectAttributes->ObjectName != NULL);
1072 
1073         /* Still have to sanitize attributes */
1074         Attributes = ObpValidateAttributes(ObjectAttributes->Attributes, PreviousMode);
1075     }
1076 
1077     /* Can't pass both, fail */
1078     if ((HasObjectName) && (ClientId)) return STATUS_INVALID_PARAMETER_MIX;
1079 
1080     /* Create an access state */
1081     Status = SeCreateAccessState(&AccessState,
1082                                  &AuxData,
1083                                  DesiredAccess,
1084                                  &PsThreadType->TypeInfo.GenericMapping);
1085     if (!NT_SUCCESS(Status)) return Status;
1086 
1087     /* Check if this is a debugger */
1088     if (SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
1089     {
1090         /* Did he want full access? */
1091         if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
1092         {
1093             /* Give it to him */
1094             AccessState.PreviouslyGrantedAccess |= THREAD_ALL_ACCESS;
1095         }
1096         else
1097         {
1098             /* Otherwise just give every other access he could want */
1099             AccessState.PreviouslyGrantedAccess |=
1100                 AccessState.RemainingDesiredAccess;
1101         }
1102 
1103         /* The caller desires nothing else now */
1104         AccessState.RemainingDesiredAccess = 0;
1105     }
1106 
1107     /* Open by name if one was given */
1108     if (HasObjectName)
1109     {
1110         /* Open it */
1111         Status = ObOpenObjectByName(ObjectAttributes,
1112                                     PsThreadType,
1113                                     PreviousMode,
1114                                     &AccessState,
1115                                     0,
1116                                     NULL,
1117                                     &hThread);
1118 
1119         /* Get rid of the access state */
1120         SeDeleteAccessState(&AccessState);
1121     }
1122     else if (ClientId)
1123     {
1124         /* Open by Thread ID */
1125         if (ClientId->UniqueProcess)
1126         {
1127             /* Get the Process */
1128             Status = PsLookupProcessThreadByCid(ClientId, NULL, &Thread);
1129         }
1130         else
1131         {
1132             /* Get the Process */
1133             Status = PsLookupThreadByThreadId(ClientId->UniqueThread, &Thread);
1134         }
1135 
1136         /* Check if we didn't find anything */
1137         if (!NT_SUCCESS(Status))
1138         {
1139             /* Get rid of the access state and return */
1140             SeDeleteAccessState(&AccessState);
1141             return Status;
1142         }
1143 
1144         /* Open the Thread Object */
1145         Status = ObOpenObjectByPointer(Thread,
1146                                        Attributes,
1147                                        &AccessState,
1148                                        0,
1149                                        PsThreadType,
1150                                        PreviousMode,
1151                                        &hThread);
1152 
1153         /* Delete the access state and dereference the thread */
1154         SeDeleteAccessState(&AccessState);
1155         ObDereferenceObject(Thread);
1156     }
1157     else
1158     {
1159         /* Neither an object name nor a client id was passed */
1160         return STATUS_INVALID_PARAMETER_MIX;
1161     }
1162 
1163     /* Check for success */
1164     if (NT_SUCCESS(Status))
1165     {
1166         /* Protect against bad user-mode pointers */
1167         _SEH2_TRY
1168         {
1169             /* Write back the handle */
1170             *ThreadHandle = hThread;
1171         }
1172         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1173         {
1174             /* Get the exception code */
1175             Status = _SEH2_GetExceptionCode();
1176         }
1177         _SEH2_END;
1178     }
1179 
1180     /* Return status */
1181     return Status;
1182 }
1183 
1184 /* EOF */
1185