- Prevent from calling PsTerminateCurrentThread from within an apc if PsTerminateThre...
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/kill.c
6 * PURPOSE: Terminating a thread
7 * PROGRAMMER: David Welch (welch@cwcom.net)
8 * UPDATE HISTORY:
9 * Created 22/05/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS *******************************************************************/
19
20 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
21 NTSTATUS STDCALL NtCallTerminatePorts(PETHREAD Thread);
22
23 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
24
25 LIST_ENTRY ThreadsToReapHead;
26
27 /* FUNCTIONS *****************************************************************/
28
29 VOID
30 PsInitializeThreadReaper(VOID)
31 {
32 InitializeListHead(&ThreadsToReapHead);
33 }
34
35 VOID
36 PsReapThreads(VOID)
37 {
38 KIRQL oldlvl;
39 PETHREAD Thread;
40 PLIST_ENTRY ListEntry;
41
42 oldlvl = KeAcquireDispatcherDatabaseLock();
43 while((ListEntry = RemoveHeadList(&ThreadsToReapHead)) != &ThreadsToReapHead)
44 {
45 PiNrThreadsAwaitingReaping--;
46 KeReleaseDispatcherDatabaseLock(oldlvl);
47 Thread = CONTAINING_RECORD(ListEntry, ETHREAD, TerminationPortList);
48
49 ObDereferenceObject(Thread);
50 oldlvl = KeAcquireDispatcherDatabaseLock();
51 }
52 KeReleaseDispatcherDatabaseLock(oldlvl);
53 }
54
55 VOID
56 PsQueueThreadReap(PETHREAD Thread)
57 {
58 InsertTailList(&ThreadsToReapHead, &Thread->TerminationPortList);
59 PiNrThreadsAwaitingReaping++;
60 }
61
62 VOID
63 PiTerminateProcessThreads(PEPROCESS Process,
64 NTSTATUS ExitStatus)
65 {
66 KIRQL oldlvl;
67 PLIST_ENTRY current_entry;
68 PETHREAD current, CurrentThread = PsGetCurrentThread();
69
70 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
71 Process, ExitStatus);
72
73 oldlvl = KeAcquireDispatcherDatabaseLock();
74
75 current_entry = Process->ThreadListHead.Flink;
76 while (current_entry != &Process->ThreadListHead)
77 {
78 current = CONTAINING_RECORD(current_entry, ETHREAD,
79 ThreadListEntry);
80 if (current != CurrentThread && current->HasTerminated == 0)
81 {
82 DPRINT("Terminating %x, current thread: %x, "
83 "thread's process: %x\n", current, PsGetCurrentThread(),
84 current->ThreadsProcess);
85 KeReleaseDispatcherDatabaseLock(oldlvl);
86 PsTerminateOtherThread(current, ExitStatus);
87 oldlvl = KeAcquireDispatcherDatabaseLock();
88 current_entry = Process->ThreadListHead.Flink;
89 }
90 else
91 {
92 current_entry = current_entry->Flink;
93 }
94 }
95 KeReleaseDispatcherDatabaseLock(oldlvl);
96 DPRINT("Finished PiTerminateProcessThreads()\n");
97 }
98
99 VOID
100 PsTerminateCurrentThread(NTSTATUS ExitStatus)
101 /*
102 * FUNCTION: Terminates the current thread
103 */
104 {
105 KIRQL oldIrql;
106 PETHREAD CurrentThread;
107 PLIST_ENTRY current_entry;
108 PKMUTANT Mutant;
109 BOOLEAN Last;
110 PEPROCESS CurrentProcess;
111 SIZE_T Length = PAGE_SIZE;
112 PVOID TebBlock;
113
114 KeLowerIrql(PASSIVE_LEVEL);
115
116 CurrentThread = PsGetCurrentThread();
117 CurrentProcess = CurrentThread->ThreadsProcess;
118
119 /* Can't terminate a thread if it attached another process */
120 if (AttachedApcEnvironment == CurrentThread->Tcb.ApcStateIndex)
121 {
122 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
123 (ULONG) CurrentThread->Tcb.ApcState.Process,
124 (ULONG) CurrentThread->Tcb.ApcStateIndex,
125 (ULONG) CurrentThread);
126 }
127
128 KeCancelTimer(&CurrentThread->Tcb.Timer);
129
130 oldIrql = KeAcquireDispatcherDatabaseLock();
131
132 DPRINT("terminating %x\n",CurrentThread);
133
134 CurrentThread->HasTerminated = TRUE;
135 CurrentThread->ExitStatus = ExitStatus;
136 KeQuerySystemTime((PLARGE_INTEGER)&CurrentThread->ExitTime);
137
138 /* If the ProcessoR Control Block's NpxThread points to the current thread
139 * unset it.
140 */
141 InterlockedCompareExchangePointer(&KeGetCurrentKPCR()->PrcbData.NpxThread,
142 NULL, ETHREAD_TO_KTHREAD(CurrentThread));
143
144 KeReleaseDispatcherDatabaseLock(oldIrql);
145
146 PsLockProcess(CurrentProcess, FALSE);
147
148 /* Remove the thread from the thread list of its process */
149 RemoveEntryList(&CurrentThread->ThreadListEntry);
150 Last = IsListEmpty(&CurrentProcess->ThreadListHead);
151 PsUnlockProcess(CurrentProcess);
152
153 /* Notify subsystems of the thread termination */
154 PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
155 PsTerminateWin32Thread(CurrentThread);
156
157 /* Free the TEB */
158 if(CurrentThread->Tcb.Teb)
159 {
160 DPRINT("Decommit teb at %p\n", CurrentThread->Tcb.Teb);
161 ExAcquireFastMutex(&CurrentProcess->TebLock);
162 TebBlock = MM_ROUND_DOWN(CurrentThread->Tcb.Teb, MM_VIRTMEM_GRANULARITY);
163 ZwFreeVirtualMemory(NtCurrentProcess(),
164 (PVOID *)&CurrentThread->Tcb.Teb,
165 &Length,
166 MEM_DECOMMIT);
167 DPRINT("teb %p, TebBlock %p\n", CurrentThread->Tcb.Teb, TebBlock);
168 if (TebBlock != CurrentProcess->TebBlock ||
169 CurrentProcess->TebBlock == CurrentProcess->TebLastAllocated)
170 {
171 MmLockAddressSpace(&CurrentProcess->AddressSpace);
172 MmReleaseMemoryAreaIfDecommitted(CurrentProcess, &CurrentProcess->AddressSpace, TebBlock);
173 MmUnlockAddressSpace(&CurrentProcess->AddressSpace);
174 }
175 CurrentThread->Tcb.Teb = NULL;
176 ExReleaseFastMutex(&CurrentProcess->TebLock);
177 }
178
179 /* abandon all owned mutants */
180 current_entry = CurrentThread->Tcb.MutantListHead.Flink;
181 while (current_entry != &CurrentThread->Tcb.MutantListHead)
182 {
183 Mutant = CONTAINING_RECORD(current_entry, KMUTANT,
184 MutantListEntry);
185 KeReleaseMutant(Mutant,
186 MUTANT_INCREMENT,
187 TRUE,
188 FALSE);
189 current_entry = CurrentThread->Tcb.MutantListHead.Flink;
190 }
191
192 oldIrql = KeAcquireDispatcherDatabaseLock();
193 CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
194 KiDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader);
195 KeReleaseDispatcherDatabaseLock (oldIrql);
196
197 /* The last thread shall close the door on exit */
198 if(Last)
199 {
200 /* save the last thread exit status */
201 CurrentProcess->LastThreadExitStatus = ExitStatus;
202
203 PspRunCreateProcessNotifyRoutines(CurrentProcess, FALSE);
204 PsTerminateWin32Process(CurrentProcess);
205 PiTerminateProcess(CurrentProcess, ExitStatus);
206 }
207
208 oldIrql = KeAcquireDispatcherDatabaseLock();
209
210 #ifdef _ENABLE_THRDEVTPAIR
211 ExpSwapThreadEventPair(CurrentThread, NULL); /* Release the associated eventpair object, if there was one */
212 #endif /* _ENABLE_THRDEVTPAIR */
213
214 ASSERT(CurrentThread->Tcb.WaitBlockList == NULL);
215
216 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
217 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
218 KEBUGCHECK(0);
219 }
220
221 VOID STDCALL
222 PiTerminateThreadRundownRoutine(PKAPC Apc)
223 {
224 ExFreePool(Apc);
225 }
226
227 VOID STDCALL
228 PiTerminateThreadKernelRoutine(PKAPC Apc,
229 PKNORMAL_ROUTINE* NormalRoutine,
230 PVOID* NormalContext,
231 PVOID* SystemArgument1,
232 PVOID* SystemArguemnt2)
233 {
234 ExFreePool(Apc);
235 }
236
237 VOID STDCALL
238 PiTerminateThreadNormalRoutine(PVOID NormalContext,
239 PVOID SystemArgument1,
240 PVOID SystemArgument2)
241 {
242 PETHREAD EThread = PsGetCurrentThread();
243 if (EThread->HasTerminated)
244 {
245 /* Someone else has already called PsTerminateCurrentThread */
246 return;
247 }
248 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus);
249 }
250
251 VOID
252 PsTerminateOtherThread(PETHREAD Thread,
253 NTSTATUS ExitStatus)
254 /*
255 * FUNCTION: Terminate a thread when calling from another thread's context
256 * NOTES: This function must be called with PiThreadLock held
257 */
258 {
259 PKAPC Apc;
260 KIRQL OldIrql;
261
262 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
263 Thread, ExitStatus);
264
265 OldIrql = KeAcquireDispatcherDatabaseLock();
266 if (Thread->HasTerminated)
267 {
268 KeReleaseDispatcherDatabaseLock (OldIrql);
269 return;
270 }
271 Thread->HasTerminated = TRUE;
272 KeReleaseDispatcherDatabaseLock (OldIrql);
273 Thread->ExitStatus = ExitStatus;
274 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
275 KeInitializeApc(Apc,
276 &Thread->Tcb,
277 OriginalApcEnvironment,
278 PiTerminateThreadKernelRoutine,
279 PiTerminateThreadRundownRoutine,
280 PiTerminateThreadNormalRoutine,
281 KernelMode,
282 NULL);
283 KeInsertQueueApc(Apc,
284 NULL,
285 NULL,
286 IO_NO_INCREMENT);
287
288 OldIrql = KeAcquireDispatcherDatabaseLock();
289 if (THREAD_STATE_BLOCKED == Thread->Tcb.State && UserMode == Thread->Tcb.WaitMode)
290 {
291 DPRINT("Unblocking thread\n");
292 KiAbortWaitThread((PKTHREAD)Thread, STATUS_THREAD_IS_TERMINATING);
293 }
294 KeReleaseDispatcherDatabaseLock(OldIrql);
295 }
296
297 NTSTATUS STDCALL
298 PiTerminateProcess(PEPROCESS Process,
299 NTSTATUS ExitStatus)
300 {
301 KIRQL OldIrql;
302 PEPROCESS CurrentProcess;
303
304 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
305 Process, ExitStatus, ObGetObjectPointerCount(Process),
306 ObGetObjectHandleCount(Process));
307
308 ObReferenceObject(Process);
309 if (InterlockedExchangeUL(&Process->Pcb.State,
310 PROCESS_STATE_TERMINATED) ==
311 PROCESS_STATE_TERMINATED)
312 {
313 ObDereferenceObject(Process);
314 return(STATUS_SUCCESS);
315 }
316 CurrentProcess = PsGetCurrentProcess();
317 if (Process != CurrentProcess)
318 {
319 KeAttachProcess(&Process->Pcb);
320 }
321 ObCloseAllHandles(Process);
322 if (Process != CurrentProcess)
323 {
324 KeDetachProcess();
325 }
326 OldIrql = KeAcquireDispatcherDatabaseLock ();
327 Process->Pcb.DispatcherHeader.SignalState = TRUE;
328 KiDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
329 KeReleaseDispatcherDatabaseLock (OldIrql);
330 ObDereferenceObject(Process);
331 return(STATUS_SUCCESS);
332 }
333
334 NTSTATUS STDCALL
335 NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
336 IN NTSTATUS ExitStatus)
337 {
338 NTSTATUS Status;
339 PEPROCESS Process;
340
341 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
342 ProcessHandle, ExitStatus);
343
344 Status = ObReferenceObjectByHandle(ProcessHandle,
345 PROCESS_TERMINATE,
346 PsProcessType,
347 KeGetCurrentThread()->PreviousMode,
348 (PVOID*)&Process,
349 NULL);
350 if (!NT_SUCCESS(Status))
351 {
352 return(Status);
353 }
354 Process->ExitStatus = ExitStatus;
355 PiTerminateProcessThreads(Process, ExitStatus);
356 if (PsGetCurrentThread()->ThreadsProcess == Process)
357 {
358 ObDereferenceObject(Process);
359 PsTerminateCurrentThread(ExitStatus);
360 /*
361 * We should never get here!
362 */
363 return(STATUS_SUCCESS);
364 }
365 ObDereferenceObject(Process);
366 return(STATUS_SUCCESS);
367 }
368
369
370 NTSTATUS STDCALL
371 NtTerminateThread(IN HANDLE ThreadHandle,
372 IN NTSTATUS ExitStatus)
373 {
374 PETHREAD Thread;
375 NTSTATUS Status;
376
377 Status = ObReferenceObjectByHandle(ThreadHandle,
378 THREAD_TERMINATE,
379 PsThreadType,
380 KeGetCurrentThread()->PreviousMode,
381 (PVOID*)&Thread,
382 NULL);
383 if (Status != STATUS_SUCCESS)
384 {
385 return(Status);
386 }
387
388 if (Thread == PsGetCurrentThread())
389 {
390 /* dereference the thread object before we kill our thread */
391 ObDereferenceObject(Thread);
392 PsTerminateCurrentThread(ExitStatus);
393 /*
394 * We should never get here!
395 */
396 }
397 else
398 {
399 PsTerminateOtherThread(Thread, ExitStatus);
400 ObDereferenceObject(Thread);
401 }
402 return(STATUS_SUCCESS);
403 }
404
405
406 /*
407 * @implemented
408 */
409 NTSTATUS STDCALL
410 PsTerminateSystemThread(NTSTATUS ExitStatus)
411 /*
412 * FUNCTION: Terminates the current thread
413 * ARGUMENTS:
414 * ExitStatus = Status to pass to the creater
415 * RETURNS: Doesn't
416 */
417 {
418 PsTerminateCurrentThread(ExitStatus);
419 return(STATUS_SUCCESS);
420 }
421
422 NTSTATUS STDCALL
423 NtCallTerminatePorts(PETHREAD Thread)
424 {
425 KIRQL oldIrql;
426 PLIST_ENTRY current_entry;
427 PEPORT_TERMINATION_REQUEST current;
428
429 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
430 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
431 &Thread->TerminationPortList);
432 {
433 current = CONTAINING_RECORD(current_entry,
434 EPORT_TERMINATION_REQUEST,
435 ThreadListEntry);
436 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
437 LpcSendTerminationPort(current->Port,
438 Thread->CreateTime);
439 ExFreePool(current);
440 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
441 }
442 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
443 return(STATUS_SUCCESS);
444 }
445
446 NTSTATUS STDCALL
447 NtRegisterThreadTerminatePort(HANDLE PortHandle)
448 {
449 NTSTATUS Status;
450 PEPORT_TERMINATION_REQUEST Request;
451 PEPORT TerminationPort;
452 KIRQL oldIrql;
453 PETHREAD Thread;
454
455 Status = ObReferenceObjectByHandle(PortHandle,
456 PORT_ALL_ACCESS,
457 LpcPortObjectType,
458 KeGetCurrentThread()->PreviousMode,
459 (PVOID*)&TerminationPort,
460 NULL);
461 if (!NT_SUCCESS(Status))
462 {
463 return(Status);
464 }
465
466 Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
467 if(Request != NULL)
468 {
469 Request->Port = TerminationPort;
470 Thread = PsGetCurrentThread();
471 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
472 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
473 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
474
475 return(STATUS_SUCCESS);
476 }
477 else
478 {
479 ObDereferenceObject(TerminationPort);
480 return(STATUS_INSUFFICIENT_RESOURCES);
481 }
482 }