My biggest commit so far (everything compiles and apparently runs fine):
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.57 2003/04/26 23:13:33 hyperion 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 KeLowerIrql(PASSIVE_LEVEL);
273 }
274
275 VOID STDCALL
276 PiDeleteThread(PVOID ObjectBody)
277 {
278 KIRQL oldIrql;
279 PETHREAD Thread;
280 ULONG i;
281 Thread = (PETHREAD)ObjectBody;
282
283 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
284
285 ObDereferenceObject(Thread->ThreadsProcess);
286 Thread->ThreadsProcess = NULL;
287
288 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
289
290 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
291 {
292 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
293 Thread->Cid.UniqueThread,
294 FALSE);
295 }
296 PiNrThreads--;
297 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
298 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
299 KeReleaseThread(Thread);
300 DPRINT("PiDeleteThread() finished\n");
301 }
302
303 NTSTATUS
304 PsInitializeThread(HANDLE ProcessHandle,
305 PETHREAD* ThreadPtr,
306 PHANDLE ThreadHandle,
307 ACCESS_MASK DesiredAccess,
308 POBJECT_ATTRIBUTES ThreadAttributes,
309 BOOLEAN First)
310 {
311 PETHREAD Thread;
312 NTSTATUS Status;
313 KIRQL oldIrql;
314 PEPROCESS Process;
315 ULONG i;
316
317 /*
318 * Reference process
319 */
320 if (ProcessHandle != NULL)
321 {
322 Status = ObReferenceObjectByHandle(ProcessHandle,
323 PROCESS_CREATE_THREAD,
324 PsProcessType,
325 UserMode,
326 (PVOID*)&Process,
327 NULL);
328 if (Status != STATUS_SUCCESS)
329 {
330 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
331 return(Status);
332 }
333 DPRINT( "Creating thread in process %x\n", Process );
334 }
335 else
336 {
337 Process = PsInitialSystemProcess;
338 ObReferenceObjectByPointer(Process,
339 PROCESS_CREATE_THREAD,
340 PsProcessType,
341 UserMode);
342 }
343
344 /*
345 * Create and initialize thread
346 */
347 Status = ObCreateObject(ThreadHandle,
348 DesiredAccess,
349 ThreadAttributes,
350 PsThreadType,
351 (PVOID*)&Thread);
352 if (!NT_SUCCESS(Status))
353 {
354 return(Status);
355 }
356
357 DPRINT("Thread = %x\n",Thread);
358
359 PiNrThreads++;
360
361 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
362 Thread->ThreadsProcess = Process;
363 /*
364 * FIXME: What lock protects this?
365 */
366 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
367 &Thread->Tcb.ProcessThreadListEntry);
368 InitializeListHead(&Thread->TerminationPortList);
369 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
370 InitializeListHead(&Thread->IrpList);
371 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
372 (LONG *)&PiNextThreadUniqueId);
373 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
374 Thread->DeadThread = 0;
375 Thread->Win32Thread = 0;
376 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
377
378 *ThreadPtr = Thread;
379
380 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
381 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
382 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
383
384 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
385 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
386
387 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
388 {
389 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
390 Thread->Cid.UniqueThread,
391 TRUE);
392 }
393
394 return(STATUS_SUCCESS);
395 }
396
397
398 static NTSTATUS
399 PsCreateTeb(HANDLE ProcessHandle,
400 PTEB *TebPtr,
401 PETHREAD Thread,
402 PUSER_STACK UserStack)
403 {
404 MEMORY_BASIC_INFORMATION Info;
405 NTSTATUS Status;
406 ULONG ByteCount;
407 ULONG RegionSize;
408 ULONG TebSize;
409 PVOID TebBase;
410 TEB Teb;
411 ULONG ResultLength;
412
413 TebBase = (PVOID)0x7FFDE000;
414 TebSize = PAGE_SIZE;
415
416 while (TRUE)
417 {
418 Status = NtQueryVirtualMemory(ProcessHandle,
419 TebBase,
420 MemoryBasicInformation,
421 &Info,
422 sizeof(Info),
423 &ResultLength);
424 if (!NT_SUCCESS(Status))
425 {
426 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
427 KeBugCheck(0);
428 }
429 /* FIXME: Race between this and the above check */
430 if (Info.State == MEM_FREE)
431 {
432 /* The TEB must reside in user space */
433 Status = NtAllocateVirtualMemory(ProcessHandle,
434 &TebBase,
435 0,
436 &TebSize,
437 MEM_RESERVE | MEM_COMMIT,
438 PAGE_READWRITE);
439 if (NT_SUCCESS(Status))
440 {
441 break;
442 }
443 }
444
445 TebBase = TebBase - TebSize;
446 }
447
448 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
449
450 /* set all pointers to and from the TEB */
451 Teb.Tib.Self = TebBase;
452 if (Thread->ThreadsProcess)
453 {
454 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
455 }
456 DPRINT("Teb.Peb %x\n", Teb.Peb);
457
458 /* store stack information from UserStack */
459 if(UserStack != NULL)
460 {
461 /* fixed-size stack */
462 if(UserStack->FixedStackBase && UserStack->FixedStackLimit)
463 {
464 Teb.Tib.StackBase = UserStack->FixedStackBase;
465 Teb.Tib.StackLimit = UserStack->FixedStackLimit;
466 Teb.DeallocationStack = UserStack->FixedStackLimit;
467 }
468 /* expandable stack */
469 else
470 {
471 Teb.Tib.StackBase = UserStack->ExpandableStackBase;
472 Teb.Tib.StackLimit = UserStack->ExpandableStackLimit;
473 Teb.DeallocationStack = UserStack->ExpandableStackBottom;
474 }
475 }
476
477 /* more initialization */
478 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
479 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
480 Teb.CurrentLocale = PsDefaultThreadLocaleId;
481
482 /* Terminate the exception handler list */
483 Teb.Tib.ExceptionList = (PVOID)-1;
484
485 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
486
487 /* write TEB data into teb page */
488 Status = NtWriteVirtualMemory(ProcessHandle,
489 TebBase,
490 &Teb,
491 sizeof(TEB),
492 &ByteCount);
493
494 if (!NT_SUCCESS(Status))
495 {
496 /* free TEB */
497 DPRINT1 ("Writing TEB failed!\n");
498
499 RegionSize = 0;
500 NtFreeVirtualMemory(ProcessHandle,
501 TebBase,
502 &RegionSize,
503 MEM_RELEASE);
504
505 return Status;
506 }
507
508 if (TebPtr != NULL)
509 {
510 *TebPtr = (PTEB)TebBase;
511 }
512
513 DPRINT("TEB allocated at %p\n", TebBase);
514
515 return Status;
516 }
517
518 VOID STDCALL
519 LdrInitApcRundownRoutine(PKAPC Apc)
520 {
521 ExFreePool(Apc);
522 }
523
524 VOID STDCALL
525 LdrInitApcKernelRoutine(PKAPC Apc,
526 PKNORMAL_ROUTINE* NormalRoutine,
527 PVOID* NormalContext,
528 PVOID* SystemArgument1,
529 PVOID* SystemArgument2)
530 {
531 ExFreePool(Apc);
532 }
533
534 NTSTATUS STDCALL
535 NtCreateThread(PHANDLE ThreadHandle,
536 ACCESS_MASK DesiredAccess,
537 POBJECT_ATTRIBUTES ObjectAttributes,
538 HANDLE ProcessHandle,
539 PCLIENT_ID Client,
540 PCONTEXT ThreadContext,
541 PUSER_STACK UserStack,
542 BOOLEAN CreateSuspended)
543 {
544 PETHREAD Thread;
545 PTEB TebBase;
546 NTSTATUS Status;
547 PKAPC LdrInitApc;
548
549 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
550 ThreadHandle,ThreadContext);
551
552 Status = PsInitializeThread(ProcessHandle,
553 &Thread,
554 ThreadHandle,
555 DesiredAccess,
556 ObjectAttributes,
557 FALSE);
558 if (!NT_SUCCESS(Status))
559 {
560 return(Status);
561 }
562
563 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
564 if (!NT_SUCCESS(Status))
565 {
566 return(Status);
567 }
568
569 Status = PsCreateTeb(ProcessHandle,
570 &TebBase,
571 Thread,
572 UserStack);
573 if (!NT_SUCCESS(Status))
574 {
575 return(Status);
576 }
577 Thread->Tcb.Teb = TebBase;
578
579 Thread->StartAddress = NULL;
580
581 if (Client != NULL)
582 {
583 *Client = Thread->Cid;
584 }
585
586 /*
587 * Maybe send a message to the process's debugger
588 */
589 DbgkCreateThread((PVOID)ThreadContext->Eip);
590
591 /*
592 * First, force the thread to be non-alertable for user-mode alerts.
593 */
594 Thread->Tcb.Alertable = FALSE;
595
596 /*
597 * If the thread is to be created suspended then queue an APC to
598 * do the suspend before we run any userspace code.
599 */
600 if (CreateSuspended)
601 {
602 PsSuspendThread(Thread, NULL);
603 }
604
605 /*
606 * Queue an APC to the thread that will execute the ntdll startup
607 * routine.
608 */
609 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
610 KeInitializeApc(LdrInitApc, &Thread->Tcb, 0, LdrInitApcKernelRoutine,
611 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
612 UserMode, NULL);
613 KeInsertQueueApc(LdrInitApc, NULL, NULL, UserMode);
614
615 /*
616 * Start the thread running and force it to execute the APC(s) we just
617 * queued before it runs anything else in user-mode.
618 */
619 Thread->Tcb.Alertable = TRUE;
620 Thread->Tcb.Alerted[0] = 1;
621 PsUnblockThread(Thread, NULL);
622
623 return(STATUS_SUCCESS);
624 }
625
626
627 NTSTATUS STDCALL
628 PsCreateSystemThread(PHANDLE ThreadHandle,
629 ACCESS_MASK DesiredAccess,
630 POBJECT_ATTRIBUTES ObjectAttributes,
631 HANDLE ProcessHandle,
632 PCLIENT_ID ClientId,
633 PKSTART_ROUTINE StartRoutine,
634 PVOID StartContext)
635 /*
636 * FUNCTION: Creates a thread which executes in kernel mode
637 * ARGUMENTS:
638 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
639 * handle
640 * DesiredAccess = Requested access to the thread
641 * ObjectAttributes = Object attributes (optional)
642 * ProcessHandle = Handle of process thread will run in
643 * NULL to use system process
644 * ClientId (OUT) = Caller supplied storage for the returned client id
645 * of the thread (optional)
646 * StartRoutine = Entry point for the thread
647 * StartContext = Argument supplied to the thread when it begins
648 * execution
649 * RETURNS: Success or failure status
650 */
651 {
652 PETHREAD Thread;
653 NTSTATUS Status;
654
655 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
656 ThreadHandle,ProcessHandle);
657
658 Status = PsInitializeThread(ProcessHandle,
659 &Thread,
660 ThreadHandle,
661 DesiredAccess,
662 ObjectAttributes,
663 FALSE);
664 if (!NT_SUCCESS(Status))
665 {
666 return(Status);
667 }
668
669 Thread->StartAddress = StartRoutine;
670 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
671 if (!NT_SUCCESS(Status))
672 {
673 return(Status);
674 }
675
676 if (ClientId != NULL)
677 {
678 *ClientId=Thread->Cid;
679 }
680
681 PsUnblockThread(Thread, NULL);
682
683 return(STATUS_SUCCESS);
684 }
685
686
687 NTSTATUS STDCALL
688 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
689 {
690 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
691 return(STATUS_INSUFFICIENT_RESOURCES);
692
693 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
694 PiThreadNotifyRoutineCount++;
695
696 return(STATUS_SUCCESS);
697 }
698
699 /* EOF */