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