/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS kernel
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ps/thread.c
- * PURPOSE: Thread managment
- *
- * PROGRAMMERS: David Welch (welch@mcmail.com)
- * Phillip Susi
+ * PURPOSE: Process Manager: Thread Management
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+ * Thomas Weidenmueller (w3seek@reactos.org)
*/
/* INCLUDES ****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
/* GLOBALS ******************************************************************/
-extern LIST_ENTRY PsActiveProcessHead;
-extern PEPROCESS PsIdleProcess;
+extern BOOLEAN CcPfEnablePrefetcher;
+extern ULONG MmReadClusterSize;
+POBJECT_TYPE PsThreadType = NULL;
-POBJECT_TYPE EXPORTED PsThreadType = NULL;
-
-/* FUNCTIONS ***************************************************************/
+/* PRIVATE FUNCTIONS *********************************************************/
VOID
-STDCALL
-PspThreadSpecialApc(PKAPC Apc,
- PKNORMAL_ROUTINE* NormalRoutine,
- PVOID* NormalContext,
- PVOID* SystemArgument1,
- PVOID* SystemArgument2)
+NTAPI
+PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,
+ IN PVOID StartContext)
{
- ExFreePool(Apc);
-}
+ PETHREAD Thread;
+ PTEB Teb;
+ BOOLEAN DeadThread = FALSE;
+ KIRQL OldIrql;
+ PAGED_CODE();
+ PSTRACE(PS_THREAD_DEBUG,
+ "StartRoutine: %p StartContext: %p\n", StartRoutine, StartContext);
-VOID
-STDCALL
-PspUserThreadStartup(PKSTART_ROUTINE StartRoutine,
- PVOID StartContext)
-{
- PKAPC ThreadApc;
- PETHREAD Thread = PsGetCurrentThread();
+ /* Go to Passive Level */
+ KeLowerIrql(PASSIVE_LEVEL);
+ Thread = PsGetCurrentThread();
- DPRINT("I am a new USER thread. This is my start routine: %p. This my context: %p."
- "This is my IRQL: %d. This is my Thread Pointer: %x.\n", StartRoutine,
- StartContext, KeGetCurrentIrql(), Thread);
+ /* Check if the thread is dead */
+ if (Thread->DeadThread)
+ {
+ /* Remember that we're dead */
+ DeadThread = TRUE;
+ }
+ else
+ {
+ /* Get the Locale ID and save Preferred Proc */
+ Teb = NtCurrentTeb();
+ Teb->CurrentLocale = MmGetSessionLocaleId();
+ Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
+ }
- if (!Thread->Terminated) {
+ /* Check if this is a dead thread, or if we're hiding */
+ if (!(Thread->DeadThread) && !(Thread->HideFromDebugger))
+ {
+ /* We're not, so notify the debugger */
+ DbgkCreateThread(Thread, StartContext);
+ }
- /* Allocate the APC */
- ThreadApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('T', 'h', 'r','d'));
+ /* Make sure we're not already dead */
+ if (!DeadThread)
+ {
+ /* Check if the Prefetcher is enabled */
+ if (CcPfEnablePrefetcher)
+ {
+ /* FIXME: Prepare to prefetch this process */
+ }
+
+ /* Raise to APC */
+ KeRaiseIrql(APC_LEVEL, &OldIrql);
- /* Initialize it */
- KeInitializeApc(ThreadApc,
- &Thread->Tcb,
- OriginalApcEnvironment,
- PspThreadSpecialApc,
- NULL,
- LdrpGetSystemDllEntryPoint(),
- UserMode,
- NULL);
+ /* Queue the User APC */
+ KiInitializeUserApc(KeGetExceptionFrame(&Thread->Tcb),
+ KeGetTrapFrame(&Thread->Tcb),
+ PspSystemDllEntryPoint,
+ NULL,
+ PspSystemDllBase,
+ NULL);
- /* Insert it into the queue */
- KeInsertQueueApc(ThreadApc, NULL, NULL, IO_NO_INCREMENT);
- Thread->Tcb.ApcState.UserApcPending = TRUE;
+ /* Lower it back to passive */
+ KeLowerIrql(PASSIVE_LEVEL);
+ }
+ else
+ {
+ /* We're dead, kill us now */
+ PspTerminateThreadByPointer(Thread,
+ STATUS_THREAD_IS_TERMINATING,
+ TRUE);
}
- /* Go to Passive Level and notify debugger */
- KeLowerIrql(PASSIVE_LEVEL);
- DbgkCreateThread(StartContext);
+ /* Do we have a cookie set yet? */
+ if (!SharedUserData->Cookie)
+ {
+ LARGE_INTEGER SystemTime;
+ ULONG NewCookie;
+ PKPRCB Prcb;
+
+ /* Generate a new cookie */
+ KeQuerySystemTime(&SystemTime);
+ Prcb = KeGetCurrentPrcb();
+ NewCookie = Prcb->MmPageFaultCount ^ Prcb->InterruptTime ^
+ SystemTime.u.LowPart ^ SystemTime.u.HighPart ^
+ (ULONG_PTR)&SystemTime;
+
+ /* Set the new cookie*/
+ InterlockedCompareExchange((LONG*)&SharedUserData->Cookie,
+ NewCookie,
+ 0);
+ }
+}
+
+LONG
+PspUnhandledExceptionInSystemThread(PEXCEPTION_POINTERS ExceptionPointers)
+{
+ /* Print debugging information */
+ DPRINT1("PS: Unhandled Kernel Mode Exception Pointers = 0x%p\n",
+ ExceptionPointers);
+ DPRINT1("Code %x Addr %p Info0 %p Info1 %p Info2 %p Info3 %p\n",
+ ExceptionPointers->ExceptionRecord->ExceptionCode,
+ ExceptionPointers->ExceptionRecord->ExceptionAddress,
+ ExceptionPointers->ExceptionRecord->ExceptionInformation[0],
+ ExceptionPointers->ExceptionRecord->ExceptionInformation[1],
+ ExceptionPointers->ExceptionRecord->ExceptionInformation[2],
+ ExceptionPointers->ExceptionRecord->ExceptionInformation[3]);
+
+ /* Bugcheck the system */
+ KeBugCheckEx(SYSTEM_THREAD_EXCEPTION_NOT_HANDLED,
+ ExceptionPointers->ExceptionRecord->ExceptionCode,
+ (ULONG_PTR)ExceptionPointers->ExceptionRecord->ExceptionAddress,
+ (ULONG_PTR)ExceptionPointers->ExceptionRecord,
+ (ULONG_PTR)ExceptionPointers->ContextRecord);
+ return 0;
}
VOID
-STDCALL
-PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine,
- PVOID StartContext)
+NTAPI
+PspSystemThreadStartup(IN PKSTART_ROUTINE StartRoutine,
+ IN PVOID StartContext)
{
- PETHREAD Thread = PsGetCurrentThread();
+ PETHREAD Thread;
+ PSTRACE(PS_THREAD_DEBUG,
+ "StartRoutine: %p StartContext: %p\n", StartRoutine, StartContext);
/* Unlock the dispatcher Database */
KeLowerIrql(PASSIVE_LEVEL);
+ Thread = PsGetCurrentThread();
- /* Make sure it's not terminated by now */
- if (!Thread->Terminated) {
-
- /* Call it */
- (StartRoutine)(StartContext);
+ /* Make sure the thread isn't gone */
+ _SEH2_TRY
+ {
+ if (!(Thread->Terminated) && !(Thread->DeadThread))
+ {
+ /* Call the Start Routine */
+ StartRoutine(StartContext);
+ }
+ }
+ _SEH2_EXCEPT(PspUnhandledExceptionInSystemThread(_SEH2_GetExceptionInformation()))
+ {
+ /* Bugcheck if we got here */
+ KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED);
}
+ _SEH2_END;
/* Exit the thread */
- PspExitThread(STATUS_SUCCESS);
+ PspTerminateThreadByPointer(Thread, STATUS_SUCCESS, TRUE);
}
NTSTATUS
-STDCALL
+NTAPI
PspCreateThread(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
IN PEPROCESS TargetProcess,
OUT PCLIENT_ID ClientId,
HANDLE hThread;
PEPROCESS Process;
PETHREAD Thread;
- PTEB TebBase;
- KIRQL OldIrql;
+ PTEB TebBase = NULL;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
- NTSTATUS Status;
- PVOID KernelStack;
+ NTSTATUS Status, AccessStatus;
+ HANDLE_TABLE_ENTRY CidEntry;
+ ACCESS_STATE LocalAccessState;
+ PACCESS_STATE AccessState = &LocalAccessState;
+ AUX_ACCESS_DATA AuxData;
+ BOOLEAN Result, SdAllocated;
+ PSECURITY_DESCRIPTOR SecurityDescriptor;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ PAGED_CODE();
+ PSTRACE(PS_THREAD_DEBUG,
+ "ThreadContext: %p TargetProcess: %p ProcessHandle: %p\n",
+ ThreadContext, TargetProcess, ProcessHandle);
- /* Reference the Process by handle or pointer, depending on what we got */
- DPRINT("PspCreateThread: %x, %x, %x\n", ProcessHandle, TargetProcess, ThreadContext);
- if (ProcessHandle) {
+ /* If we were called from PsCreateSystemThread, then we're kernel mode */
+ if (StartRoutine) PreviousMode = KernelMode;
+ /* Reference the Process by handle or pointer, depending on what we got */
+ if (ProcessHandle)
+ {
/* Normal thread or System Thread */
- DPRINT("Referencing Parent Process\n");
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_CREATE_THREAD,
PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
- } else {
-
+ PSREFTRACE(Process);
+ }
+ else
+ {
/* System thread inside System Process, or Normal Thread with a bug */
- if (StartRoutine) {
-
+ if (StartRoutine)
+ {
/* Reference the Process by Pointer */
- DPRINT("Referencing Parent System Process\n");
ObReferenceObject(TargetProcess);
Process = TargetProcess;
Status = STATUS_SUCCESS;
-
- } else {
-
+ }
+ else
+ {
/* Fake ObReference returning this */
Status = STATUS_INVALID_HANDLE;
}
}
/* Check for success */
- if(!NT_SUCCESS(Status)) {
+ if (!NT_SUCCESS(Status)) return Status;
- DPRINT1("Invalid Process Handle, or no handle given\n");
- return(Status);
+ /* Also make sure that User-Mode isn't trying to create a system thread */
+ if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess))
+ {
+ /* Fail */
+ ObDereferenceObject(Process);
+ return STATUS_INVALID_HANDLE;
}
/* Create Thread Object */
- DPRINT("Creating Thread Object\n");
Status = ObCreateObject(PreviousMode,
PsThreadType,
ObjectAttributes,
- KernelMode,
+ PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
(PVOID*)&Thread);
-
- /* Check for success */
- if (!NT_SUCCESS(Status)) {
-
- /* Dereference the Process */
- DPRINT1("Failed to Create Thread Object\n");
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed; dereference the process and exit */
ObDereferenceObject(Process);
- return(Status);
+ return Status;
}
/* Zero the Object entirely */
- DPRINT("Cleaning Thread Object\n");
RtlZeroMemory(Thread, sizeof(ETHREAD));
- /* Create Cid Handle */
- DPRINT("Creating Thread Handle (CID)\n");
- if (!(NT_SUCCESS(PsCreateCidHandle(Thread, PsThreadType, &Thread->Cid.UniqueThread)))) {
+ /* Initialize rundown protection */
+ ExInitializeRundownProtection(&Thread->RundownProtect);
- DPRINT1("Failed to create Thread Handle (CID)\n");
- ObDereferenceObject(Process);
+ /* Initialize exit code */
+ Thread->ExitStatus = STATUS_PENDING;
+
+ /* Set the Process CID */
+ Thread->ThreadsProcess = Process;
+ Thread->Cid.UniqueProcess = Process->UniqueProcessId;
+
+ /* Create Cid Handle */
+ CidEntry.Object = Thread;
+ CidEntry.GrantedAccess = 0;
+ Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
+ if (!Thread->Cid.UniqueThread)
+ {
+ /* We couldn't create the CID, dereference the thread and fail */
ObDereferenceObject(Thread);
- return Status;
+ return STATUS_INSUFFICIENT_RESOURCES;
}
- /* Initialize Lists */
- DPRINT("Initialliazing Thread Lists and Locks\n");
+ /* Save the read cluster size */
+ Thread->ReadClusterSize = MmReadClusterSize;
+
+ /* Initialize the LPC Reply Semaphore */
+ KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, 1);
+
+ /* Initialize the list heads and locks */
InitializeListHead(&Thread->LpcReplyChain);
InitializeListHead(&Thread->IrpList);
+ InitializeListHead(&Thread->PostBlockList);
InitializeListHead(&Thread->ActiveTimerListHead);
KeInitializeSpinLock(&Thread->ActiveTimerListLock);
- /* Initialize LPC */
- DPRINT("Initialliazing Thread Semaphore\n");
- KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, LONG_MAX);
-
- /* Allocate Stack for non-GUI Thread */
- DPRINT("Initialliazing Thread Stack\n");
- KernelStack = MmCreateKernelStack(FALSE);
-
- /* Set the Process CID */
- DPRINT("Initialliazing Thread PID and Parent Process\n");
- Thread->Cid.UniqueProcess = Process->UniqueProcessId;
- Thread->ThreadsProcess = Process;
+ /* Acquire rundown protection */
+ if (!ExAcquireRundownProtection (&Process->RundownProtect))
+ {
+ /* Fail */
+ ObDereferenceObject(Thread);
+ return STATUS_PROCESS_IS_TERMINATING;
+ }
/* Now let the kernel initialize the context */
- if (ThreadContext) {
-
- /* User-mode Thread */
-
- /* Create Teb */
- DPRINT("Initialliazing Thread PEB\n");
- TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb);
+ if (ThreadContext)
+ {
+ /* User-mode Thread, create Teb */
+ Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Failed to create the TEB. Release rundown and dereference */
+ ExReleaseRundownProtection(&Process->RundownProtect);
+ ObDereferenceObject(Thread);
+ return Status;
+ }
/* Set the Start Addresses */
- DPRINT("Initialliazing Thread Start Addresses :%x, %x\n", ThreadContext->Eip, ThreadContext->Eax);
- Thread->StartAddress = (PVOID)ThreadContext->Eip;
- Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
+ Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext);
+ Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext);
/* Let the kernel intialize the Thread */
- DPRINT("Initialliazing Kernel Thread\n");
- KeInitializeThread(&Process->Pcb,
- &Thread->Tcb,
- PspUserThreadStartup,
- NULL,
- NULL,
- ThreadContext,
- TebBase,
- KernelStack);
-
- } else {
-
+ Status = KeInitThread(&Thread->Tcb,
+ NULL,
+ PspUserThreadStartup,
+ NULL,
+ Thread->StartAddress,
+ ThreadContext,
+ TebBase,
+ &Process->Pcb);
+ }
+ else
+ {
/* System Thread */
- DPRINT("Initialliazing Thread Start Address :%x\n", StartRoutine);
Thread->StartAddress = StartRoutine;
- Thread->SystemThread = TRUE;
+ PspSetCrossThreadFlag(Thread, CT_SYSTEM_THREAD_BIT);
/* Let the kernel intialize the Thread */
- DPRINT("Initialliazing Kernel Thread\n");
- KeInitializeThread(&Process->Pcb,
- &Thread->Tcb,
- PspSystemThreadStartup,
- StartRoutine,
- StartContext,
- NULL,
- NULL,
- KernelStack);
+ Status = KeInitThread(&Thread->Tcb,
+ NULL,
+ PspSystemThreadStartup,
+ StartRoutine,
+ StartContext,
+ NULL,
+ NULL,
+ &Process->Pcb);
+ }
+
+ /* Check if we failed */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Delete the TEB if we had done */
+ if (TebBase) MmDeleteTeb(Process, TebBase);
+
+ /* Release rundown and dereference */
+ ExReleaseRundownProtection(&Process->RundownProtect);
+ ObDereferenceObject(Thread);
+ return Status;
+ }
+
+ /* Lock the process */
+ KeEnterCriticalRegion();
+ ExAcquirePushLockExclusive(&Process->ProcessLock);
+
+ /* Make sure the proces didn't just die on us */
+ if (Process->ProcessDelete) goto Quickie;
+
+ /* Check if the thread was ours, terminated and it was user mode */
+ if ((Thread->Terminated) &&
+ (ThreadContext) &&
+ (Thread->ThreadsProcess == Process))
+ {
+ /* Cleanup, we don't want to start it up and context switch */
+ goto Quickie;
}
/*
* Note, this is the ETHREAD Thread List. It is removed in
* ps/kill.c!PspExitThread.
*/
- DPRINT("Inserting into Process Thread List \n");
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
+ Process->ActiveThreads++;
+
+ /* Start the thread */
+ KeStartThread(&Thread->Tcb);
+
+ /* Release the process lock */
+ ExReleasePushLockExclusive(&Process->ProcessLock);
+ KeLeaveCriticalRegion();
+
+ /* Release rundown */
+ ExReleaseRundownProtection(&Process->RundownProtect);
+
+ /* Notify WMI */
+ //WmiTraceProcess(Process, TRUE);
+ //WmiTraceThread(Thread, InitialTeb, TRUE);
/* Notify Thread Creation */
- DPRINT("Running Thread Notify \n");
PspRunCreateThreadNotifyRoutines(Thread, TRUE);
- /* FIXME: Use Lock */
- DPRINT("Apcs Queueable: %d \n", Thread->Tcb.ApcQueueable);
- Thread->Tcb.ApcQueueable = TRUE;
+ /* Reference ourselves as a keep-alive */
+ ObReferenceObjectEx(Thread, 2);
/* Suspend the Thread if we have to */
- if (CreateSuspended) {
+ if (CreateSuspended) KeSuspendThread(&Thread->Tcb);
+
+ /* Check if we were already terminated */
+ if (Thread->Terminated) KeForceResumeThread(&Thread->Tcb);
+
+ /* Create an access state */
+ Status = SeCreateAccessStateEx(NULL,
+ ThreadContext ?
+ PsGetCurrentProcess() : Process,
+ &LocalAccessState,
+ &AuxData,
+ DesiredAccess,
+ &PsThreadType->TypeInfo.GenericMapping);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Access state failed, thread is dead */
+ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
- DPRINT("Suspending Thread\n");
- KeSuspendThread(&Thread->Tcb);
- }
+ /* If we were suspended, wake it up */
+ if (CreateSuspended) KeResumeThread(&Thread->Tcb);
- /* Reference ourselves as a keep-alive */
- ObReferenceObject(Thread);
+ /* Dispatch thread */
+ KeReadyThread(&Thread->Tcb);
+
+ /* Dereference completely to kill it */
+ ObDereferenceObjectEx(Thread, 2);
+ return Status;
+ }
/* Insert the Thread into the Object Manager */
- DPRINT("Inserting Thread\n");
- Status = ObInsertObject((PVOID)Thread,
- NULL,
+ Status = ObInsertObject(Thread,
+ AccessState,
DesiredAccess,
0,
NULL,
&hThread);
- /* Return Cid and Handle */
- DPRINT("All worked great!\n");
- if(NT_SUCCESS(Status)) {
+ /* Delete the access state if we had one */
+ if (AccessState) SeDeleteAccessState(AccessState);
+
+ /* Check for success */
+ if (NT_SUCCESS(Status))
+ {
+ /* Wrap in SEH to protect against bad user-mode pointers */
+ _SEH2_TRY
+ {
+ /* Return Cid and Handle */
+ if (ClientId) *ClientId = Thread->Cid;
+ *ThreadHandle = hThread;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Thread insertion failed, thread is dead */
+ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
- _SEH_TRY {
+ /* If we were suspended, wake it up */
+ if (CreateSuspended) KeResumeThread(&Thread->Tcb);
- if(ClientId != NULL) {
+ /* Dispatch thread */
+ KeReadyThread(&Thread->Tcb);
- *ClientId = Thread->Cid;
- }
- *ThreadHandle = hThread;
+ /* Dereference it, leaving only the keep-alive */
+ ObDereferenceObject(Thread);
- } _SEH_HANDLE {
+ /* Close its handle, killing it */
+ ObCloseHandle(ThreadHandle, PreviousMode);
- Status = _SEH_GetExceptionCode();
+ /* Return the exception code */
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
+ else
+ {
+ /* Thread insertion failed, thread is dead */
+ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
- } _SEH_END;
+ /* If we were suspended, wake it up */
+ if (CreateSuspended) KeResumeThread(&Thread->Tcb);
}
- /* FIXME: SECURITY */
+ /* Get the create time */
+ KeQuerySystemTime(&Thread->CreateTime);
+ ASSERT(!(Thread->CreateTime.HighPart & 0xF0000000));
+
+ /* Make sure the thread isn't dead */
+ if (!Thread->DeadThread)
+ {
+ /* Get the thread's SD */
+ Status = ObGetObjectSecurity(Thread,
+ &SecurityDescriptor,
+ &SdAllocated);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Thread insertion failed, thread is dead */
+ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
+
+ /* If we were suspended, wake it up */
+ if (CreateSuspended) KeResumeThread(&Thread->Tcb);
+
+ /* Dispatch thread */
+ KeReadyThread(&Thread->Tcb);
+
+ /* Dereference it, leaving only the keep-alive */
+ ObDereferenceObject(Thread);
+
+ /* Close its handle, killing it */
+ ObCloseHandle(ThreadHandle, PreviousMode);
+ return Status;
+ }
+
+ /* Create the subject context */
+ SubjectContext.ProcessAuditId = Process;
+ SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
+ SubjectContext.ClientToken = NULL;
+
+ /* Do the access check */
+ Result = SeAccessCheck(SecurityDescriptor,
+ &SubjectContext,
+ FALSE,
+ MAXIMUM_ALLOWED,
+ 0,
+ NULL,
+ &PsThreadType->TypeInfo.GenericMapping,
+ PreviousMode,
+ &Thread->GrantedAccess,
+ &AccessStatus);
+
+ /* Dereference the token and let go the SD */
+ ObFastDereferenceObject(&Process->Token,
+ SubjectContext.PrimaryToken);
+ ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
+
+ /* Remove access if it failed */
+ if (!Result) Process->GrantedAccess = 0;
+
+ /* Set least some minimum access */
+ Thread->GrantedAccess |= (THREAD_TERMINATE |
+ THREAD_SET_INFORMATION |
+ THREAD_QUERY_INFORMATION);
+ }
+ else
+ {
+ /* Set the thread access mask to maximum */
+ Thread->GrantedAccess = THREAD_ALL_ACCESS;
+ }
/* Dispatch thread */
- DPRINT("About to dispatch the thread: %x!\n", &Thread->Tcb);
- OldIrql = KeAcquireDispatcherDatabaseLock ();
- KiUnblockThread(&Thread->Tcb, NULL, 0);
+ KeReadyThread(&Thread->Tcb);
+
+ /* Dereference it, leaving only the keep-alive */
ObDereferenceObject(Thread);
- KeReleaseDispatcherDatabaseLock(OldIrql);
/* Return */
- DPRINT("Returning\n");
return Status;
+
+ /* Most annoying failure case ever, where we undo almost all manually */
+Quickie:
+ /* When we get here, the process is locked, unlock it */
+ ExReleasePushLockExclusive(&Process->ProcessLock);
+ KeLeaveCriticalRegion();
+
+ /* Uninitailize it */
+ KeUninitThread(&Thread->Tcb);
+
+ /* If we had a TEB, delete it */
+ if (TebBase) MmDeleteTeb(Process, TebBase);
+
+ /* Release rundown protection, which we also hold */
+ ExReleaseRundownProtection(&Process->RundownProtect);
+
+ /* Dereference the thread and return failure */
+ ObDereferenceObject(Thread);
+ return STATUS_PROCESS_IS_TERMINATING;
}
+/* PUBLIC FUNCTIONS **********************************************************/
+
/*
* @implemented
*/
NTSTATUS
-STDCALL
-PsCreateSystemThread(PHANDLE ThreadHandle,
- ACCESS_MASK DesiredAccess,
- POBJECT_ATTRIBUTES ObjectAttributes,
- HANDLE ProcessHandle,
- PCLIENT_ID ClientId,
- PKSTART_ROUTINE StartRoutine,
- PVOID StartContext)
+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)
{
PEPROCESS TargetProcess = NULL;
HANDLE Handle = ProcessHandle;
+ PAGED_CODE();
+ PSTRACE(PS_THREAD_DEBUG,
+ "ProcessHandle: %p StartRoutine: %p StartContext: %p\n",
+ ProcessHandle, StartRoutine, StartContext);
/* Check if we have a handle. If not, use the System Process */
- if (!ProcessHandle) {
-
+ if (!ProcessHandle)
+ {
Handle = NULL;
TargetProcess = PsInitialSystemProcess;
}
StartContext);
}
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+PsLookupThreadByThreadId(IN HANDLE ThreadId,
+ OUT PETHREAD *Thread)
+{
+ PHANDLE_TABLE_ENTRY CidEntry;
+ PETHREAD FoundThread;
+ NTSTATUS Status = STATUS_INVALID_PARAMETER;
+ PAGED_CODE();
+ PSTRACE(PS_THREAD_DEBUG, "ThreadId: %p\n", ThreadId);
+ KeEnterCriticalRegion();
+
+ /* Get the CID Handle Entry */
+ CidEntry = ExMapHandleToPointer(PspCidTable, ThreadId);
+ if (CidEntry)
+ {
+ /* Get the Process */
+ FoundThread = CidEntry->Object;
+
+ /* Make sure it's really a process */
+ if (FoundThread->Tcb.DispatcherHeader.Type == ThreadObject)
+ {
+ /* Safe Reference and return it */
+ if (ObReferenceObjectSafe(FoundThread))
+ {
+ *Thread = FoundThread;
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ /* Unlock the Entry */
+ ExUnlockHandleTableEntry(PspCidTable, CidEntry);
+ }
+
+ /* Return to caller */
+ KeLeaveCriticalRegion();
+ return Status;
+}
+
/*
* @implemented
*/
HANDLE
-STDCALL
+NTAPI
PsGetCurrentThreadId(VOID)
{
- return(PsGetCurrentThread()->Cid.UniqueThread);
+ return PsGetCurrentThread()->Cid.UniqueThread;
}
/*
* @implemented
*/
ULONG
-STDCALL
-PsGetThreadFreezeCount(PETHREAD Thread)
+NTAPI
+PsGetThreadFreezeCount(IN PETHREAD Thread)
{
return Thread->Tcb.FreezeCount;
}
* @implemented
*/
BOOLEAN
-STDCALL
-PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
+NTAPI
+PsGetThreadHardErrorsAreDisabled(IN PETHREAD Thread)
{
- return Thread->HardErrorsAreDisabled;
+ return Thread->HardErrorsAreDisabled ? TRUE : FALSE;
}
/*
* @implemented
*/
HANDLE
-STDCALL
-PsGetThreadId(PETHREAD Thread)
+NTAPI
+PsGetThreadId(IN PETHREAD Thread)
{
return Thread->Cid.UniqueThread;
}
* @implemented
*/
PEPROCESS
-STDCALL
-PsGetThreadProcess(PETHREAD Thread)
+NTAPI
+PsGetThreadProcess(IN PETHREAD Thread)
{
return Thread->ThreadsProcess;
}
* @implemented
*/
HANDLE
-STDCALL
-PsGetThreadProcessId(PETHREAD Thread)
+NTAPI
+PsGetThreadProcessId(IN PETHREAD Thread)
{
return Thread->Cid.UniqueProcess;
}
* @implemented
*/
HANDLE
-STDCALL
-PsGetThreadSessionId(PETHREAD Thread)
+NTAPI
+PsGetThreadSessionId(IN PETHREAD Thread)
{
return (HANDLE)Thread->ThreadsProcess->Session;
}
* @implemented
*/
PTEB
-STDCALL
-PsGetThreadTeb(PETHREAD Thread)
+NTAPI
+PsGetThreadTeb(IN PETHREAD Thread)
{
return Thread->Tcb.Teb;
}
* @implemented
*/
PVOID
-STDCALL
-PsGetThreadWin32Thread(PETHREAD Thread)
+NTAPI
+PsGetThreadWin32Thread(IN PETHREAD Thread)
{
return Thread->Tcb.Win32Thread;
}
* @implemented
*/
KPROCESSOR_MODE
-STDCALL
+NTAPI
PsGetCurrentThreadPreviousMode(VOID)
{
return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
* @implemented
*/
PVOID
-STDCALL
+NTAPI
PsGetCurrentThreadStackBase(VOID)
{
return PsGetCurrentThread()->Tcb.StackBase;
* @implemented
*/
PVOID
-STDCALL
+NTAPI
PsGetCurrentThreadStackLimit(VOID)
{
return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
* @implemented
*/
BOOLEAN
-STDCALL
+NTAPI
PsIsThreadTerminating(IN PETHREAD Thread)
{
- return (Thread->Terminated ? TRUE : FALSE);
+ return Thread->Terminated ? TRUE : FALSE;
}
/*
* @implemented
*/
BOOLEAN
-STDCALL
-PsIsSystemThread(PETHREAD Thread)
+NTAPI
+PsIsSystemThread(IN PETHREAD Thread)
{
- return (Thread->SystemThread ? TRUE: FALSE);
+ return Thread->SystemThread ? TRUE: FALSE;
}
/*
* @implemented
*/
BOOLEAN
-STDCALL
-PsIsThreadImpersonating(PETHREAD Thread)
+NTAPI
+PsIsThreadImpersonating(IN PETHREAD Thread)
{
- return Thread->ActiveImpersonationInfo;
+ return Thread->ActiveImpersonationInfo ? TRUE : FALSE;
}
/*
* @implemented
*/
VOID
-STDCALL
-PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
- BOOLEAN HardErrorsAreDisabled)
+NTAPI
+PsSetThreadHardErrorsAreDisabled(IN PETHREAD Thread,
+ IN BOOLEAN HardErrorsAreDisabled)
{
Thread->HardErrorsAreDisabled = HardErrorsAreDisabled;
}
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+PsGetCurrentThreadWin32Thread(VOID)
+{
+ return PsGetCurrentThread()->Tcb.Win32Thread;
+}
+
/*
* @implemented
*/
VOID
-STDCALL
-PsSetThreadWin32Thread(PETHREAD Thread,
- PVOID Win32Thread)
+NTAPI
+PsSetThreadWin32Thread(IN PETHREAD Thread,
+ IN PVOID Win32Thread)
{
Thread->Tcb.Win32Thread = Win32Thread;
}
NTSTATUS
-STDCALL
+NTAPI
NtCreateThread(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
OUT PCLIENT_ID ClientId,
IN PCONTEXT ThreadContext,
IN BOOLEAN CreateSuspended)
{
INITIAL_TEB SafeInitialTeb;
-
PAGED_CODE();
+ PSTRACE(PS_THREAD_DEBUG,
+ "ProcessHandle: %p Context: %p\n", ProcessHandle, ThreadContext);
- DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
- ThreadHandle,ThreadContext);
-
- if(KeGetPreviousMode() != KernelMode) {
-
- _SEH_TRY {
-
- ProbeForWrite(ThreadHandle,
- sizeof(HANDLE),
- sizeof(ULONG));
-
- if(ClientId != NULL) {
-
- ProbeForWrite(ClientId,
- sizeof(CLIENT_ID),
- sizeof(ULONG));
- }
-
- if(ThreadContext != NULL) {
-
- ProbeForRead(ThreadContext,
- sizeof(CONTEXT),
- sizeof(ULONG));
+ /* Check if this was from user-mode */
+ if (KeGetPreviousMode() != KernelMode)
+ {
+ /* Make sure that we got a context */
+ if (!ThreadContext) return STATUS_INVALID_PARAMETER;
- } else {
+ /* Protect checks */
+ _SEH2_TRY
+ {
+ /* Make sure the handle pointer we got is valid */
+ ProbeForWriteHandle(ThreadHandle);
- DPRINT1("No context for User-Mode Thread!!\n");
- return STATUS_INVALID_PARAMETER;
+ /* Check if the caller wants a client id */
+ if (ClientId)
+ {
+ /* Make sure we can write to it */
+ ProbeForWrite(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
}
- ProbeForRead(InitialTeb,
- sizeof(INITIAL_TEB),
- sizeof(ULONG));
-
- } _SEH_HANDLE {
-
- return _SEH_GetExceptionCode();
+ /* Make sure that the entire context is readable */
+ ProbeForRead(ThreadContext, sizeof(CONTEXT), sizeof(ULONG));
- } _SEH_END;
+ /* Check the Initial TEB */
+ ProbeForRead(InitialTeb, sizeof(INITIAL_TEB), sizeof(ULONG));
+ SafeInitialTeb = *InitialTeb;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Return the exception code */
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
+ else
+ {
+ /* Use the Initial TEB as is */
+ SafeInitialTeb = *InitialTeb;
}
-
- /* Use probed data for the Initial TEB */
- SafeInitialTeb = *InitialTeb;
- InitialTeb = &SafeInitialTeb;
/* Call the shared function */
return PspCreateThread(ThreadHandle,
NULL,
ClientId,
ThreadContext,
- InitialTeb,
+ &SafeInitialTeb,
CreateSuspended,
NULL,
NULL);
* @implemented
*/
NTSTATUS
-STDCALL
+NTAPI
NtOpenThread(OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
- IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
- IN PCLIENT_ID ClientId OPTIONAL)
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PCLIENT_ID ClientId OPTIONAL)
{
- KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
CLIENT_ID SafeClientId;
- HANDLE hThread = 0;
- NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Attributes = 0;
+ HANDLE hThread = NULL;
+ NTSTATUS Status;
PETHREAD Thread;
-
+ BOOLEAN HasObjectName = FALSE;
+ ACCESS_STATE AccessState;
+ AUX_ACCESS_DATA AuxData;
PAGED_CODE();
+ PSTRACE(PS_THREAD_DEBUG,
+ "ClientId: %p ObjectAttributes: %p\n", ClientId, ObjectAttributes);
- /* Probe the paraemeters */
- if(PreviousMode != KernelMode)
+ /* Check if we were called from user mode */
+ if (PreviousMode != KernelMode)
{
- _SEH_TRY
+ /* Enter SEH for probing */
+ _SEH2_TRY
{
- ProbeForWrite(ThreadHandle,
- sizeof(HANDLE),
- sizeof(ULONG));
+ /* Probe the thread handle */
+ ProbeForWriteHandle(ThreadHandle);
- if(ClientId != NULL)
+ /* Check for a CID structure */
+ if (ClientId)
{
- ProbeForRead(ClientId,
- sizeof(CLIENT_ID),
- sizeof(ULONG));
-
+ /* Probe and capture it */
+ ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
SafeClientId = *ClientId;
ClientId = &SafeClientId;
}
+
+ /*
+ * Just probe the object attributes structure, don't capture it
+ * completely. This is done later if necessary
+ */
+ ProbeForRead(ObjectAttributes,
+ sizeof(OBJECT_ATTRIBUTES),
+ sizeof(ULONG));
+ HasObjectName = (ObjectAttributes->ObjectName != NULL);
+ Attributes = ObjectAttributes->Attributes;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Return the exception code */
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ }
+ else
+ {
+ /* Otherwise just get the data directly */
+ HasObjectName = (ObjectAttributes->ObjectName != NULL);
+ Attributes = ObjectAttributes->Attributes;
+ }
+
+ /* Can't pass both, fail */
+ if ((HasObjectName) && (ClientId)) return STATUS_INVALID_PARAMETER_MIX;
+
+ /* Create an access state */
+ Status = SeCreateAccessState(&AccessState,
+ &AuxData,
+ DesiredAccess,
+ &PsProcessType->TypeInfo.GenericMapping);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Check if this is a debugger */
+ if (SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
+ {
+ /* Did he want full access? */
+ if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
+ {
+ /* Give it to him */
+ AccessState.PreviouslyGrantedAccess |= THREAD_ALL_ACCESS;
}
- _SEH_HANDLE
+ else
{
- Status = _SEH_GetExceptionCode();
+ /* Otherwise just give every other access he could want */
+ AccessState.PreviouslyGrantedAccess |=
+ AccessState.RemainingDesiredAccess;
}
- _SEH_END;
- if(!NT_SUCCESS(Status)) return Status;
+ /* The caller desires nothing else now */
+ AccessState.RemainingDesiredAccess = 0;
}
/* Open by name if one was given */
- if (ObjectAttributes->ObjectName)
+ if (HasObjectName)
{
/* Open it */
Status = ObOpenObjectByName(ObjectAttributes,
PsThreadType,
- NULL,
PreviousMode,
- DesiredAccess,
+ &AccessState,
+ 0,
NULL,
- hThread);
+ &hThread);
- if (Status != STATUS_SUCCESS)
- {
- DPRINT1("Could not open object by name\n");
- }
-
- /* Return Status */
- return(Status);
+ /* Get rid of the access state */
+ SeDeleteAccessState(&AccessState);
}
else if (ClientId)
{
if (ClientId->UniqueProcess)
{
/* Get the Process */
- if (ClientId->UniqueProcess == (HANDLE)-1) KEBUGCHECK(0);
- DPRINT("Opening by Process ID: %x\n", ClientId->UniqueProcess);
- Status = PsLookupProcessThreadByCid(ClientId,
- NULL,
- &Thread);
+ Status = PsLookupProcessThreadByCid(ClientId, NULL, &Thread);
}
else
{
/* Get the Process */
- DPRINT("Opening by Thread ID: %x\n", ClientId->UniqueThread);
- Status = PsLookupThreadByThreadId(ClientId->UniqueThread,
- &Thread);
+ Status = PsLookupThreadByThreadId(ClientId->UniqueThread, &Thread);
}
- if(!NT_SUCCESS(Status))
+ /* Check if we didn't find anything */
+ if (!NT_SUCCESS(Status))
{
- DPRINT1("Failure to find Thread\n");
+ /* Get rid of the access state and return */
+ SeDeleteAccessState(&AccessState);
return Status;
}
/* Open the Thread Object */
Status = ObOpenObjectByPointer(Thread,
- ObjectAttributes->Attributes,
- NULL,
- DesiredAccess,
+ Attributes,
+ &AccessState,
+ 0,
PsThreadType,
PreviousMode,
- hThread);
- if(!NT_SUCCESS(Status))
- {
- DPRINT1("Failure to open Thread\n");
- }
+ &hThread);
- /* Dereference the thread */
+ /* Delete the access state and dereference the thread */
+ SeDeleteAccessState(&AccessState);
ObDereferenceObject(Thread);
}
+ else
+ {
+ /* Neither an object name nor a client id was passed */
+ return STATUS_INVALID_PARAMETER_MIX;
+ }
- /* Write back the handle */
- if(NT_SUCCESS(Status))
+ /* Check for success */
+ if (NT_SUCCESS(Status))
{
- _SEH_TRY
+ /* Protect against bad user-mode pointers */
+ _SEH2_TRY
{
+ /* Write back the handle */
*ThreadHandle = hThread;
}
- _SEH_HANDLE
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
- Status = _SEH_GetExceptionCode();
+ /* Get the exception code */
+ Status = _SEH2_GetExceptionCode();
}
- _SEH_END;
+ _SEH2_END;
}
/* Return status */
return Status;
}
-NTSTATUS
-STDCALL
-NtYieldExecution(VOID)
-{
- KiDispatchThread(Ready);
- return(STATUS_SUCCESS);
-}
-
-NTSTATUS
-STDCALL
-NtTestAlert(VOID)
-{
- /* Check and Alert Thread if needed */
- return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED : STATUS_SUCCESS;
-}
-
-/*
- * @implemented
- */
-KPROCESSOR_MODE
-STDCALL
-ExGetPreviousMode (VOID)
-{
- return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
-}
-
/* EOF */