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