2002-10-01 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.55 2002/10/01 19:27:25 chorns Exp $
2 *
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)
8 * REVISION HISTORY:
9 * 23/06/98: Created
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
11 */
12
13 /*
14 * NOTE:
15 *
16 * All of the routines that manipulate the thread queue synchronize on
17 * a single spinlock
18 *
19 */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/se.h>
28 #include <internal/id.h>
29 #include <internal/dbg.h>
30 #include <internal/ldr.h>
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* GLOBAL *******************************************************************/
36
37 static ULONG PiNextThreadUniqueId = 0;
38
39 extern KSPIN_LOCK PiThreadListLock;
40 extern ULONG PiNrThreads;
41
42 extern LIST_ENTRY PiThreadListHead;
43
44 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
45
46 static ULONG PiThreadNotifyRoutineCount = 0;
47 static PCREATE_THREAD_NOTIFY_ROUTINE
48 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
49
50 /* FUNCTIONS ***************************************************************/
51
52 NTSTATUS STDCALL
53 PsAssignImpersonationToken(PETHREAD Thread,
54 HANDLE TokenHandle)
55 {
56 PACCESS_TOKEN Token;
57 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
58 NTSTATUS Status;
59
60 if (TokenHandle != NULL)
61 {
62 Status = ObReferenceObjectByHandle(TokenHandle,
63 0,
64 SepTokenObjectType,
65 UserMode,
66 (PVOID*)&Token,
67 NULL);
68 if (!NT_SUCCESS(Status))
69 {
70 return(Status);
71 }
72 ImpersonationLevel = Token->ImpersonationLevel;
73 }
74 else
75 {
76 Token = NULL;
77 ImpersonationLevel = 0;
78 }
79
80 PsImpersonateClient(Thread,
81 Token,
82 0,
83 0,
84 ImpersonationLevel);
85 if (Token != NULL)
86 {
87 ObDereferenceObject(Token);
88 }
89 return(STATUS_SUCCESS);
90 }
91
92 VOID STDCALL
93 PsRevertToSelf(VOID)
94 {
95 PETHREAD Thread;
96
97 Thread = PsGetCurrentThread();
98
99 if (Thread->ActiveImpersonationInfo != 0)
100 {
101 ObDereferenceObject(Thread->ImpersonationInfo->Token);
102 Thread->ActiveImpersonationInfo = 0;
103 }
104 }
105
106 VOID STDCALL
107 PsImpersonateClient(PETHREAD Thread,
108 PACCESS_TOKEN Token,
109 UCHAR b,
110 UCHAR c,
111 SECURITY_IMPERSONATION_LEVEL Level)
112 {
113 if (Token == 0)
114 {
115 if (Thread->ActiveImpersonationInfo != 0)
116 {
117 Thread->ActiveImpersonationInfo = 0;
118 if (Thread->ImpersonationInfo->Token != NULL)
119 {
120 ObDereferenceObject(Thread->ImpersonationInfo->Token);
121 }
122 }
123 return;
124 }
125 if (Thread->ActiveImpersonationInfo == 0 ||
126 Thread->ImpersonationInfo == NULL)
127 {
128 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
129 sizeof(PS_IMPERSONATION_INFO));
130 }
131 Thread->ImpersonationInfo->Level = Level;
132 Thread->ImpersonationInfo->Unknown2 = c;
133 Thread->ImpersonationInfo->Unknown1 = b;
134 Thread->ImpersonationInfo->Token = Token;
135 ObReferenceObjectByPointer(Token,
136 0,
137 SepTokenObjectType,
138 KernelMode);
139 Thread->ActiveImpersonationInfo = 1;
140 }
141
142 PACCESS_TOKEN
143 PsReferenceEffectiveToken(PETHREAD Thread,
144 PTOKEN_TYPE TokenType,
145 PUCHAR b,
146 PSECURITY_IMPERSONATION_LEVEL Level)
147 {
148 PEPROCESS Process;
149 PACCESS_TOKEN Token;
150
151 if (Thread->ActiveImpersonationInfo == 0)
152 {
153 Process = Thread->ThreadsProcess;
154 *TokenType = TokenPrimary;
155 *b = 0;
156 Token = Process->Token;
157 }
158 else
159 {
160 Token = Thread->ImpersonationInfo->Token;
161 *TokenType = TokenImpersonation;
162 *b = Thread->ImpersonationInfo->Unknown2;
163 *Level = Thread->ImpersonationInfo->Level;
164 }
165 return(Token);
166 }
167
168 NTSTATUS STDCALL
169 NtImpersonateThread(IN HANDLE ThreadHandle,
170 IN HANDLE ThreadToImpersonateHandle,
171 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
172 {
173 PETHREAD Thread;
174 PETHREAD ThreadToImpersonate;
175 NTSTATUS Status;
176 SECURITY_CLIENT_CONTEXT ClientContext;
177
178 Status = ObReferenceObjectByHandle(ThreadHandle,
179 0,
180 PsThreadType,
181 UserMode,
182 (PVOID*)&Thread,
183 NULL);
184 if (!NT_SUCCESS(Status))
185 {
186 return(Status);
187 }
188
189 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
190 0,
191 PsThreadType,
192 UserMode,
193 (PVOID*)&ThreadToImpersonate,
194 NULL);
195 if (!NT_SUCCESS(Status))
196 {
197 ObDereferenceObject(Thread);
198 return(Status);
199 }
200
201 Status = SeCreateClientSecurity(ThreadToImpersonate,
202 SecurityQualityOfService,
203 0,
204 &ClientContext);
205 if (!NT_SUCCESS(Status))
206 {
207 ObDereferenceObject(Thread);
208 ObDereferenceObject(ThreadToImpersonate);
209 return(Status);
210 }
211
212 SeImpersonateClient(&ClientContext, Thread);
213 if (ClientContext.Token != NULL)
214 {
215 ObDereferenceObject(ClientContext.Token);
216 }
217 return(STATUS_SUCCESS);
218 }
219
220 NTSTATUS STDCALL
221 NtOpenThreadToken(IN HANDLE ThreadHandle,
222 IN ACCESS_MASK DesiredAccess,
223 IN BOOLEAN OpenAsSelf,
224 OUT PHANDLE TokenHandle)
225 {
226 #if 0
227 PETHREAD Thread;
228 NTSTATUS Status;
229 PACCESS_TOKEN Token;
230
231 Status = ObReferenceObjectByHandle(ThreadHandle,
232 0,
233 PsThreadType,
234 UserMode,
235 (PVOID*)&Thread,
236 NULL);
237 if (!NT_SUCCESS(Status))
238 {
239 return(Status);
240 }
241
242 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
243 SepCreateImpersonationTokenDacl(Token);
244 #endif
245 return(STATUS_UNSUCCESSFUL);
246 }
247
248 PACCESS_TOKEN STDCALL
249 PsReferenceImpersonationToken(PETHREAD Thread,
250 PULONG Unknown1,
251 PULONG Unknown2,
252 SECURITY_IMPERSONATION_LEVEL* Level)
253 {
254 if (Thread->ActiveImpersonationInfo == 0)
255 {
256 return(NULL);
257 }
258
259 *Level = Thread->ImpersonationInfo->Level;
260 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
261 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
262 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
263 TOKEN_ALL_ACCESS,
264 SepTokenObjectType,
265 KernelMode);
266 return(Thread->ImpersonationInfo->Token);
267 }
268
269 VOID
270 PiBeforeBeginThread(CONTEXT c)
271 {
272 DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
273 KeLowerIrql(PASSIVE_LEVEL);
274 }
275
276 VOID STDCALL
277 PiDeleteThread(PVOID ObjectBody)
278 {
279 KIRQL oldIrql;
280 PETHREAD Thread;
281 ULONG i;
282 Thread = (PETHREAD)ObjectBody;
283
284 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
285
286 ObDereferenceObject(Thread->ThreadsProcess);
287 Thread->ThreadsProcess = NULL;
288
289 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
290
291 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
292 {
293 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
294 Thread->Cid.UniqueThread,
295 FALSE);
296 }
297 PiNrThreads--;
298 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
299 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
300 KeReleaseThread(Thread);
301 DPRINT("PiDeleteThread() finished\n");
302 }
303
304 NTSTATUS
305 PsInitializeThread(HANDLE ProcessHandle,
306 PETHREAD* ThreadPtr,
307 PHANDLE ThreadHandle,
308 ACCESS_MASK DesiredAccess,
309 POBJECT_ATTRIBUTES ThreadAttributes,
310 BOOLEAN First)
311 {
312 PETHREAD Thread;
313 NTSTATUS Status;
314 KIRQL oldIrql;
315 PEPROCESS Process;
316 ULONG i;
317
318 /*
319 * Reference process
320 */
321 if (ProcessHandle != NULL)
322 {
323 Status = ObReferenceObjectByHandle(ProcessHandle,
324 PROCESS_CREATE_THREAD,
325 PsProcessType,
326 UserMode,
327 (PVOID*)&Process,
328 NULL);
329 if (Status != STATUS_SUCCESS)
330 {
331 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
332 return(Status);
333 }
334 DPRINT( "Creating thread in process %x\n", Process );
335 }
336 else
337 {
338 Process = PsInitialSystemProcess;
339 ObReferenceObjectByPointer(Process,
340 PROCESS_CREATE_THREAD,
341 PsProcessType,
342 UserMode);
343 }
344
345 /*
346 * Create and initialize thread
347 */
348 Status = ObCreateObject(ThreadHandle,
349 DesiredAccess,
350 ThreadAttributes,
351 PsThreadType,
352 (PVOID*)&Thread);
353 if (!NT_SUCCESS(Status))
354 {
355 return(Status);
356 }
357
358 DPRINT("Thread = %x\n",Thread);
359
360 PiNrThreads++;
361
362 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
363 Thread->ThreadsProcess = Process;
364 /*
365 * FIXME: What lock protects this?
366 */
367 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
368 &Thread->Tcb.ProcessThreadListEntry);
369 InitializeListHead(&Thread->TerminationPortList);
370 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
371 InitializeListHead(&Thread->IrpList);
372 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
373 &PiNextThreadUniqueId);
374 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
375 Thread->DeadThread = 0;
376 Thread->Win32Thread = 0;
377 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
378
379 *ThreadPtr = Thread;
380
381 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
382 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
383 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
384
385 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
386 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
387
388 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
389 {
390 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
391 Thread->Cid.UniqueThread,
392 TRUE);
393 }
394
395 return(STATUS_SUCCESS);
396 }
397
398
399 static NTSTATUS
400 PsCreateTeb(HANDLE ProcessHandle,
401 PTEB *TebPtr,
402 PETHREAD Thread,
403 PINITIAL_TEB InitialTeb)
404 {
405 MEMORY_BASIC_INFORMATION Info;
406 NTSTATUS Status;
407 ULONG ByteCount;
408 ULONG RegionSize;
409 ULONG TebSize;
410 PVOID TebBase;
411 TEB Teb;
412 ULONG ResultLength;
413
414 TebBase = (PVOID)0x7FFDE000;
415 TebSize = PAGE_SIZE;
416
417 while (TRUE)
418 {
419 Status = NtQueryVirtualMemory(ProcessHandle,
420 TebBase,
421 MemoryBasicInformation,
422 &Info,
423 sizeof(Info),
424 &ResultLength);
425 if (!NT_SUCCESS(Status))
426 {
427 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
428 KeBugCheck(0);
429 }
430 /* FIXME: Race between this and the above check */
431 if (Info.State == MEM_FREE)
432 {
433 /* The TEB must reside in user space */
434 Status = NtAllocateVirtualMemory(ProcessHandle,
435 &TebBase,
436 0,
437 &TebSize,
438 MEM_RESERVE | MEM_COMMIT,
439 PAGE_READWRITE);
440 if (NT_SUCCESS(Status))
441 {
442 break;
443 }
444 }
445
446 TebBase = TebBase - TebSize;
447 }
448
449 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
450
451 /* set all pointers to and from the TEB */
452 Teb.Tib.Self = TebBase;
453 if (Thread->ThreadsProcess)
454 {
455 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
456 }
457 DPRINT("Teb.Peb %x\n", Teb.Peb);
458
459 /* store stack information from InitialTeb */
460 if (InitialTeb != NULL)
461 {
462 Teb.Tib.StackBase = InitialTeb->StackBase;
463 Teb.Tib.StackLimit = InitialTeb->StackLimit;
464 Teb.DeallocationStack = InitialTeb->StackAllocate;
465 }
466
467 /* more initialization */
468 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
469 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
470 Teb.CurrentLocale = PsDefaultThreadLocaleId;
471
472 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
473
474 /* write TEB data into teb page */
475 Status = NtWriteVirtualMemory(ProcessHandle,
476 TebBase,
477 &Teb,
478 sizeof(TEB),
479 &ByteCount);
480
481 if (!NT_SUCCESS(Status))
482 {
483 /* free TEB */
484 DPRINT1 ("Writing TEB failed!\n");
485
486 RegionSize = 0;
487 NtFreeVirtualMemory(ProcessHandle,
488 TebBase,
489 &RegionSize,
490 MEM_RELEASE);
491
492 return Status;
493 }
494
495 if (TebPtr != NULL)
496 {
497 *TebPtr = (PTEB)TebBase;
498 }
499
500 DPRINT("TEB allocated at %p\n", TebBase);
501
502 return Status;
503 }
504
505 VOID STDCALL
506 LdrInitApcRundownRoutine(PKAPC Apc)
507 {
508 ExFreePool(Apc);
509 }
510
511 VOID STDCALL
512 LdrInitApcKernelRoutine(PKAPC Apc,
513 PKNORMAL_ROUTINE* NormalRoutine,
514 PVOID* NormalContext,
515 PVOID* SystemArgument1,
516 PVOID* SystemArgument2)
517 {
518 ExFreePool(Apc);
519 }
520
521 NTSTATUS STDCALL
522 NtCreateThread(PHANDLE ThreadHandle,
523 ACCESS_MASK DesiredAccess,
524 POBJECT_ATTRIBUTES ObjectAttributes,
525 HANDLE ProcessHandle,
526 PCLIENT_ID Client,
527 PCONTEXT ThreadContext,
528 PINITIAL_TEB InitialTeb,
529 BOOLEAN CreateSuspended)
530 {
531 PETHREAD Thread;
532 PTEB TebBase;
533 NTSTATUS Status;
534 PKAPC LdrInitApc;
535
536 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
537 ThreadHandle,ThreadContext);
538
539 Status = PsInitializeThread(ProcessHandle,
540 &Thread,
541 ThreadHandle,
542 DesiredAccess,
543 ObjectAttributes,
544 FALSE);
545 if (!NT_SUCCESS(Status))
546 {
547 return(Status);
548 }
549
550 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
551 if (!NT_SUCCESS(Status))
552 {
553 return(Status);
554 }
555
556 Status = PsCreateTeb(ProcessHandle,
557 &TebBase,
558 Thread,
559 InitialTeb);
560 if (!NT_SUCCESS(Status))
561 {
562 return(Status);
563 }
564 Thread->Tcb.Teb = TebBase;
565
566 Thread->StartAddress = NULL;
567
568 if (Client != NULL)
569 {
570 *Client = Thread->Cid;
571 }
572
573 /*
574 * Maybe send a message to the process's debugger
575 */
576 DbgkCreateThread((PVOID)ThreadContext->Eip);
577
578 /*
579 * First, force the thread to be non-alertable for user-mode alerts.
580 */
581 Thread->Tcb.Alertable = FALSE;
582
583 /*
584 * If the thread is to be created suspended then queue an APC to
585 * do the suspend before we run any userspace code.
586 */
587 if (CreateSuspended)
588 {
589 PsSuspendThread(Thread, NULL);
590 }
591
592 /*
593 * Queue an APC to the thread that will execute the ntdll startup
594 * routine.
595 */
596 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
597 KeInitializeApc(LdrInitApc, &Thread->Tcb, 0, LdrInitApcKernelRoutine,
598 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
599 UserMode, NULL);
600 KeInsertQueueApc(LdrInitApc, NULL, NULL, UserMode);
601
602 /*
603 * Start the thread running and force it to execute the APC(s) we just
604 * queued before it runs anything else in user-mode.
605 */
606 Thread->Tcb.Alertable = TRUE;
607 Thread->Tcb.Alerted[0] = 1;
608 PsUnblockThread(Thread, NULL);
609
610 return(STATUS_SUCCESS);
611 }
612
613
614 NTSTATUS STDCALL
615 PsCreateSystemThread(PHANDLE ThreadHandle,
616 ACCESS_MASK DesiredAccess,
617 POBJECT_ATTRIBUTES ObjectAttributes,
618 HANDLE ProcessHandle,
619 PCLIENT_ID ClientId,
620 PKSTART_ROUTINE StartRoutine,
621 PVOID StartContext)
622 /*
623 * FUNCTION: Creates a thread which executes in kernel mode
624 * ARGUMENTS:
625 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
626 * handle
627 * DesiredAccess = Requested access to the thread
628 * ObjectAttributes = Object attributes (optional)
629 * ProcessHandle = Handle of process thread will run in
630 * NULL to use system process
631 * ClientId (OUT) = Caller supplied storage for the returned client id
632 * of the thread (optional)
633 * StartRoutine = Entry point for the thread
634 * StartContext = Argument supplied to the thread when it begins
635 * execution
636 * RETURNS: Success or failure status
637 */
638 {
639 PETHREAD Thread;
640 NTSTATUS Status;
641
642 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
643 ThreadHandle,ProcessHandle);
644
645 Status = PsInitializeThread(ProcessHandle,
646 &Thread,
647 ThreadHandle,
648 DesiredAccess,
649 ObjectAttributes,
650 FALSE);
651 if (!NT_SUCCESS(Status))
652 {
653 return(Status);
654 }
655
656 Thread->StartAddress = StartRoutine;
657 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
658 if (!NT_SUCCESS(Status))
659 {
660 return(Status);
661 }
662
663 if (ClientId != NULL)
664 {
665 *ClientId=Thread->Cid;
666 }
667
668 PsUnblockThread(Thread, NULL);
669
670 return(STATUS_SUCCESS);
671 }
672
673
674 NTSTATUS STDCALL
675 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
676 {
677 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
678 return(STATUS_INSUFFICIENT_RESOURCES);
679
680 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
681 PiThreadNotifyRoutineCount++;
682
683 return(STATUS_SUCCESS);
684 }
685
686 /* EOF */