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