1 /* $Id: create.c,v 1.64 2003/07/21 21:53:53 royce Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/thread.c
6 * PURPOSE: Thread managment
7 * PROGRAMMER: David Welch (welch@mcmail.com)
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
23 #define NTOS_MODE_KERNEL
25 #include <internal/ke.h>
26 #include <internal/ob.h>
27 #include <internal/ps.h>
28 #include <internal/se.h>
29 #include <internal/id.h>
30 #include <internal/dbg.h>
31 #include <internal/ldr.h>
34 #include <internal/debug.h>
36 /* GLOBAL *******************************************************************/
38 static ULONG PiNextThreadUniqueId
= 0;
40 extern KSPIN_LOCK PiThreadListLock
;
41 extern ULONG PiNrThreads
;
43 extern LIST_ENTRY PiThreadListHead
;
45 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
47 static ULONG PiThreadNotifyRoutineCount
= 0;
48 static PCREATE_THREAD_NOTIFY_ROUTINE
49 PiThreadNotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
51 /* FUNCTIONS ***************************************************************/
54 PsAssignImpersonationToken(PETHREAD Thread
,
58 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
;
61 if (TokenHandle
!= NULL
)
63 Status
= ObReferenceObjectByHandle(TokenHandle
,
69 if (!NT_SUCCESS(Status
))
73 ImpersonationLevel
= Token
->ImpersonationLevel
;
78 ImpersonationLevel
= 0;
81 PsImpersonateClient(Thread
,
88 ObDereferenceObject(Token
);
90 return(STATUS_SUCCESS
);
101 Thread
= PsGetCurrentThread();
103 if (Thread
->ActiveImpersonationInfo
!= 0)
105 ObDereferenceObject(Thread
->ImpersonationInfo
->Token
);
106 Thread
->ActiveImpersonationInfo
= 0;
114 PsImpersonateClient(PETHREAD Thread
,
118 SECURITY_IMPERSONATION_LEVEL Level
)
122 if (Thread
->ActiveImpersonationInfo
!= 0)
124 Thread
->ActiveImpersonationInfo
= 0;
125 if (Thread
->ImpersonationInfo
->Token
!= NULL
)
127 ObDereferenceObject(Thread
->ImpersonationInfo
->Token
);
132 if (Thread
->ActiveImpersonationInfo
== 0 ||
133 Thread
->ImpersonationInfo
== NULL
)
135 Thread
->ImpersonationInfo
= ExAllocatePool(NonPagedPool
,
136 sizeof(PS_IMPERSONATION_INFO
));
138 Thread
->ImpersonationInfo
->Level
= Level
;
139 Thread
->ImpersonationInfo
->Unknown2
= c
;
140 Thread
->ImpersonationInfo
->Unknown1
= b
;
141 Thread
->ImpersonationInfo
->Token
= Token
;
142 ObReferenceObjectByPointer(Token
,
146 Thread
->ActiveImpersonationInfo
= 1;
150 PsReferenceEffectiveToken(PETHREAD Thread
,
151 PTOKEN_TYPE TokenType
,
153 PSECURITY_IMPERSONATION_LEVEL Level
)
158 if (Thread
->ActiveImpersonationInfo
== 0)
160 Process
= Thread
->ThreadsProcess
;
161 *TokenType
= TokenPrimary
;
163 Token
= Process
->Token
;
167 Token
= Thread
->ImpersonationInfo
->Token
;
168 *TokenType
= TokenImpersonation
;
169 *b
= Thread
->ImpersonationInfo
->Unknown2
;
170 *Level
= Thread
->ImpersonationInfo
->Level
;
176 NtImpersonateThread(IN HANDLE ThreadHandle
,
177 IN HANDLE ThreadToImpersonateHandle
,
178 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService
)
181 PETHREAD ThreadToImpersonate
;
183 SECURITY_CLIENT_CONTEXT ClientContext
;
185 Status
= ObReferenceObjectByHandle(ThreadHandle
,
191 if (!NT_SUCCESS(Status
))
196 Status
= ObReferenceObjectByHandle(ThreadToImpersonateHandle
,
200 (PVOID
*)&ThreadToImpersonate
,
202 if (!NT_SUCCESS(Status
))
204 ObDereferenceObject(Thread
);
208 Status
= SeCreateClientSecurity(ThreadToImpersonate
,
209 SecurityQualityOfService
,
212 if (!NT_SUCCESS(Status
))
214 ObDereferenceObject(Thread
);
215 ObDereferenceObject(ThreadToImpersonate
);
219 SeImpersonateClient(&ClientContext
, Thread
);
220 if (ClientContext
.Token
!= NULL
)
222 ObDereferenceObject(ClientContext
.Token
);
224 return(STATUS_SUCCESS
);
228 NtOpenThreadToken(IN HANDLE ThreadHandle
,
229 IN ACCESS_MASK DesiredAccess
,
230 IN BOOLEAN OpenAsSelf
,
231 OUT PHANDLE TokenHandle
)
238 Status
= ObReferenceObjectByHandle(ThreadHandle
,
244 if (!NT_SUCCESS(Status
))
249 Token
= PsReferencePrimaryToken(Thread
->ThreadsProcess
);
250 SepCreateImpersonationTokenDacl(Token
);
252 return(STATUS_UNSUCCESSFUL
);
258 PACCESS_TOKEN STDCALL
259 PsReferenceImpersonationToken(PETHREAD Thread
,
262 SECURITY_IMPERSONATION_LEVEL
* Level
)
264 if (Thread
->ActiveImpersonationInfo
== 0)
269 *Level
= Thread
->ImpersonationInfo
->Level
;
270 *Unknown1
= Thread
->ImpersonationInfo
->Unknown1
;
271 *Unknown2
= Thread
->ImpersonationInfo
->Unknown2
;
272 ObReferenceObjectByPointer(Thread
->ImpersonationInfo
->Token
,
276 return(Thread
->ImpersonationInfo
->Token
);
280 PiBeforeBeginThread(CONTEXT c
)
282 KeLowerIrql(PASSIVE_LEVEL
);
286 PiDeleteThread(PVOID ObjectBody
)
291 PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
292 ULONG NotifyRoutineCount
;
294 Thread
= (PETHREAD
)ObjectBody
;
296 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody
);
298 /* Terminate Win32 thread */
299 PsTerminateWin32Thread (Thread
);
301 ObDereferenceObject(Thread
->ThreadsProcess
);
302 Thread
->ThreadsProcess
= NULL
;
304 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
305 for (i
= 0; i
< PiThreadNotifyRoutineCount
; i
++)
307 NotifyRoutine
[i
] = PiThreadNotifyRoutine
[i
];
309 NotifyRoutineCount
= PiThreadNotifyRoutineCount
;
311 RemoveEntryList(&Thread
->Tcb
.ThreadListEntry
);
312 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
314 for (i
= 0; i
< NotifyRoutineCount
; i
++)
316 //must be called below DISPATCH_LVL
317 NotifyRoutine
[i
](Thread
->Cid
.UniqueProcess
,
318 Thread
->Cid
.UniqueThread
,
322 KeReleaseThread(Thread
);
323 DPRINT("PiDeleteThread() finished\n");
327 PsInitializeThread(HANDLE ProcessHandle
,
329 PHANDLE ThreadHandle
,
330 ACCESS_MASK DesiredAccess
,
331 POBJECT_ATTRIBUTES ThreadAttributes
,
339 ULONG NotifyRoutineCount
;
340 PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
[MAX_THREAD_NOTIFY_ROUTINE_COUNT
];
345 if (ProcessHandle
!= NULL
)
347 Status
= ObReferenceObjectByHandle(ProcessHandle
,
348 PROCESS_CREATE_THREAD
,
353 if (Status
!= STATUS_SUCCESS
)
355 DPRINT("Failed at %s:%d\n",__FILE__
,__LINE__
);
358 DPRINT( "Creating thread in process %x\n", Process
);
362 Process
= PsInitialSystemProcess
;
363 ObReferenceObjectByPointer(Process
,
364 PROCESS_CREATE_THREAD
,
370 * Create and initialize thread
372 Status
= ObRosCreateObject(ThreadHandle
,
377 if (!NT_SUCCESS(Status
))
382 DPRINT("Thread = %x\n",Thread
);
386 KeInitializeThread(&Process
->Pcb
, &Thread
->Tcb
, First
);
387 Thread
->ThreadsProcess
= Process
;
389 * FIXME: What lock protects this?
391 InsertTailList(&Thread
->ThreadsProcess
->ThreadListHead
,
392 &Thread
->Tcb
.ProcessThreadListEntry
);
393 InitializeListHead(&Thread
->TerminationPortList
);
394 KeInitializeSpinLock(&Thread
->ActiveTimerListLock
);
395 InitializeListHead(&Thread
->IrpList
);
396 Thread
->Cid
.UniqueThread
= (HANDLE
)InterlockedIncrement(
397 (LONG
*)&PiNextThreadUniqueId
);
398 Thread
->Cid
.UniqueProcess
= (HANDLE
)Thread
->ThreadsProcess
->UniqueProcessId
;
399 Thread
->DeadThread
= 0;
400 Thread
->Win32Thread
= 0;
401 DPRINT("Thread->Cid.UniqueThread %d\n",Thread
->Cid
.UniqueThread
);
405 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
406 InsertTailList(&PiThreadListHead
, &Thread
->Tcb
.ThreadListEntry
);
407 for (i
= 0; i
< PiThreadNotifyRoutineCount
; i
++)
409 NotifyRoutine
[i
] = PiThreadNotifyRoutine
[i
];
411 NotifyRoutineCount
= PiThreadNotifyRoutineCount
;
412 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
414 Thread
->Tcb
.BasePriority
= Thread
->ThreadsProcess
->Pcb
.BasePriority
;
415 Thread
->Tcb
.Priority
= Thread
->Tcb
.BasePriority
;
417 for (i
= 0; i
< NotifyRoutineCount
; i
++)
419 //must be called below DISPATCH_LVL
420 NotifyRoutine
[i
](Thread
->Cid
.UniqueProcess
,
421 Thread
->Cid
.UniqueThread
,
425 return(STATUS_SUCCESS
);
430 PsCreateTeb(HANDLE ProcessHandle
,
433 PUSER_STACK UserStack
)
435 MEMORY_BASIC_INFORMATION Info
;
444 TebBase
= (PVOID
)0x7FFDE000;
449 Status
= NtQueryVirtualMemory(ProcessHandle
,
451 MemoryBasicInformation
,
455 if (!NT_SUCCESS(Status
))
457 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status
);
460 /* FIXME: Race between this and the above check */
461 if (Info
.State
== MEM_FREE
)
463 /* The TEB must reside in user space */
464 Status
= NtAllocateVirtualMemory(ProcessHandle
,
468 MEM_RESERVE
| MEM_COMMIT
,
470 if (NT_SUCCESS(Status
))
476 TebBase
= TebBase
- TebSize
;
479 DPRINT ("TebBase %p TebSize %lu\n", TebBase
, TebSize
);
481 RtlZeroMemory(&Teb
, sizeof(TEB
));
482 /* set all pointers to and from the TEB */
483 Teb
.Tib
.Self
= TebBase
;
484 if (Thread
->ThreadsProcess
)
486 Teb
.Peb
= Thread
->ThreadsProcess
->Peb
; /* No PEB yet!! */
488 DPRINT("Teb.Peb %x\n", Teb
.Peb
);
490 /* store stack information from UserStack */
491 if(UserStack
!= NULL
)
493 /* fixed-size stack */
494 if(UserStack
->FixedStackBase
&& UserStack
->FixedStackLimit
)
496 Teb
.Tib
.StackBase
= UserStack
->FixedStackBase
;
497 Teb
.Tib
.StackLimit
= UserStack
->FixedStackLimit
;
498 Teb
.DeallocationStack
= UserStack
->FixedStackLimit
;
500 /* expandable stack */
503 Teb
.Tib
.StackBase
= UserStack
->ExpandableStackBase
;
504 Teb
.Tib
.StackLimit
= UserStack
->ExpandableStackLimit
;
505 Teb
.DeallocationStack
= UserStack
->ExpandableStackBottom
;
509 /* more initialization */
510 Teb
.Cid
.UniqueThread
= Thread
->Cid
.UniqueThread
;
511 Teb
.Cid
.UniqueProcess
= Thread
->Cid
.UniqueProcess
;
512 Teb
.CurrentLocale
= PsDefaultThreadLocaleId
;
514 /* Terminate the exception handler list */
515 Teb
.Tib
.ExceptionList
= (PVOID
)-1;
517 DPRINT("sizeof(TEB) %x\n", sizeof(TEB
));
519 /* write TEB data into teb page */
520 Status
= NtWriteVirtualMemory(ProcessHandle
,
526 if (!NT_SUCCESS(Status
))
529 DPRINT1 ("Writing TEB failed!\n");
532 NtFreeVirtualMemory(ProcessHandle
,
542 *TebPtr
= (PTEB
)TebBase
;
545 DPRINT("TEB allocated at %p\n", TebBase
);
551 LdrInitApcRundownRoutine(PKAPC Apc
)
557 LdrInitApcKernelRoutine(PKAPC Apc
,
558 PKNORMAL_ROUTINE
* NormalRoutine
,
559 PVOID
* NormalContext
,
560 PVOID
* SystemArgument1
,
561 PVOID
* SystemArgument2
)
567 NtCreateThread(PHANDLE ThreadHandle
,
568 ACCESS_MASK DesiredAccess
,
569 POBJECT_ATTRIBUTES ObjectAttributes
,
570 HANDLE ProcessHandle
,
572 PCONTEXT ThreadContext
,
573 PUSER_STACK UserStack
,
574 BOOLEAN CreateSuspended
)
581 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
582 ThreadHandle
,ThreadContext
);
584 Status
= PsInitializeThread(ProcessHandle
,
590 if (!NT_SUCCESS(Status
))
595 Status
= KiArchInitThreadWithContext(&Thread
->Tcb
, ThreadContext
);
596 if (!NT_SUCCESS(Status
))
601 Status
= PsCreateTeb(ProcessHandle
,
605 if (!NT_SUCCESS(Status
))
609 Thread
->Tcb
.Teb
= TebBase
;
611 Thread
->StartAddress
= NULL
;
615 *Client
= Thread
->Cid
;
619 * Maybe send a message to the process's debugger
621 DbgkCreateThread((PVOID
)ThreadContext
->Eip
);
624 * First, force the thread to be non-alertable for user-mode alerts.
626 Thread
->Tcb
.Alertable
= FALSE
;
629 * If the thread is to be created suspended then queue an APC to
630 * do the suspend before we run any userspace code.
634 PsSuspendThread(Thread
, NULL
);
638 * Queue an APC to the thread that will execute the ntdll startup
641 LdrInitApc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
642 KeInitializeApc(LdrInitApc
, &Thread
->Tcb
, OriginalApcEnvironment
, LdrInitApcKernelRoutine
,
643 LdrInitApcRundownRoutine
, LdrpGetSystemDllEntryPoint(),
645 KeInsertQueueApc(LdrInitApc
, NULL
, NULL
, IO_NO_INCREMENT
);
648 * Start the thread running and force it to execute the APC(s) we just
649 * queued before it runs anything else in user-mode.
651 Thread
->Tcb
.Alertable
= TRUE
;
652 Thread
->Tcb
.Alerted
[0] = 1;
653 PsUnblockThread(Thread
, NULL
);
655 return(STATUS_SUCCESS
);
663 PsCreateSystemThread(PHANDLE ThreadHandle
,
664 ACCESS_MASK DesiredAccess
,
665 POBJECT_ATTRIBUTES ObjectAttributes
,
666 HANDLE ProcessHandle
,
668 PKSTART_ROUTINE StartRoutine
,
671 * FUNCTION: Creates a thread which executes in kernel mode
673 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
675 * DesiredAccess = Requested access to the thread
676 * ObjectAttributes = Object attributes (optional)
677 * ProcessHandle = Handle of process thread will run in
678 * NULL to use system process
679 * ClientId (OUT) = Caller supplied storage for the returned client id
680 * of the thread (optional)
681 * StartRoutine = Entry point for the thread
682 * StartContext = Argument supplied to the thread when it begins
684 * RETURNS: Success or failure status
690 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
691 ThreadHandle
,ProcessHandle
);
693 Status
= PsInitializeThread(ProcessHandle
,
699 if (!NT_SUCCESS(Status
))
704 Thread
->StartAddress
= StartRoutine
;
705 Status
= KiArchInitThread(&Thread
->Tcb
, StartRoutine
, StartContext
);
706 if (!NT_SUCCESS(Status
))
711 if (ClientId
!= NULL
)
713 *ClientId
=Thread
->Cid
;
716 PsUnblockThread(Thread
, NULL
);
718 return(STATUS_SUCCESS
);
726 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
)
730 KeAcquireSpinLock(&PiThreadListLock
, &oldIrql
);
731 if (PiThreadNotifyRoutineCount
>= MAX_THREAD_NOTIFY_ROUTINE_COUNT
)
733 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
734 return(STATUS_INSUFFICIENT_RESOURCES
);
737 PiThreadNotifyRoutine
[PiThreadNotifyRoutineCount
] = NotifyRoutine
;
738 PiThreadNotifyRoutineCount
++;
739 KeReleaseSpinLock(&PiThreadListLock
, oldIrql
);
741 return(STATUS_SUCCESS
);