- Properly create, grow and delete the kernel stack. Fixes potential memory leaks...
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/kill.c
5 * PURPOSE: Thread Termination and Reaping
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 PETHREAD PspReaperList = NULL;
20 WORK_QUEUE_ITEM PspReaperWorkItem;
21 BOOLEAN PspReaping = FALSE;
22 extern LIST_ENTRY PsActiveProcessHead;
23 extern FAST_MUTEX PspActiveProcessMutex;
24 extern PHANDLE_TABLE PspCidTable;
25
26 /* FUNCTIONS *****************************************************************/
27
28 VOID
29 STDCALL
30 PspReapRoutine(PVOID Context)
31 {
32 KIRQL OldIrql;
33 PETHREAD Thread, NewThread;
34
35 /* Acquire lock */
36 DPRINT("Evil reaper running!!\n");
37 OldIrql = KeAcquireDispatcherDatabaseLock();
38
39 /* Get the first Thread Entry */
40 Thread = PspReaperList;
41 PspReaperList = NULL;
42 DPRINT("PspReaperList: %x\n", Thread);
43
44 /* Check to see if the list is empty */
45 do {
46
47 /* Unlock the Dispatcher */
48 KeReleaseDispatcherDatabaseLock(OldIrql);
49
50 /* Is there a thread on the list? */
51 while (Thread) {
52
53 /* Get the next Thread */
54 DPRINT("Thread: %x\n", Thread);
55 DPRINT("Thread: %x\n", Thread->ReaperLink);
56 NewThread = Thread->ReaperLink;
57
58 /* Remove reference to current thread */
59 ObDereferenceObject(Thread);
60
61 /* Move to next Thread */
62 Thread = NewThread;
63 }
64
65 /* No more linked threads... Reacquire the Lock */
66 OldIrql = KeAcquireDispatcherDatabaseLock();
67
68 /* Now try to get a new thread from the list */
69 Thread = PspReaperList;
70 PspReaperList = NULL;
71 DPRINT("PspReaperList: %x\n", Thread);
72
73 /* Loop again if there is a new thread */
74 } while (Thread);
75
76 PspReaping = FALSE;
77 DPRINT("Done reaping\n");
78 KeReleaseDispatcherDatabaseLock(OldIrql);
79 }
80
81 VOID
82 STDCALL
83 PspKillMostProcesses(VOID)
84 {
85 PLIST_ENTRY current_entry;
86 PEPROCESS current;
87
88 ASSERT(PsGetCurrentProcessId() == PsInitialSystemProcess->UniqueProcessId);
89
90 /* Acquire the Active Process Lock */
91 ExAcquireFastMutex(&PspActiveProcessMutex);
92
93 /* Loop all processes on the list */
94 current_entry = PsActiveProcessHead.Flink;
95 while (current_entry != &PsActiveProcessHead)
96 {
97 current = CONTAINING_RECORD(current_entry, EPROCESS, ActiveProcessLinks);
98 current_entry = current_entry->Flink;
99
100 if (current->UniqueProcessId != PsInitialSystemProcess->UniqueProcessId)
101 {
102 /* Terminate all the Threads in this Process */
103 PspTerminateProcessThreads(current, STATUS_SUCCESS);
104 }
105 }
106
107 /* Release the lock */
108 ExReleaseFastMutex(&PspActiveProcessMutex);
109 }
110
111 VOID
112 STDCALL
113 PspTerminateProcessThreads(PEPROCESS Process,
114 NTSTATUS ExitStatus)
115 {
116 PLIST_ENTRY CurrentEntry;
117 PETHREAD Thread, CurrentThread = PsGetCurrentThread();
118
119 CurrentEntry = Process->ThreadListHead.Flink;
120 while (CurrentEntry != &Process->ThreadListHead) {
121
122 /* Get the Current Thread */
123 Thread = CONTAINING_RECORD(CurrentEntry, ETHREAD, ThreadListEntry);
124
125 /* Move to the Next Thread */
126 CurrentEntry = CurrentEntry->Flink;
127
128 /* Make sure it's not the one we're in */
129 if (Thread != CurrentThread) {
130
131 /* Make sure it didn't already terminate */
132 if (!Thread->Terminated) {
133
134 Thread->Terminated = TRUE;
135
136 /* Terminate it by APC */
137 PspTerminateThreadByPointer(Thread, ExitStatus);
138 }
139 }
140 }
141 }
142
143 VOID
144 STDCALL
145 PspDeleteProcess(PVOID ObjectBody)
146 {
147 PEPROCESS Process = (PEPROCESS)ObjectBody;
148
149 DPRINT("PiDeleteProcess(ObjectBody %x)\n", ObjectBody);
150
151 /* Remove it from the Active List */
152 ExAcquireFastMutex(&PspActiveProcessMutex);
153 RemoveEntryList(&Process->ActiveProcessLinks);
154 ExReleaseFastMutex(&PspActiveProcessMutex);
155
156 /* Delete the CID Handle */
157 if(Process->UniqueProcessId)
158 {
159 ExDestroyHandle(PspCidTable, Process->UniqueProcessId);
160 }
161
162 /* KDB hook */
163 KDB_DELETEPROCESS_HOOK(Process);
164
165 /* Dereference the Token */
166 SeDeassignPrimaryToken(Process);
167
168 /* Release Memory Information */
169 MmReleaseMmInfo(Process);
170
171 /* Delete the W32PROCESS structure if there's one associated */
172 if(Process->Win32Process != NULL) ExFreePool(Process->Win32Process);
173 }
174
175 VOID
176 STDCALL
177 PspDeleteThread(PVOID ObjectBody)
178 {
179 PETHREAD Thread = (PETHREAD)ObjectBody;
180 PEPROCESS Process = Thread->ThreadsProcess;
181
182 DPRINT("PiDeleteThread(ObjectBody 0x%x, process 0x%x)\n",ObjectBody, Thread->ThreadsProcess);
183
184 /* Deassociate the Process */
185 Thread->ThreadsProcess = NULL;
186
187 /* Delete the CID Handle */
188 if(Thread->Cid.UniqueThread)
189 {
190 ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread);
191 }
192
193 /* Free the W32THREAD structure if present */
194 if(Thread->Tcb.Win32Thread != NULL) ExFreePool (Thread->Tcb.Win32Thread);
195
196 /* Release the Kernel Stack */
197 MmDeleteKernelStack((PVOID)Thread->Tcb.StackLimit, Thread->Tcb.StackLimit);
198
199 /* Dereference the Process */
200 ObDereferenceObject(Process);
201 }
202
203 /*
204 * FUNCTION: Terminates the current thread
205 * See "Windows Internals" - Chapter 13, Page 50-53
206 */
207 VOID
208 STDCALL
209 PspExitThread(NTSTATUS ExitStatus)
210 {
211 PETHREAD CurrentThread;
212 BOOLEAN Last;
213 PEPROCESS CurrentProcess;
214 PTERMINATION_PORT TerminationPort;
215 PTEB Teb;
216 KIRQL oldIrql;
217 PLIST_ENTRY FirstEntry, CurrentEntry;
218 PKAPC Apc;
219
220 DPRINT("PspExitThread(ExitStatus %x), Current: 0x%x\n", ExitStatus, PsGetCurrentThread());
221
222 /* Get the Current Thread and Process */
223 CurrentThread = PsGetCurrentThread();
224 CurrentProcess = CurrentThread->ThreadsProcess;
225
226 /* Set the Exit Status and Exit Time */
227 CurrentThread->ExitStatus = ExitStatus;
228 KeQuerySystemTime(&CurrentThread->ExitTime);
229
230 /* Can't terminate a thread if it attached another process */
231 if (KeIsAttachedProcess()) {
232
233 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
234 (ULONG) CurrentThread->Tcb.ApcState.Process,
235 (ULONG) CurrentThread->Tcb.ApcStateIndex,
236 (ULONG) CurrentThread);
237 }
238
239 /* Lower to Passive Level */
240 KeLowerIrql(PASSIVE_LEVEL);
241
242 /* Lock the Process before we modify its thread entries */
243 PsLockProcess(CurrentProcess, FALSE);
244
245 /* wake up the thread so we don't deadlock on PsLockProcess */
246 KeForceResumeThread(&CurrentThread->Tcb);
247
248 /* Run Thread Notify Routines before we desintegrate the thread */
249 PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
250
251 /* Remove the thread from the thread list of its process */
252 RemoveEntryList(&CurrentThread->ThreadListEntry);
253 Last = IsListEmpty(&CurrentProcess->ThreadListHead);
254
255 /* Set the last Thread Exit Status */
256 CurrentProcess->LastThreadExitStatus = ExitStatus;
257
258 if (Last) {
259
260 /* Save the Exit Time if not already done by NtTerminateProcess. This
261 happens when the last thread just terminates without explicitly
262 terminating the process. */
263 CurrentProcess->ExitTime = CurrentThread->ExitTime;
264 CurrentProcess->ExitStatus = ExitStatus;
265 }
266
267 /* Check if the process has a debug port */
268 if (CurrentProcess->DebugPort) {
269
270 /* Notify the Debug API. TODO */
271 //Last ? DbgkExitProcess(ExitStatus) : DbgkExitThread(ExitStatus);
272 }
273
274 /* Process the Termination Ports */
275 while ((TerminationPort = CurrentThread->TerminationPort)) {
276
277 DPRINT("TerminationPort: %p\n", TerminationPort);
278
279 /* Get the next one */
280 CurrentThread->TerminationPort = TerminationPort->Next;
281
282 /* Send the LPC Message */
283 LpcSendTerminationPort(TerminationPort->Port, CurrentThread->CreateTime);
284 ObDereferenceObject(TerminationPort->Port);
285
286 /* Free the Port */
287 ExFreePool(TerminationPort);
288 }
289
290 /* Rundown Win32 Structures */
291 PsTerminateWin32Thread(CurrentThread);
292 if (Last) PsTerminateWin32Process(CurrentProcess);
293
294 /* Rundown Registry Notifications. TODO (refers to NtChangeNotify, not Cm callbacks) */
295 //CmNotifyRunDown(CurrentThread);
296
297 /* Free the TEB */
298 if((Teb = CurrentThread->Tcb.Teb))
299 {
300 /* Clean up the stack first, if requested */
301 if (Teb->FreeStackOnTermination)
302 {
303 ULONG Dummy = 0;
304 ZwFreeVirtualMemory(NtCurrentProcess(),
305 &Teb->DeallocationStack,
306 &Dummy,
307 MEM_RELEASE);
308 }
309
310 DPRINT("Decommit teb at %p\n", Teb);
311 MmDeleteTeb(CurrentProcess, Teb);
312 CurrentThread->Tcb.Teb = NULL;
313 }
314
315 /* The last Thread shuts down the Process */
316 if (Last) PspExitProcess(CurrentProcess);
317
318 /* Unlock the Process */
319 PsUnlockProcess(CurrentProcess);
320
321 /* Cancel I/O for the thread. */
322 IoCancelThreadIo(CurrentThread);
323
324 /* Rundown Timers */
325 ExTimerRundown();
326 KeCancelTimer(&CurrentThread->Tcb.Timer);
327
328 /* If the Processor Control Block's NpxThread points to the current thread
329 * unset it.
330 */
331 KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
332 InterlockedCompareExchangePointer(&KeGetCurrentPrcb()->NpxThread,
333 NULL,
334 (PKPROCESS)CurrentThread);
335 KeLowerIrql(oldIrql);
336
337 /* Rundown Mutexes */
338 KeRundownThread();
339
340 /* Disable new APC Queuing, this is as far as we'll let them go */
341 KeDisableThreadApcQueueing(&CurrentThread->Tcb);
342
343 /* Flush the User APCs */
344 FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, UserMode);
345 if (FirstEntry != NULL)
346 {
347 CurrentEntry = FirstEntry;
348 do
349 {
350 /* Get the APC */
351 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
352
353 /* Move to the next one */
354 CurrentEntry = CurrentEntry->Flink;
355
356 /* Rundown the APC or de-allocate it */
357 if (Apc->RundownRoutine)
358 {
359 /* Call its own routine */
360 (Apc->RundownRoutine)(Apc);
361 }
362 else
363 {
364 /* Do it ourselves */
365 ExFreePool(Apc);
366 }
367 }
368 while (CurrentEntry != FirstEntry);
369 }
370
371 /* Call the Lego routine */
372 if (CurrentThread->Tcb.LegoData) PspRunLegoRoutine(&CurrentThread->Tcb);
373
374 /* Flush the APC queue, which should be empty */
375 if ((FirstEntry = KeFlushQueueApc(&CurrentThread->Tcb, KernelMode)))
376 {
377 /* Bugcheck time */
378 KEBUGCHECKEX(KERNEL_APC_PENDING_DURING_EXIT,
379 (ULONG_PTR)FirstEntry,
380 CurrentThread->Tcb.KernelApcDisable,
381 oldIrql,
382 0);
383 }
384
385 /* Terminate the Thread from the Scheduler */
386 KeTerminateThread(0);
387 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
388 KEBUGCHECK(0);
389 }
390
391 VOID
392 STDCALL
393 PsExitSpecialApc(PKAPC Apc,
394 PKNORMAL_ROUTINE* NormalRoutine,
395 PVOID* NormalContext,
396 PVOID* SystemArgument1,
397 PVOID* SystemArguemnt2)
398 {
399 DPRINT("PsExitSpecialApc called: 0x%x (proc: 0x%x, '%.16s')\n",
400 PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
401
402 /* Don't do anything unless we are in User-Mode */
403 if (Apc->SystemArgument2)
404 {
405 NTSTATUS ExitStatus = (NTSTATUS)Apc->NormalContext;
406
407 /* Free the APC */
408 ExFreePool(Apc);
409
410 /* Terminate the Thread */
411 PspExitThread(ExitStatus);
412
413 /* we should never reach this point! */
414 KEBUGCHECK(0);
415 }
416 }
417
418 VOID
419 STDCALL
420 PspExitNormalApc(PVOID NormalContext,
421 PVOID SystemArgument1,
422 PVOID SystemArgument2)
423 {
424 PKAPC Apc = (PKAPC)SystemArgument1;
425 PETHREAD Thread = PsGetCurrentThread();
426 NTSTATUS ExitStatus;
427
428 DPRINT("PspExitNormalApc called: 0x%x (proc: 0x%x, '%.16s')\n",
429 PsGetCurrentThread(), PsGetCurrentProcess(), PsGetCurrentProcess()->ImageFileName);
430
431 /* This should never happen */
432 ASSERT(!SystemArgument2);
433
434 /* If this is a system thread, we can safely kill it from Kernel-Mode */
435 if (PsIsSystemThread(Thread))
436 {
437 /* Get the Exit Status */
438 DPRINT1("Killing System Thread\n");
439 ExitStatus = (NTSTATUS)Apc->NormalContext;
440
441 /* Free the APC */
442 ExFreePool(Apc);
443
444 /* Exit the Thread */
445 PspExitThread(ExitStatus);
446 }
447
448 /* If we're here, this is not a System Thread, so kill it from User-Mode */
449 DPRINT("Initializing User-Mode APC\n");
450 KeInitializeApc(Apc,
451 &Thread->Tcb,
452 OriginalApcEnvironment,
453 PsExitSpecialApc,
454 NULL,
455 PspExitNormalApc,
456 UserMode,
457 NormalContext);
458
459 /* Now insert the APC with the User-Mode Flag */
460 KeInsertQueueApc(Apc, Apc, (PVOID)UserMode, 2);
461
462 /* Forcefully resume the thread */
463 KeForceResumeThread(&Thread->Tcb);
464 }
465
466 /*
467 * See "Windows Internals" - Chapter 13, Page 49
468 */
469 VOID
470 STDCALL
471 PspTerminateThreadByPointer(PETHREAD Thread,
472 NTSTATUS ExitStatus)
473 {
474 PKAPC Apc;
475
476 DPRINT("PspTerminatedThreadByPointer(Thread %x, ExitStatus %x)\n",
477 Thread, ExitStatus);
478
479 /* Check if we are already in the right context */
480 if (PsGetCurrentThread() == Thread) {
481
482 /* Directly terminate the thread */
483 PspExitThread(ExitStatus);
484
485 /* we should never reach this point! */
486 KEBUGCHECK(0);
487 }
488
489 /* Allocate the APC */
490 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
491
492 /* Initialize a Kernel Mode APC to Kill the Thread */
493 KeInitializeApc(Apc,
494 &Thread->Tcb,
495 OriginalApcEnvironment,
496 PsExitSpecialApc,
497 NULL,
498 PspExitNormalApc,
499 KernelMode,
500 (PVOID)ExitStatus);
501
502 /* Insert it into the APC Queue */
503 KeInsertQueueApc(Apc,
504 Apc,
505 NULL,
506 2);
507
508 /* Forcefully resume the thread */
509 KeForceResumeThread(&Thread->Tcb);
510 }
511
512 NTSTATUS
513 STDCALL
514 PspExitProcess(PEPROCESS Process)
515 {
516 DPRINT("PspExitProcess 0x%x\n", Process);
517
518 PspRunCreateProcessNotifyRoutines(Process, FALSE);
519
520 PspDestroyQuotaBlock(Process);
521
522 /* close all handles associated with our process, this needs to be done
523 when the last thread still runs */
524 ObKillProcess(Process);
525
526 KeSetProcess(&Process->Pcb, IO_NO_INCREMENT);
527
528 /* release the keep-alive reference of the process object */
529 ObDereferenceObject(Process);
530
531 return(STATUS_SUCCESS);
532 }
533
534 NTSTATUS
535 STDCALL
536 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
537 IN NTSTATUS ExitStatus)
538 {
539 NTSTATUS Status;
540 PEPROCESS Process;
541 PETHREAD CurrentThread;
542 BOOLEAN KillByHandle;
543
544 PAGED_CODE();
545
546 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
547 ProcessHandle, ExitStatus);
548
549 KillByHandle = (ProcessHandle != NULL);
550
551 /* Get the Process Object */
552 Status = ObReferenceObjectByHandle((KillByHandle ? ProcessHandle : NtCurrentProcess()),
553 PROCESS_TERMINATE,
554 PsProcessType,
555 KeGetPreviousMode(),
556 (PVOID*)&Process,
557 NULL);
558 if (!NT_SUCCESS(Status)) {
559
560 DPRINT1("Invalid handle to Process\n");
561 return(Status);
562 }
563
564 CurrentThread = PsGetCurrentThread();
565
566 PsLockProcess(Process, FALSE);
567
568 if(Process->ExitTime.QuadPart != 0)
569 {
570 PsUnlockProcess(Process);
571 ObDereferenceObject(Process);
572 return STATUS_PROCESS_IS_TERMINATING;
573 }
574
575 /* Terminate all the Process's Threads */
576 PspTerminateProcessThreads(Process, ExitStatus);
577
578 /* only kill the calling thread if it either passed a process handle or
579 NtCurrentProcess() */
580 if (KillByHandle) {
581
582 /* set the exit time as we're about to release the process lock before
583 we kill ourselves to prevent threads outside of our process trying
584 to kill us */
585 KeQuerySystemTime(&Process->ExitTime);
586 Process->ExitStatus = ExitStatus;
587
588 /* Only master thread remains... kill it off */
589 if (CurrentThread->ThreadsProcess == Process) {
590
591 /* mark our thread as terminating so attempts to terminate it, when
592 unlocking the process, fail */
593 CurrentThread->Terminated = TRUE;
594
595 PsUnlockProcess(Process);
596
597 /* we can safely dereference the process because the current thread
598 holds a reference to it until it gets reaped */
599 ObDereferenceObject(Process);
600
601 /* now the other threads get a chance to terminate, we don't wait but
602 just kill ourselves right now. The process will be run down when the
603 last thread terminates */
604
605 PspExitThread(ExitStatus);
606
607 /* we should never reach this point! */
608 KEBUGCHECK(0);
609 }
610 }
611
612 /* unlock and dereference the process so the threads can kill themselves */
613 PsUnlockProcess(Process);
614 ObDereferenceObject(Process);
615
616 return(STATUS_SUCCESS);
617 }
618
619 NTSTATUS
620 STDCALL
621 NtTerminateThread(IN HANDLE ThreadHandle,
622 IN NTSTATUS ExitStatus)
623 {
624 PETHREAD Thread;
625 NTSTATUS Status;
626
627 PAGED_CODE();
628
629 /* Handle the special NULL case */
630 if (!ThreadHandle)
631 {
632 /* Check if we're the only thread left */
633 if (IsListEmpty(&PsGetCurrentProcess()->Pcb.ThreadListHead))
634 {
635 /* This is invalid */
636 DPRINT1("Can't terminate self\n");
637 return STATUS_CANT_TERMINATE_SELF;
638 }
639 else
640 {
641 /* Use current handle */
642 ThreadHandle = NtCurrentThread();
643 }
644 }
645
646 /* Get the Thread Object */
647 Status = ObReferenceObjectByHandle(ThreadHandle,
648 THREAD_TERMINATE,
649 PsThreadType,
650 KeGetPreviousMode(),
651 (PVOID*)&Thread,
652 NULL);
653 if (!NT_SUCCESS(Status)) {
654
655 DPRINT1("Could not reference thread object\n");
656 return(Status);
657 }
658
659 /* Make sure this is not a system thread */
660 if (PsIsSystemThread(Thread)) {
661
662 DPRINT1("Trying to Terminate a system thread!\n");
663 ObDereferenceObject(Thread);
664 return STATUS_INVALID_PARAMETER;
665 }
666
667 /* Check to see if we're running in the same thread */
668 if (Thread != PsGetCurrentThread()) {
669
670 /* we need to lock the process to make sure it's not already terminating */
671 PsLockProcess(Thread->ThreadsProcess, FALSE);
672
673 /* This isn't our thread, terminate it if not already done */
674 if (!Thread->Terminated) {
675
676 Thread->Terminated = TRUE;
677
678 /* Terminate it */
679 PspTerminateThreadByPointer(Thread, ExitStatus);
680 }
681
682 PsUnlockProcess(Thread->ThreadsProcess);
683
684 /* Dereference the Thread and return */
685 ObDereferenceObject(Thread);
686
687 } else {
688
689 Thread->Terminated = TRUE;
690
691 /* it's safe to dereference thread, there's at least the keep-alive
692 reference which will be removed by the thread reaper causing the
693 thread to be finally destroyed */
694 ObDereferenceObject(Thread);
695
696 /* Terminate him, he's ours */
697 PspExitThread(ExitStatus);
698
699 /* We do never reach this point */
700 KEBUGCHECK(0);
701 }
702
703 return(STATUS_SUCCESS);
704 }
705
706 /*
707 * @implemented
708 */
709 NTSTATUS
710 STDCALL
711 PsTerminateSystemThread(NTSTATUS ExitStatus)
712 {
713 PETHREAD Thread = PsGetCurrentThread();
714
715 /* Make sure this is a system thread */
716 if (!PsIsSystemThread(Thread)) {
717
718 DPRINT1("Trying to Terminate a non-system thread!\n");
719 return STATUS_INVALID_PARAMETER;
720 }
721
722 /* Terminate it for real */
723 PspExitThread(ExitStatus);
724
725 /* we should never reach this point! */
726 KEBUGCHECK(0);
727
728 return(STATUS_SUCCESS);
729 }
730
731 NTSTATUS
732 STDCALL
733 NtRegisterThreadTerminatePort(HANDLE PortHandle)
734 {
735 NTSTATUS Status;
736 PTERMINATION_PORT TerminationPort;
737 PVOID TerminationLpcPort;
738 PETHREAD Thread;
739
740 PAGED_CODE();
741
742 /* Get the Port */
743 Status = ObReferenceObjectByHandle(PortHandle,
744 PORT_ALL_ACCESS,
745 LpcPortObjectType,
746 KeGetPreviousMode(),
747 &TerminationLpcPort,
748 NULL);
749 if (!NT_SUCCESS(Status)) {
750
751 DPRINT1("Failed to reference Port\n");
752 return(Status);
753 }
754
755 /* Allocate the Port and make sure it suceeded */
756 if((TerminationPort = ExAllocatePoolWithTag(NonPagedPool,
757 sizeof(TERMINATION_PORT),
758 TAG('P', 's', 'T', '=')))) {
759
760 /* Associate the Port */
761 Thread = PsGetCurrentThread();
762 TerminationPort->Port = TerminationLpcPort;
763 DPRINT("TerminationPort: %p\n", TerminationPort);
764 TerminationPort->Next = Thread->TerminationPort;
765 Thread->TerminationPort = TerminationPort;
766 DPRINT("TerminationPort: %p\n", Thread->TerminationPort);
767
768 /* Return success */
769 return(STATUS_SUCCESS);
770
771 } else {
772
773 /* Dereference and Fail */
774 ObDereferenceObject(TerminationPort);
775 return(STATUS_INSUFFICIENT_RESOURCES);
776 }
777 }