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