reactos/reactos/ntoskrnl/ps/create.c
Eric Kohl 20f65a3fb9 Fixed callback calling conventions (part 1).
svn path=/trunk/; revision=2200
2001-08-26 17:30:21 +00:00

655 lines
15 KiB
C

/* $Id: create.c,v 1.39 2001/08/26 17:30:21 ekohl Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ps/thread.c
* PURPOSE: Thread managment
* PROGRAMMER: David Welch (welch@mcmail.com)
* REVISION HISTORY:
* 23/06/98: Created
* 12/10/99: Phillip Susi: Thread priorities, and APC work
*/
/*
* NOTE:
*
* All of the routines that manipulate the thread queue synchronize on
* a single spinlock
*
*/
/* INCLUDES ****************************************************************/
#include <ddk/ntddk.h>
#include <internal/ke.h>
#include <internal/ob.h>
#include <internal/ps.h>
#include <internal/ob.h>
#include <internal/id.h>
#include <internal/dbg.h>
#define NDEBUG
#include <internal/debug.h>
/* GLOBAL *******************************************************************/
static ULONG PiNextThreadUniqueId = 0;
extern KSPIN_LOCK PiThreadListLock;
extern ULONG PiNrThreads;
extern LIST_ENTRY PiThreadListHead;
/* FUNCTIONS ***************************************************************/
NTSTATUS STDCALL
PsAssignImpersonationToken(PETHREAD Thread,
HANDLE TokenHandle)
{
PACCESS_TOKEN Token;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
NTSTATUS Status;
if (TokenHandle != NULL)
{
Status = ObReferenceObjectByHandle(TokenHandle,
0,
SeTokenType,
UserMode,
(PVOID*)&Token,
NULL);
if (!NT_SUCCESS(Status))
{
return(Status);
}
ImpersonationLevel = Token->ImpersonationLevel;
}
else
{
Token = NULL;
ImpersonationLevel = 0;
}
PsImpersonateClient(Thread,
Token,
0,
0,
ImpersonationLevel);
if (Token != NULL)
{
ObDereferenceObject(Token);
}
return(STATUS_SUCCESS);
}
VOID STDCALL
PsRevertToSelf(PETHREAD Thread)
{
if (Thread->ActiveImpersonationInfo != 0)
{
Thread->ActiveImpersonationInfo = 0;
ObDereferenceObject(Thread->ImpersonationInfo->Token);
}
}
VOID STDCALL
PsImpersonateClient(PETHREAD Thread,
PACCESS_TOKEN Token,
UCHAR b,
UCHAR c,
SECURITY_IMPERSONATION_LEVEL Level)
{
if (Token == 0)
{
if (Thread->ActiveImpersonationInfo != 0)
{
Thread->ActiveImpersonationInfo = 0;
if (Thread->ImpersonationInfo->Token != NULL)
{
ObDereferenceObject(Thread->ImpersonationInfo->Token);
}
}
return;
}
if (Thread->ActiveImpersonationInfo == 0 ||
Thread->ImpersonationInfo == NULL)
{
Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
sizeof(PS_IMPERSONATION_INFO));
}
Thread->ImpersonationInfo->Level = Level;
Thread->ImpersonationInfo->Unknown2 = c;
Thread->ImpersonationInfo->Unknown1 = b;
Thread->ImpersonationInfo->Token = Token;
ObReferenceObjectByPointer(Token,
0,
SeTokenType,
KernelMode);
Thread->ActiveImpersonationInfo = 1;
}
PACCESS_TOKEN
PsReferenceEffectiveToken(PETHREAD Thread,
PTOKEN_TYPE TokenType,
PUCHAR b,
PSECURITY_IMPERSONATION_LEVEL Level)
{
PEPROCESS Process;
PACCESS_TOKEN Token;
if (Thread->ActiveImpersonationInfo == 0)
{
Process = Thread->ThreadsProcess;
*TokenType = TokenPrimary;
*b = 0;
Token = Process->Token;
}
else
{
Token = Thread->ImpersonationInfo->Token;
*TokenType = TokenImpersonation;
*b = Thread->ImpersonationInfo->Unknown2;
*Level = Thread->ImpersonationInfo->Level;
}
return(Token);
}
NTSTATUS STDCALL
NtImpersonateThread (IN HANDLE ThreadHandle,
IN HANDLE ThreadToImpersonateHandle,
IN PSECURITY_QUALITY_OF_SERVICE
SecurityQualityOfService)
{
PETHREAD Thread;
PETHREAD ThreadToImpersonate;
NTSTATUS Status;
SE_SOME_STRUCT2 b;
Status = ObReferenceObjectByHandle(ThreadHandle,
0,
PsThreadType,
UserMode,
(PVOID*)&Thread,
NULL);
if (!NT_SUCCESS(Status))
{
return(Status);
}
Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
0,
PsThreadType,
UserMode,
(PVOID*)&ThreadToImpersonate,
NULL);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Thread);
return(Status);
}
Status = SeCreateClientSecurity(ThreadToImpersonate,
SecurityQualityOfService,
0,
&b);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Thread);
ObDereferenceObject(ThreadToImpersonate);
return(Status);
}
SeImpersonateClient(&b, Thread);
if (b.Token != NULL)
{
ObDereferenceObject(b.Token);
}
return(STATUS_SUCCESS);
}
NTSTATUS STDCALL
NtOpenThreadToken(IN HANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN BOOLEAN OpenAsSelf,
OUT PHANDLE TokenHandle)
{
#if 0
PETHREAD Thread;
NTSTATUS Status;
PACCESS_TOKEN Token;
Status = ObReferenceObjectByHandle(ThreadHandle,
0,
PsThreadType,
UserMode,
(PVOID*)&Thread,
NULL);
if (!NT_SUCCESS(Status))
{
return(Status);
}
Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
SepCreateImpersonationTokenDacl(Token);
#endif
return(STATUS_UNSUCCESSFUL);
}
PACCESS_TOKEN STDCALL
PsReferenceImpersonationToken(PETHREAD Thread,
PULONG Unknown1,
PULONG Unknown2,
SECURITY_IMPERSONATION_LEVEL* Level)
{
if (Thread->ActiveImpersonationInfo == 0)
{
return(NULL);
}
*Level = Thread->ImpersonationInfo->Level;
*Unknown1 = Thread->ImpersonationInfo->Unknown1;
*Unknown2 = Thread->ImpersonationInfo->Unknown2;
ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
TOKEN_ALL_ACCESS,
SeTokenType,
KernelMode);
return(Thread->ImpersonationInfo->Token);
}
VOID
PiTimeoutThread(struct _KDPC *dpc,
PVOID Context,
PVOID arg1,
PVOID arg2)
{
// wake up the thread, and tell it it timed out
NTSTATUS Status = STATUS_TIMEOUT;
DPRINT("PiTimeoutThread()\n");
KeRemoveAllWaitsThread((PETHREAD)Context, Status);
}
VOID
PiBeforeBeginThread(CONTEXT c)
{
DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
//KeReleaseSpinLock(&PiThreadListLock, PASSIVE_LEVEL);
KeLowerIrql(PASSIVE_LEVEL);
}
#if 0
VOID
PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
{
NTSTATUS Ret;
// KeReleaseSpinLock(&PiThreadListLock,PASSIVE_LEVEL);
KeLowerIrql(PASSIVE_LEVEL);
Ret = StartRoutine(StartContext);
PsTerminateSystemThread(Ret);
KeBugCheck(0);
}
#endif
VOID STDCALL
PiDeleteThread(PVOID ObjectBody)
{
KIRQL oldIrql;
DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
DPRINT("Process %x(%d)\n", ((PETHREAD)ObjectBody)->ThreadsProcess,
ObGetReferenceCount(((PETHREAD)ObjectBody)->ThreadsProcess));
ObDereferenceObject(((PETHREAD)ObjectBody)->ThreadsProcess);
((PETHREAD)ObjectBody)->ThreadsProcess = NULL;
PiNrThreads--;
RemoveEntryList(&((PETHREAD)ObjectBody)->Tcb.ThreadListEntry);
HalReleaseTask((PETHREAD)ObjectBody);
KeReleaseSpinLock(&PiThreadListLock, oldIrql);
DPRINT("PiDeleteThread() finished\n");
}
VOID STDCALL
PiCloseThread(PVOID ObjectBody,
ULONG HandleCount)
{
DPRINT("PiCloseThread(ObjectBody %x)\n", ObjectBody);
DPRINT("ObGetReferenceCount(ObjectBody) %d "
"ObGetHandleCount(ObjectBody) %d\n",
ObGetReferenceCount(ObjectBody),
ObGetHandleCount(ObjectBody));
}
NTSTATUS
PsInitializeThread(HANDLE ProcessHandle,
PETHREAD* ThreadPtr,
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ThreadAttributes,
BOOLEAN First)
{
PETHREAD Thread;
NTSTATUS Status;
KIRQL oldIrql;
PEPROCESS Process;
/*
* Reference process
*/
if (ProcessHandle != NULL)
{
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_CREATE_THREAD,
PsProcessType,
UserMode,
(PVOID*)&Process,
NULL);
if (Status != STATUS_SUCCESS)
{
DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
return(Status);
}
DPRINT( "Creating thread in process %x\n", Process );
}
else
{
Process = PsInitialSystemProcess;
ObReferenceObjectByPointer(Process,
PROCESS_CREATE_THREAD,
PsProcessType,
UserMode);
}
/*
* Create and initialize thread
*/
Status = ObCreateObject(ThreadHandle,
DesiredAccess,
ThreadAttributes,
PsThreadType,
(PVOID*)&Thread);
if (!NT_SUCCESS(Status))
{
return(Status);
}
DPRINT("Thread = %x\n",Thread);
PiNrThreads++;
KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
Thread->ThreadsProcess = Process;
/*
* FIXME: What lock protects this?
*/
InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
&Thread->Tcb.ProcessThreadListEntry);
InitializeListHead(&Thread->TerminationPortList);
KeInitializeSpinLock(&Thread->ActiveTimerListLock);
InitializeListHead(&Thread->IrpList);
Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
&PiNextThreadUniqueId);
Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
Thread->DeadThread = 0;
Thread->Win32Thread = 0;
DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
*ThreadPtr = Thread;
KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
KeReleaseSpinLock(&PiThreadListLock, oldIrql);
Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
Thread->Tcb.Priority = Thread->Tcb.BasePriority;
return(STATUS_SUCCESS);
}
static NTSTATUS
PsCreateTeb(HANDLE ProcessHandle,
PTEB *TebPtr,
PETHREAD Thread,
PINITIAL_TEB InitialTeb)
{
MEMORY_BASIC_INFORMATION Info;
NTSTATUS Status;
ULONG ByteCount;
ULONG RegionSize;
ULONG TebSize;
PVOID TebBase;
TEB Teb;
ULONG ResultLength;
TebBase = (PVOID)0x7FFDE000;
TebSize = PAGESIZE;
while (TRUE)
{
Status = NtQueryVirtualMemory(ProcessHandle,
TebBase,
MemoryBasicInformation,
&Info,
sizeof(Info),
&ResultLength);
if (!NT_SUCCESS(Status))
{
DbgPrint("NtQueryVirtualMemory (Status %x)\n", Status);
KeBugCheck(0);
}
/* FIXME: Race between this and the above check */
if (Info.State == MEM_FREE)
{
/* The TEB must reside in user space */
Status = NtAllocateVirtualMemory(ProcessHandle,
&TebBase,
0,
&TebSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (NT_SUCCESS(Status))
{
break;
}
}
TebBase = TebBase - TebSize;
}
DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
/* set all pointers to and from the TEB */
Teb.Tib.Self = TebBase;
if (Thread->ThreadsProcess)
{
Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
}
DPRINT("Teb.Peb %x\n", Teb.Peb);
/* store stack information from InitialTeb */
if (InitialTeb != NULL)
{
Teb.Tib.StackBase = InitialTeb->StackBase;
Teb.Tib.StackLimit = InitialTeb->StackLimit;
Teb.DeallocationStack = InitialTeb->StackAllocate;
}
/* more initialization */
Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
Teb.CurrentLocale = PsDefaultThreadLocaleId;
DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
/* write TEB data into teb page */
Status = NtWriteVirtualMemory(ProcessHandle,
TebBase,
&Teb,
sizeof(TEB),
&ByteCount);
if (!NT_SUCCESS(Status))
{
/* free TEB */
DPRINT1 ("Writing TEB failed!\n");
RegionSize = 0;
NtFreeVirtualMemory(ProcessHandle,
TebBase,
&RegionSize,
MEM_RELEASE);
return Status;
}
if (TebPtr != NULL)
{
*TebPtr = (PTEB)TebBase;
}
DPRINT("TEB allocated at %p\n", TebBase);
return Status;
}
NTSTATUS STDCALL
NtCreateThread (PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PCLIENT_ID Client,
PCONTEXT ThreadContext,
PINITIAL_TEB InitialTeb,
BOOLEAN CreateSuspended)
{
PETHREAD Thread;
PTEB TebBase;
NTSTATUS Status;
DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
ThreadHandle,ThreadContext);
Status = PsInitializeThread(ProcessHandle,
&Thread,
ThreadHandle,
DesiredAccess,
ObjectAttributes,
FALSE);
if (!NT_SUCCESS(Status))
{
return(Status);
}
Status = Ke386InitThreadWithContext(&Thread->Tcb,
ThreadContext);
if (!NT_SUCCESS(Status))
{
return(Status);
}
Status = PsCreateTeb(ProcessHandle,
&TebBase,
Thread,
InitialTeb);
if (!NT_SUCCESS(Status))
{
return(Status);
}
/* Attention: TebBase is in user memory space */
Thread->Tcb.Teb = TebBase;
Thread->StartAddress=NULL;
if (Client != NULL)
{
*Client=Thread->Cid;
}
/*
* Maybe send a message to the process's debugger
*/
DbgkCreateThread((PVOID)ThreadContext->Eip);
/*
* Start the thread running
*/
if (!CreateSuspended)
{
DPRINT("Not creating suspended\n");
PsUnblockThread(Thread, NULL);
}
else
{
KeBugCheck(0);
}
return(STATUS_SUCCESS);
}
NTSTATUS STDCALL
PsCreateSystemThread(PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PCLIENT_ID ClientId,
PKSTART_ROUTINE StartRoutine,
PVOID StartContext)
/*
* FUNCTION: Creates a thread which executes in kernel mode
* ARGUMENTS:
* ThreadHandle (OUT) = Caller supplied storage for the returned thread
* handle
* DesiredAccess = Requested access to the thread
* ObjectAttributes = Object attributes (optional)
* ProcessHandle = Handle of process thread will run in
* NULL to use system process
* ClientId (OUT) = Caller supplied storage for the returned client id
* of the thread (optional)
* StartRoutine = Entry point for the thread
* StartContext = Argument supplied to the thread when it begins
* execution
* RETURNS: Success or failure status
*/
{
PETHREAD Thread;
NTSTATUS Status;
DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
ThreadHandle,ProcessHandle);
Status = PsInitializeThread(ProcessHandle,
&Thread,
ThreadHandle,
DesiredAccess,
ObjectAttributes,
FALSE);
if (!NT_SUCCESS(Status))
{
return(Status);
}
Thread->StartAddress=StartRoutine;
Status = Ke386InitThread(&Thread->Tcb,
StartRoutine,
StartContext);
if (!NT_SUCCESS(Status))
{
return(Status);
}
if (ClientId!=NULL)
{
*ClientId=Thread->Cid;
}
PsUnblockThread(Thread, NULL);
return(STATUS_SUCCESS);
}
/* EOF */