f46d5647b29c0c660fae972b0c0582bfcb259810
[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 #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    RtlZeroMemory(&Teb, sizeof(TEB));
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 UserStack */
460    if(UserStack != NULL)
461    {
462     /* fixed-size stack */
463     if(UserStack->FixedStackBase && UserStack->FixedStackLimit)
464     {
465      Teb.Tib.StackBase = UserStack->FixedStackBase;
466      Teb.Tib.StackLimit = UserStack->FixedStackLimit;
467      Teb.DeallocationStack = UserStack->FixedStackLimit;
468     }
469     /* expandable stack */
470     else
471     {
472      Teb.Tib.StackBase = UserStack->ExpandableStackBase;
473      Teb.Tib.StackLimit = UserStack->ExpandableStackLimit;
474      Teb.DeallocationStack = UserStack->ExpandableStackBottom;
475     }
476    }
477
478    /* more initialization */
479    Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
480    Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
481    Teb.CurrentLocale = PsDefaultThreadLocaleId;
482
483    /* Terminate the exception handler list */
484    Teb.Tib.ExceptionList = (PVOID)-1;
485    
486    DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
487    
488    /* write TEB data into teb page */
489    Status = NtWriteVirtualMemory(ProcessHandle,
490                                  TebBase,
491                                  &Teb,
492                                  sizeof(TEB),
493                                  &ByteCount);
494
495    if (!NT_SUCCESS(Status))
496      {
497         /* free TEB */
498         DPRINT1 ("Writing TEB failed!\n");
499
500         RegionSize = 0;
501         NtFreeVirtualMemory(ProcessHandle,
502                             TebBase,
503                             &RegionSize,
504                             MEM_RELEASE);
505
506         return Status;
507      }
508
509    if (TebPtr != NULL)
510      {
511         *TebPtr = (PTEB)TebBase;
512      }
513
514    DPRINT("TEB allocated at %p\n", TebBase);
515
516    return Status;
517 }
518
519 VOID STDCALL
520 LdrInitApcRundownRoutine(PKAPC Apc)
521 {
522    ExFreePool(Apc);
523 }
524
525 VOID STDCALL
526 LdrInitApcKernelRoutine(PKAPC Apc,
527                         PKNORMAL_ROUTINE* NormalRoutine,
528                         PVOID* NormalContext,
529                         PVOID* SystemArgument1,
530                         PVOID* SystemArgument2)
531 {
532   ExFreePool(Apc);
533 }
534
535 NTSTATUS STDCALL
536 NtCreateThread(PHANDLE ThreadHandle,
537                ACCESS_MASK DesiredAccess,
538                POBJECT_ATTRIBUTES ObjectAttributes,
539                HANDLE ProcessHandle,
540                PCLIENT_ID Client,
541                PCONTEXT ThreadContext,
542                PUSER_STACK UserStack,
543                BOOLEAN CreateSuspended)
544 {
545   PETHREAD Thread;
546   PTEB TebBase;
547   NTSTATUS Status;
548   PKAPC LdrInitApc;
549
550   DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
551          ThreadHandle,ThreadContext);
552
553   Status = PsInitializeThread(ProcessHandle,
554                               &Thread,
555                               ThreadHandle,
556                               DesiredAccess,
557                               ObjectAttributes,
558                               FALSE);
559   if (!NT_SUCCESS(Status))
560     {
561       return(Status);
562     }
563
564   Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
565   if (!NT_SUCCESS(Status))
566     {
567       return(Status);
568     }
569
570   Status = PsCreateTeb(ProcessHandle,
571                        &TebBase,
572                        Thread,
573                        UserStack);
574   if (!NT_SUCCESS(Status))
575     {
576       return(Status);
577     }
578   Thread->Tcb.Teb = TebBase;
579
580   Thread->StartAddress = NULL;
581
582   if (Client != NULL)
583     {
584       *Client = Thread->Cid;
585     }
586
587   /*
588    * Maybe send a message to the process's debugger
589    */
590   DbgkCreateThread((PVOID)ThreadContext->Eip);
591
592   /*
593    * First, force the thread to be non-alertable for user-mode alerts.
594    */
595   Thread->Tcb.Alertable = FALSE;
596
597   /*
598    * If the thread is to be created suspended then queue an APC to
599    * do the suspend before we run any userspace code.
600    */
601   if (CreateSuspended)
602     {
603       PsSuspendThread(Thread, NULL);
604     }
605
606   /*
607    * Queue an APC to the thread that will execute the ntdll startup
608    * routine.
609    */
610   LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
611   KeInitializeApc(LdrInitApc, &Thread->Tcb, 0, LdrInitApcKernelRoutine,
612                   LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(), 
613                   UserMode, NULL);
614   KeInsertQueueApc(LdrInitApc, NULL, NULL, UserMode);
615
616   /*
617    * Start the thread running and force it to execute the APC(s) we just
618    * queued before it runs anything else in user-mode.
619    */
620   Thread->Tcb.Alertable = TRUE;
621   Thread->Tcb.Alerted[0] = 1;
622   PsUnblockThread(Thread, NULL);
623
624   return(STATUS_SUCCESS);
625 }
626
627
628 NTSTATUS STDCALL
629 PsCreateSystemThread(PHANDLE ThreadHandle,
630                      ACCESS_MASK DesiredAccess,
631                      POBJECT_ATTRIBUTES ObjectAttributes,
632                      HANDLE ProcessHandle,
633                      PCLIENT_ID ClientId,
634                      PKSTART_ROUTINE StartRoutine,
635                      PVOID StartContext)
636 /*
637  * FUNCTION: Creates a thread which executes in kernel mode
638  * ARGUMENTS:
639  *       ThreadHandle (OUT) = Caller supplied storage for the returned thread 
640  *                            handle
641  *       DesiredAccess = Requested access to the thread
642  *       ObjectAttributes = Object attributes (optional)
643  *       ProcessHandle = Handle of process thread will run in
644  *                       NULL to use system process
645  *       ClientId (OUT) = Caller supplied storage for the returned client id
646  *                        of the thread (optional)
647  *       StartRoutine = Entry point for the thread
648  *       StartContext = Argument supplied to the thread when it begins
649  *                     execution
650  * RETURNS: Success or failure status
651  */
652 {
653    PETHREAD Thread;
654    NTSTATUS Status;
655    
656    DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
657             ThreadHandle,ProcessHandle);
658    
659    Status = PsInitializeThread(ProcessHandle,
660                                &Thread,
661                                ThreadHandle,
662                                DesiredAccess,
663                                ObjectAttributes,
664                                FALSE);
665    if (!NT_SUCCESS(Status))
666      {
667         return(Status);
668      }
669    
670    Thread->StartAddress = StartRoutine;
671    Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
672    if (!NT_SUCCESS(Status))
673      {
674         return(Status);
675      }
676
677    if (ClientId != NULL)
678      {
679         *ClientId=Thread->Cid;
680      }
681
682    PsUnblockThread(Thread, NULL);
683    
684    return(STATUS_SUCCESS);
685 }
686
687
688 NTSTATUS STDCALL
689 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
690 {
691   if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
692     return(STATUS_INSUFFICIENT_RESOURCES);
693
694   PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
695   PiThreadNotifyRoutineCount++;
696
697   return(STATUS_SUCCESS);
698 }
699
700 /* EOF */