Changed ObCreateObject() to a more nt-compatible format.
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.33 2001/06/16 14:11:15 ekohl 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/ob.h>
28 #include <internal/id.h>
29 #include <internal/dbg.h>
30
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 /* GLOBAL *******************************************************************/
35
36 static ULONG PiNextThreadUniqueId = 0;
37
38 extern KSPIN_LOCK PiThreadListLock;
39 extern ULONG PiNrThreads;
40
41 extern LIST_ENTRY PiThreadListHead;
42
43 /* FUNCTIONS ***************************************************************/
44
45 NTSTATUS STDCALL
46 PsAssignImpersonationToken(PETHREAD Thread,
47 HANDLE TokenHandle)
48 {
49 PACCESS_TOKEN Token;
50 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
51 NTSTATUS Status;
52
53 if (TokenHandle != NULL)
54 {
55 Status = ObReferenceObjectByHandle(TokenHandle,
56 0,
57 SeTokenType,
58 UserMode,
59 (PVOID*)&Token,
60 NULL);
61 if (!NT_SUCCESS(Status))
62 {
63 return(Status);
64 }
65 ImpersonationLevel = Token->ImpersonationLevel;
66 }
67 else
68 {
69 Token = NULL;
70 ImpersonationLevel = 0;
71 }
72
73 PsImpersonateClient(Thread,
74 Token,
75 0,
76 0,
77 ImpersonationLevel);
78 if (Token != NULL)
79 {
80 ObDereferenceObject(Token);
81 }
82 return(STATUS_SUCCESS);
83 }
84
85 VOID STDCALL
86 PsRevertToSelf(PETHREAD Thread)
87 {
88 if (Thread->ActiveImpersonationInfo != 0)
89 {
90 Thread->ActiveImpersonationInfo = 0;
91 ObDereferenceObject(Thread->ImpersonationInfo->Token);
92 }
93 }
94
95 VOID STDCALL
96 PsImpersonateClient(PETHREAD Thread,
97 PACCESS_TOKEN Token,
98 UCHAR b,
99 UCHAR c,
100 SECURITY_IMPERSONATION_LEVEL Level)
101 {
102 if (Token == 0)
103 {
104 if (Thread->ActiveImpersonationInfo != 0)
105 {
106 Thread->ActiveImpersonationInfo = 0;
107 if (Thread->ImpersonationInfo->Token != NULL)
108 {
109 ObDereferenceObject(Thread->ImpersonationInfo->Token);
110 }
111 }
112 return;
113 }
114 if (Thread->ActiveImpersonationInfo == 0 ||
115 Thread->ImpersonationInfo == NULL)
116 {
117 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
118 sizeof(PS_IMPERSONATION_INFO));
119 }
120 Thread->ImpersonationInfo->Level = Level;
121 Thread->ImpersonationInfo->Unknown2 = c;
122 Thread->ImpersonationInfo->Unknown1 = b;
123 Thread->ImpersonationInfo->Token = Token;
124 ObReferenceObjectByPointer(Token,
125 0,
126 SeTokenType,
127 KernelMode);
128 Thread->ActiveImpersonationInfo = 1;
129 }
130
131 PACCESS_TOKEN
132 PsReferenceEffectiveToken(PETHREAD Thread,
133 PTOKEN_TYPE TokenType,
134 PUCHAR b,
135 PSECURITY_IMPERSONATION_LEVEL Level)
136 {
137 PEPROCESS Process;
138 PACCESS_TOKEN Token;
139
140 if (Thread->ActiveImpersonationInfo == 0)
141 {
142 Process = Thread->ThreadsProcess;
143 *TokenType = TokenPrimary;
144 *b = 0;
145 Token = Process->Token;
146 }
147 else
148 {
149 Token = Thread->ImpersonationInfo->Token;
150 *TokenType = TokenImpersonation;
151 *b = Thread->ImpersonationInfo->Unknown2;
152 *Level = Thread->ImpersonationInfo->Level;
153 }
154 return(Token);
155 }
156
157 NTSTATUS STDCALL
158 NtImpersonateThread (IN HANDLE ThreadHandle,
159 IN HANDLE ThreadToImpersonateHandle,
160 IN PSECURITY_QUALITY_OF_SERVICE
161 SecurityQualityOfService)
162 {
163 PETHREAD Thread;
164 PETHREAD ThreadToImpersonate;
165 NTSTATUS Status;
166 SE_SOME_STRUCT2 b;
167
168 Status = ObReferenceObjectByHandle(ThreadHandle,
169 0,
170 PsThreadType,
171 UserMode,
172 (PVOID*)&Thread,
173 NULL);
174 if (!NT_SUCCESS(Status))
175 {
176 return(Status);
177 }
178
179 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
180 0,
181 PsThreadType,
182 UserMode,
183 (PVOID*)&ThreadToImpersonate,
184 NULL);
185 if (!NT_SUCCESS(Status))
186 {
187 ObDereferenceObject(Thread);
188 return(Status);
189 }
190
191 Status = SeCreateClientSecurity(ThreadToImpersonate,
192 SecurityQualityOfService,
193 0,
194 &b);
195 if (!NT_SUCCESS(Status))
196 {
197 ObDereferenceObject(Thread);
198 ObDereferenceObject(ThreadToImpersonate);
199 return(Status);
200 }
201
202 SeImpersonateClient(&b, Thread);
203 if (b.Token != NULL)
204 {
205 ObDereferenceObject(b.Token);
206 }
207 return(STATUS_SUCCESS);
208 }
209
210 NTSTATUS STDCALL
211 NtOpenThreadToken(IN HANDLE ThreadHandle,
212 IN ACCESS_MASK DesiredAccess,
213 IN BOOLEAN OpenAsSelf,
214 OUT PHANDLE TokenHandle)
215 {
216 #if 0
217 PETHREAD Thread;
218 NTSTATUS Status;
219 PACCESS_TOKEN Token;
220
221 Status = ObReferenceObjectByHandle(ThreadHandle,
222 0,
223 PsThreadType,
224 UserMode,
225 (PVOID*)&Thread,
226 NULL);
227 if (!NT_SUCCESS(Status))
228 {
229 return(Status);
230 }
231
232 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
233 SepCreateImpersonationTokenDacl(Token);
234 #endif
235 return(STATUS_UNSUCCESSFUL);
236 }
237
238 PACCESS_TOKEN STDCALL
239 PsReferenceImpersonationToken(PETHREAD Thread,
240 PULONG Unknown1,
241 PULONG Unknown2,
242 SECURITY_IMPERSONATION_LEVEL* Level)
243 {
244 if (Thread->ActiveImpersonationInfo == 0)
245 {
246 return(NULL);
247 }
248
249 *Level = Thread->ImpersonationInfo->Level;
250 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
251 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
252 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
253 TOKEN_ALL_ACCESS,
254 SeTokenType,
255 KernelMode);
256 return(Thread->ImpersonationInfo->Token);
257 }
258
259 VOID
260 PiTimeoutThread(struct _KDPC *dpc,
261 PVOID Context,
262 PVOID arg1,
263 PVOID arg2)
264 {
265 // wake up the thread, and tell it it timed out
266 NTSTATUS Status = STATUS_TIMEOUT;
267
268 DPRINT("PiTimeoutThread()\n");
269
270 KeRemoveAllWaitsThread((PETHREAD)Context, Status);
271 }
272
273 VOID
274 PiBeforeBeginThread(CONTEXT c)
275 {
276 DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
277 //KeReleaseSpinLock(&PiThreadListLock, PASSIVE_LEVEL);
278 KeLowerIrql(PASSIVE_LEVEL);
279 }
280
281 #if 0
282 VOID
283 PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
284 {
285 NTSTATUS Ret;
286
287 // KeReleaseSpinLock(&PiThreadListLock,PASSIVE_LEVEL);
288 KeLowerIrql(PASSIVE_LEVEL);
289 Ret = StartRoutine(StartContext);
290 PsTerminateSystemThread(Ret);
291 KeBugCheck(0);
292 }
293 #endif
294
295 VOID PiDeleteThread(PVOID ObjectBody)
296 {
297 KIRQL oldIrql;
298
299 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
300
301 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
302 DPRINT("Process %x(%d)\n", ((PETHREAD)ObjectBody)->ThreadsProcess,
303 ObGetReferenceCount(((PETHREAD)ObjectBody)->ThreadsProcess));
304 ObDereferenceObject(((PETHREAD)ObjectBody)->ThreadsProcess);
305 ((PETHREAD)ObjectBody)->ThreadsProcess = NULL;
306 PiNrThreads--;
307 RemoveEntryList(&((PETHREAD)ObjectBody)->Tcb.ThreadListEntry);
308 HalReleaseTask((PETHREAD)ObjectBody);
309 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
310 DPRINT("PiDeleteThread() finished\n");
311 }
312
313 VOID PiCloseThread(PVOID ObjectBody, ULONG HandleCount)
314 {
315 DPRINT("PiCloseThread(ObjectBody %x)\n", ObjectBody);
316 DPRINT("ObGetReferenceCount(ObjectBody) %d "
317 "ObGetHandleCount(ObjectBody) %d\n",
318 ObGetReferenceCount(ObjectBody),
319 ObGetHandleCount(ObjectBody));
320 }
321
322 NTSTATUS
323 PsInitializeThread(HANDLE ProcessHandle,
324 PETHREAD* ThreadPtr,
325 PHANDLE ThreadHandle,
326 ACCESS_MASK DesiredAccess,
327 POBJECT_ATTRIBUTES ThreadAttributes,
328 BOOLEAN First)
329 {
330 PETHREAD Thread;
331 NTSTATUS Status;
332 KIRQL oldIrql;
333 PEPROCESS Process;
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 &PiNextThreadUniqueId);
391 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
392 Thread->DeadThread = 0;
393 Thread->Win32ThreadData = 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 return(STATUS_SUCCESS);
406 }
407
408
409 static NTSTATUS PsCreateTeb (HANDLE ProcessHandle,
410 PNT_TEB *TebPtr,
411 PETHREAD Thread,
412 PINITIAL_TEB InitialTeb)
413 {
414 MEMORY_BASIC_INFORMATION Info;
415 NTSTATUS Status;
416 ULONG ByteCount;
417 ULONG RegionSize;
418 ULONG TebSize;
419 PVOID TebBase;
420 NT_TEB Teb;
421 ULONG ResultLength;
422
423 TebBase = (PVOID)0x7FFDE000;
424 TebSize = PAGESIZE;
425
426 while (TRUE)
427 {
428 Status = NtQueryVirtualMemory(ProcessHandle,
429 TebBase,
430 MemoryBasicInformation,
431 &Info,
432 sizeof(Info),
433 &ResultLength);
434 if (!NT_SUCCESS(Status))
435 {
436 DbgPrint("NtQueryVirtualMemory (Status %x)\n", Status);
437 KeBugCheck(0);
438 }
439 /* FIXME: Race between this and the above check */
440 if (Info.State == MEM_FREE)
441 {
442 /* The TEB must reside in user space */
443 Status = NtAllocateVirtualMemory(ProcessHandle,
444 &TebBase,
445 0,
446 &TebSize,
447 MEM_COMMIT,
448 PAGE_READWRITE);
449 if (NT_SUCCESS(Status))
450 {
451 break;
452 }
453 }
454
455 TebBase = TebBase - TebSize;
456 }
457
458 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
459
460 /* set all pointers to and from the TEB */
461 Teb.Tib.Self = TebBase;
462 if (Thread->ThreadsProcess)
463 {
464 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
465 }
466 DPRINT("Teb.Peb %x\n", Teb.Peb);
467
468 /* store stack information from InitialTeb */
469 if (InitialTeb != NULL)
470 {
471 Teb.Tib.StackBase = InitialTeb->StackBase;
472 Teb.Tib.StackLimit = InitialTeb->StackLimit;
473
474 /*
475 * I don't know if this is really stored in a WNT-TEB,
476 * but it's needed to free the thread stack. (Eric Kohl)
477 */
478 Teb.StackCommit = InitialTeb->StackCommit;
479 Teb.StackCommitMax = InitialTeb->StackCommitMax;
480 Teb.StackReserve = InitialTeb->StackReserve;
481 }
482
483
484 /* more initialization */
485 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
486 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
487
488 DPRINT("sizeof(NT_TEB) %x\n", sizeof(NT_TEB));
489
490 /* write TEB data into teb page */
491 Status = NtWriteVirtualMemory(ProcessHandle,
492 TebBase,
493 &Teb,
494 sizeof(NT_TEB),
495 &ByteCount);
496
497 if (!NT_SUCCESS(Status))
498 {
499 /* free TEB */
500 DPRINT1 ("Writing TEB failed!\n");
501
502 RegionSize = 0;
503 NtFreeVirtualMemory(ProcessHandle,
504 TebBase,
505 &RegionSize,
506 MEM_RELEASE);
507
508 return Status;
509 }
510
511 if (TebPtr != NULL)
512 {
513 *TebPtr = (PNT_TEB)TebBase;
514 }
515
516 DPRINT("TEB allocated at %p\n", TebBase);
517
518 return Status;
519 }
520
521
522 NTSTATUS STDCALL
523 NtCreateThread (PHANDLE ThreadHandle,
524 ACCESS_MASK DesiredAccess,
525 POBJECT_ATTRIBUTES ObjectAttributes,
526 HANDLE ProcessHandle,
527 PCLIENT_ID Client,
528 PCONTEXT ThreadContext,
529 PINITIAL_TEB InitialTeb,
530 BOOLEAN CreateSuspended)
531 {
532 PETHREAD Thread;
533 PNT_TEB TebBase;
534 NTSTATUS Status;
535
536 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
537 ThreadHandle,ThreadContext);
538
539 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
540 DesiredAccess,ObjectAttributes, FALSE);
541 if (!NT_SUCCESS(Status))
542 {
543 return(Status);
544 }
545
546 #if 0
547 Status = NtWriteVirtualMemory(ProcessHandle,
548 (PVOID)(((ULONG)ThreadContext->Esp) - 8),
549 &ThreadContext->Eip,
550 sizeof(ULONG),
551 &Length);
552 if (!NT_SUCCESS(Status))
553 {
554 DPRINT1("NtWriteVirtualMemory failed\n");
555 KeBugCheck(0);
556 }
557 ThreadContext->Eip = LdrpGetSystemDllEntryPoint;
558 #endif
559
560 Status = Ke386InitThreadWithContext(&Thread->Tcb, ThreadContext);
561 if (!NT_SUCCESS(Status))
562 {
563 return(Status);
564 }
565
566 Status = PsCreateTeb (ProcessHandle,
567 &TebBase,
568 Thread,
569 InitialTeb);
570 if (!NT_SUCCESS(Status))
571 {
572 return(Status);
573 }
574
575 /* Attention: TebBase is in user memory space */
576 Thread->Tcb.Teb = TebBase;
577
578 Thread->StartAddress=NULL;
579
580 if (Client != NULL)
581 {
582 *Client=Thread->Cid;
583 }
584
585 /*
586 * Maybe send a message to the process's debugger
587 */
588 DbgkCreateThread((PVOID)ThreadContext->Eip);
589
590 /*
591 * Start the thread running
592 */
593 if (!CreateSuspended)
594 {
595 DPRINT("Not creating suspended\n");
596 PsUnblockThread(Thread, NULL);
597 }
598 else
599 {
600 KeBugCheck(0);
601 }
602 return(STATUS_SUCCESS);
603 }
604
605
606 NTSTATUS STDCALL
607 PsCreateSystemThread(PHANDLE ThreadHandle,
608 ACCESS_MASK DesiredAccess,
609 POBJECT_ATTRIBUTES ObjectAttributes,
610 HANDLE ProcessHandle,
611 PCLIENT_ID ClientId,
612 PKSTART_ROUTINE StartRoutine,
613 PVOID StartContext)
614 /*
615 * FUNCTION: Creates a thread which executes in kernel mode
616 * ARGUMENTS:
617 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
618 * handle
619 * DesiredAccess = Requested access to the thread
620 * ObjectAttributes = Object attributes (optional)
621 * ProcessHandle = Handle of process thread will run in
622 * NULL to use system process
623 * ClientId (OUT) = Caller supplied storage for the returned client id
624 * of the thread (optional)
625 * StartRoutine = Entry point for the thread
626 * StartContext = Argument supplied to the thread when it begins
627 * execution
628 * RETURNS: Success or failure status
629 */
630 {
631 PETHREAD Thread;
632 NTSTATUS Status;
633
634 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
635 ThreadHandle,ProcessHandle);
636
637 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
638 DesiredAccess,ObjectAttributes, FALSE);
639 if (!NT_SUCCESS(Status))
640 {
641 return(Status);
642 }
643
644 Thread->StartAddress=StartRoutine;
645 Status = Ke386InitThread(&Thread->Tcb, StartRoutine, StartContext);
646 if (!NT_SUCCESS(Status))
647 {
648 return(Status);
649 }
650
651 if (ClientId!=NULL)
652 {
653 *ClientId=Thread->Cid;
654 }
655
656 PsUnblockThread(Thread, NULL);
657
658 return(STATUS_SUCCESS);
659 }
660
661 /* EOF */