+ExAcquireResourceExclusiveLite()
[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 PACCESS_TOKEN STDCALL 
253 PsReferenceImpersonationToken(PETHREAD Thread,
254                               PULONG Unknown1,
255                               PULONG Unknown2,
256                               SECURITY_IMPERSONATION_LEVEL* Level)
257 {
258    if (Thread->ActiveImpersonationInfo == 0)
259      {
260         return(NULL);
261      }
262    
263    *Level = Thread->ImpersonationInfo->Level;
264    *Unknown1 = Thread->ImpersonationInfo->Unknown1;
265    *Unknown2 = Thread->ImpersonationInfo->Unknown2;
266    ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
267                               TOKEN_ALL_ACCESS,
268                               SepTokenObjectType,
269                               KernelMode);
270    return(Thread->ImpersonationInfo->Token);
271 }
272
273 VOID
274 PiBeforeBeginThread(CONTEXT c)
275 {
276    DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
277    KeLowerIrql(PASSIVE_LEVEL);
278 }
279
280 VOID STDCALL
281 PiDeleteThread(PVOID ObjectBody)
282 {
283   KIRQL oldIrql;
284   PETHREAD Thread;
285   ULONG i;
286   Thread = (PETHREAD)ObjectBody;
287
288   DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
289
290   ObDereferenceObject(Thread->ThreadsProcess);
291   Thread->ThreadsProcess = NULL;
292
293   KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
294   
295   for (i = 0; i < PiThreadNotifyRoutineCount; i++)
296     {
297       PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
298                                Thread->Cid.UniqueThread,
299                                FALSE);
300     }    
301   PiNrThreads--;
302   RemoveEntryList(&Thread->Tcb.ThreadListEntry);
303   KeReleaseSpinLock(&PiThreadListLock, oldIrql);
304   KeReleaseThread(Thread);
305   DPRINT("PiDeleteThread() finished\n");
306 }
307
308 #endif /* LIBCAPTIVE */
309
310 NTSTATUS
311 PsInitializeThread(HANDLE ProcessHandle,
312                    PETHREAD* ThreadPtr,
313                    PHANDLE ThreadHandle,
314                    ACCESS_MASK  DesiredAccess,
315                    POBJECT_ATTRIBUTES ThreadAttributes,
316                    BOOLEAN First)
317 {
318    PETHREAD Thread;
319    NTSTATUS Status;
320    KIRQL oldIrql;
321    PEPROCESS Process;
322 #ifndef LIBCAPTIVE
323    ULONG i;
324 #endif /* LIBCAPTIVE */
325    
326    /*
327     * Reference process
328     */
329    if (ProcessHandle != NULL)
330      {
331         Status = ObReferenceObjectByHandle(ProcessHandle,
332                                            PROCESS_CREATE_THREAD,
333                                            PsProcessType,
334                                            UserMode,
335                                            (PVOID*)&Process,
336                                            NULL);
337         if (Status != STATUS_SUCCESS)
338           {
339              DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
340              return(Status);
341           }
342         DPRINT( "Creating thread in process %x\n", Process );
343      }
344    else
345      {
346         Process = PsInitialSystemProcess;
347         ObReferenceObjectByPointer(Process,
348                                    PROCESS_CREATE_THREAD,
349                                    PsProcessType,
350                                    UserMode);
351      }
352    
353    /*
354     * Create and initialize thread
355     */
356    Status = ObCreateObject(ThreadHandle,
357                            DesiredAccess,
358                            ThreadAttributes,
359                            PsThreadType,
360                            (PVOID*)&Thread);
361    if (!NT_SUCCESS(Status))
362      {
363         return(Status);
364      }
365
366    DPRINT("Thread = %x\n",Thread);
367    
368    PiNrThreads++;
369    
370    KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
371    Thread->ThreadsProcess = Process;
372    /*
373     * FIXME: What lock protects this?
374     */
375    InsertTailList(&Thread->ThreadsProcess->ThreadListHead, 
376                   &Thread->Tcb.ProcessThreadListEntry);
377    InitializeListHead(&Thread->TerminationPortList);
378    KeInitializeSpinLock(&Thread->ActiveTimerListLock);
379    InitializeListHead(&Thread->IrpList);
380    Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
381                                               &PiNextThreadUniqueId);
382    Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
383    Thread->DeadThread = 0;
384    Thread->Win32Thread = 0;
385    DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
386    
387    *ThreadPtr = Thread;
388    
389    KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
390    InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
391    KeReleaseSpinLock(&PiThreadListLock, oldIrql);
392
393    Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
394    Thread->Tcb.Priority = Thread->Tcb.BasePriority;
395
396 #ifndef LIBCAPTIVE
397   for (i = 0; i < PiThreadNotifyRoutineCount; i++)
398     {
399       PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
400                                Thread->Cid.UniqueThread,
401                                TRUE);
402     }
403 #endif /* LIBCAPTIVE */
404
405   return(STATUS_SUCCESS);
406 }
407
408 #ifndef LIBCAPTIVE
409
410 static NTSTATUS
411 PsCreateTeb(HANDLE ProcessHandle,
412             PTEB *TebPtr,
413             PETHREAD Thread,
414             PINITIAL_TEB InitialTeb)
415 {
416    MEMORY_BASIC_INFORMATION Info;
417    NTSTATUS Status;
418    ULONG ByteCount;
419    ULONG RegionSize;
420    ULONG TebSize;
421    PVOID TebBase;
422    TEB Teb;
423    ULONG ResultLength;
424
425    TebBase = (PVOID)0x7FFDE000;
426    TebSize = PAGE_SIZE;
427
428    while (TRUE)
429      {
430         Status = NtQueryVirtualMemory(ProcessHandle,
431                                       TebBase,
432                                       MemoryBasicInformation,
433                                       &Info,
434                                       sizeof(Info),
435                                       &ResultLength);
436         if (!NT_SUCCESS(Status))
437           {
438              CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
439              KeBugCheck(0);
440           }
441         /* FIXME: Race between this and the above check */
442         if (Info.State == MEM_FREE)
443           {
444              /* The TEB must reside in user space */
445              Status = NtAllocateVirtualMemory(ProcessHandle,
446                                               &TebBase,
447                                               0,
448                                               &TebSize,
449                                               MEM_RESERVE | MEM_COMMIT,
450                                               PAGE_READWRITE);
451              if (NT_SUCCESS(Status))
452                {
453                   break;
454                }
455           }
456              
457         TebBase = TebBase - TebSize;
458      }
459
460    DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
461
462    /* set all pointers to and from the TEB */
463    Teb.Tib.Self = TebBase;
464    if (Thread->ThreadsProcess)
465      {
466         Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
467      }
468    DPRINT("Teb.Peb %x\n", Teb.Peb);
469    
470    /* store stack information from InitialTeb */
471    if (InitialTeb != NULL)
472      {
473         Teb.Tib.StackBase = InitialTeb->StackBase;
474         Teb.Tib.StackLimit = InitialTeb->StackLimit;
475         Teb.DeallocationStack = InitialTeb->StackAllocate;
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                PINITIAL_TEB InitialTeb,
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                        InitialTeb);
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 #endif /* LIBCAPTIVE */
701
702 /* EOF */