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