Check if attached when terminating thread. Suggested by tamlin.
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /* $Id: kill.c,v 1.75 2004/09/14 18:37:40 gvg Exp $
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 extern ULONG PiNrThreads;
21 extern ULONG PiNrRunnableThreads;
22 extern KSPIN_LOCK PiThreadListLock;
23 extern LIST_ENTRY PiThreadListHead;
24 extern KSPIN_LOCK PiApcLock;
25
26 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
27
28 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
29
30 /* FUNCTIONS *****************************************************************/
31
32 VOID
33 PiTerminateProcessThreads(PEPROCESS Process,
34 NTSTATUS ExitStatus)
35 {
36 KIRQL oldlvl;
37 PLIST_ENTRY current_entry;
38 PETHREAD current;
39
40 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
41 Process, ExitStatus);
42
43 KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
44
45 current_entry = Process->ThreadListHead.Flink;
46 while (current_entry != &Process->ThreadListHead)
47 {
48 current = CONTAINING_RECORD(current_entry, ETHREAD,
49 Tcb.ProcessThreadListEntry);
50 if (current != PsGetCurrentThread() &&
51 current->DeadThread == 0)
52 {
53 DPRINT("Terminating %x, current thread: %x, "
54 "thread's process: %x\n", current, PsGetCurrentThread(),
55 current->ThreadsProcess);
56 KeReleaseSpinLock(&PiThreadListLock, oldlvl);
57 PsTerminateOtherThread(current, ExitStatus);
58 KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
59 current_entry = Process->ThreadListHead.Flink;
60 }
61 else
62 {
63 current_entry = current_entry->Flink;
64 }
65 }
66 KeReleaseSpinLock(&PiThreadListLock, oldlvl);
67 DPRINT("Finished PiTerminateProcessThreads()\n");
68 }
69
70 VOID
71 PsReapThreads(VOID)
72 {
73 PLIST_ENTRY current_entry;
74 PETHREAD current;
75 KIRQL oldIrql;
76
77 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
78
79 current_entry = PiThreadListHead.Flink;
80
81 while (current_entry != &PiThreadListHead)
82 {
83 current = CONTAINING_RECORD(current_entry, ETHREAD,
84 Tcb.ThreadListEntry);
85
86 current_entry = current_entry->Flink;
87
88 if (current->Tcb.State == THREAD_STATE_TERMINATED_1)
89 {
90 PiNrThreadsAwaitingReaping--;
91 current->Tcb.State = THREAD_STATE_TERMINATED_2;
92
93 /*
94 An unbelievably complex chain of events would cause a system crash
95 if PiThreadListLock was still held when the thread object is about
96 to be destroyed
97 */
98 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
99 ObDereferenceObject(current);
100 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
101
102 current_entry = PiThreadListHead.Flink;
103 }
104 }
105 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
106 }
107
108 VOID
109 PsTerminateCurrentThread(NTSTATUS ExitStatus)
110 /*
111 * FUNCTION: Terminates the current thread
112 */
113 {
114 KIRQL oldIrql;
115 PETHREAD CurrentThread;
116 PLIST_ENTRY current_entry;
117 PKMUTANT Mutant;
118 BOOLEAN Last;
119 PEPROCESS CurrentProcess;
120 SIZE_T Length = PAGE_SIZE;
121
122 KeLowerIrql(PASSIVE_LEVEL);
123
124 CurrentThread = PsGetCurrentThread();
125 CurrentProcess = CurrentThread->ThreadsProcess;
126
127 /* Can't terminate a thread if it attached another process */
128 if (AttachedApcEnvironment == CurrentThread->Tcb.ApcStateIndex)
129 {
130 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT, (ULONG) CurrentProcess,
131 (ULONG) CurrentThread->Tcb.ApcState.Process,
132 (ULONG) CurrentThread->Tcb.ApcStateIndex,
133 (ULONG) CurrentThread);
134 }
135
136 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
137
138 DPRINT("terminating %x\n",CurrentThread);
139
140 CurrentThread->ExitStatus = ExitStatus;
141 KeQuerySystemTime((PLARGE_INTEGER)&CurrentThread->u1.ExitTime);
142 KeCancelTimer(&CurrentThread->Tcb.Timer);
143
144 /* Remove the thread from the thread list of its process */
145 RemoveEntryList(&CurrentThread->Tcb.ProcessThreadListEntry);
146 Last = IsListEmpty(&CurrentProcess->ThreadListHead);
147
148 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
149
150 /* Notify subsystems of the thread termination */
151 PspRunCreateThreadNotifyRoutines(CurrentThread, FALSE);
152 PsTerminateWin32Thread(CurrentThread);
153
154 /* Free the TEB */
155 if(CurrentThread->Tcb.Teb)
156 ZwFreeVirtualMemory
157 (
158 NtCurrentProcess(),
159 (PVOID *)&CurrentThread->Tcb.Teb,
160 &Length,
161 MEM_RELEASE
162 );
163
164 /* abandon all owned mutants */
165 current_entry = CurrentThread->Tcb.MutantListHead.Flink;
166 while (current_entry != &CurrentThread->Tcb.MutantListHead)
167 {
168 Mutant = CONTAINING_RECORD(current_entry, KMUTANT,
169 MutantListEntry);
170 KeReleaseMutant(Mutant,
171 MUTANT_INCREMENT,
172 TRUE,
173 FALSE);
174 current_entry = CurrentThread->Tcb.MutantListHead.Flink;
175 }
176
177 oldIrql = KeAcquireDispatcherDatabaseLock();
178 CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
179 KeDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader);
180 KeReleaseDispatcherDatabaseLock (oldIrql);
181
182 /* The last thread shall close the door on exit */
183 if(Last)
184 {
185 PspRunCreateProcessNotifyRoutines(CurrentProcess, FALSE);
186 PsTerminateWin32Process(CurrentProcess);
187 PiTerminateProcess(CurrentProcess, ExitStatus);
188 }
189
190 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
191
192 ExpSwapThreadEventPair(CurrentThread, NULL); /* Release the associated eventpair object, if there was one */
193 KeRemoveAllWaitsThread (CurrentThread, STATUS_UNSUCCESSFUL, FALSE);
194
195 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
196 DPRINT1("Unexpected return, CurrentThread %x PsGetCurrentThread() %x\n", CurrentThread, PsGetCurrentThread());
197 KEBUGCHECK(0);
198 }
199
200 VOID STDCALL
201 PiTerminateThreadRundownRoutine(PKAPC Apc)
202 {
203 ExFreePool(Apc);
204 }
205
206 VOID STDCALL
207 PiTerminateThreadKernelRoutine(PKAPC Apc,
208 PKNORMAL_ROUTINE* NormalRoutine,
209 PVOID* NormalContext,
210 PVOID* SystemArgument1,
211 PVOID* SystemArguemnt2)
212 {
213 ExFreePool(Apc);
214 }
215
216 VOID STDCALL
217 PiTerminateThreadNormalRoutine(PVOID NormalContext,
218 PVOID SystemArgument1,
219 PVOID SystemArgument2)
220 {
221 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus);
222 }
223
224 VOID
225 PsTerminateOtherThread(PETHREAD Thread,
226 NTSTATUS ExitStatus)
227 /*
228 * FUNCTION: Terminate a thread when calling from another thread's context
229 * NOTES: This function must be called with PiThreadListLock held
230 */
231 {
232 PKAPC Apc;
233 NTSTATUS Status;
234
235 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
236 Thread, ExitStatus);
237
238 Thread->DeadThread = 1;
239 Thread->ExitStatus = ExitStatus;
240 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
241 KeInitializeApc(Apc,
242 &Thread->Tcb,
243 OriginalApcEnvironment,
244 PiTerminateThreadKernelRoutine,
245 PiTerminateThreadRundownRoutine,
246 PiTerminateThreadNormalRoutine,
247 KernelMode,
248 NULL);
249 KeInsertQueueApc(Apc,
250 NULL,
251 NULL,
252 IO_NO_INCREMENT);
253 if (THREAD_STATE_BLOCKED == Thread->Tcb.State && UserMode == Thread->Tcb.WaitMode)
254 {
255 DPRINT("Unblocking thread\n");
256 Status = STATUS_THREAD_IS_TERMINATING;
257 KeRemoveAllWaitsThread(Thread, Status, TRUE);
258 }
259 }
260
261 NTSTATUS STDCALL
262 PiTerminateProcess(PEPROCESS Process,
263 NTSTATUS ExitStatus)
264 {
265 KIRQL OldIrql;
266 PEPROCESS CurrentProcess;
267
268 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
269 Process, ExitStatus, ObGetObjectPointerCount(Process),
270 ObGetObjectHandleCount(Process));
271
272 ObReferenceObject(Process);
273 if (InterlockedExchange((PLONG)&Process->Pcb.State,
274 PROCESS_STATE_TERMINATED) ==
275 PROCESS_STATE_TERMINATED)
276 {
277 ObDereferenceObject(Process);
278 return(STATUS_SUCCESS);
279 }
280 CurrentProcess = PsGetCurrentProcess();
281 if (Process != CurrentProcess)
282 {
283 KeAttachProcess( Process );
284 }
285 ObCloseAllHandles(Process);
286 if (Process != CurrentProcess)
287 {
288 KeDetachProcess();
289 }
290 OldIrql = KeAcquireDispatcherDatabaseLock ();
291 Process->Pcb.DispatcherHeader.SignalState = TRUE;
292 KeDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
293 KeReleaseDispatcherDatabaseLock (OldIrql);
294 ObDereferenceObject(Process);
295 return(STATUS_SUCCESS);
296 }
297
298 NTSTATUS STDCALL
299 NtTerminateProcess(IN HANDLE ProcessHandle,
300 IN NTSTATUS ExitStatus)
301 {
302 NTSTATUS Status;
303 PEPROCESS Process;
304
305 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
306 ProcessHandle, ExitStatus);
307
308 Status = ObReferenceObjectByHandle(ProcessHandle,
309 PROCESS_TERMINATE,
310 PsProcessType,
311 KeGetCurrentThread()->PreviousMode,
312 (PVOID*)&Process,
313 NULL);
314 if (!NT_SUCCESS(Status))
315 {
316 return(Status);
317 }
318 Process->ExitStatus = ExitStatus;
319 PiTerminateProcessThreads(Process, ExitStatus);
320 if (PsGetCurrentThread()->ThreadsProcess == Process)
321 {
322 ObDereferenceObject(Process);
323 PsTerminateCurrentThread(ExitStatus);
324 }
325 ObDereferenceObject(Process);
326 return(STATUS_SUCCESS);
327 }
328
329
330 NTSTATUS STDCALL
331 NtTerminateThread(IN HANDLE ThreadHandle,
332 IN NTSTATUS ExitStatus)
333 {
334 PETHREAD Thread;
335 NTSTATUS Status;
336
337 Status = ObReferenceObjectByHandle(ThreadHandle,
338 THREAD_TERMINATE,
339 PsThreadType,
340 KeGetCurrentThread()->PreviousMode,
341 (PVOID*)&Thread,
342 NULL);
343 if (Status != STATUS_SUCCESS)
344 {
345 return(Status);
346 }
347
348 ObDereferenceObject(Thread);
349
350 if (Thread == PsGetCurrentThread())
351 {
352 PsTerminateCurrentThread(ExitStatus);
353 }
354 else
355 {
356 PsTerminateOtherThread(Thread, ExitStatus);
357 }
358 return(STATUS_SUCCESS);
359 }
360
361
362 /*
363 * @implemented
364 */
365 NTSTATUS STDCALL
366 PsTerminateSystemThread(NTSTATUS ExitStatus)
367 /*
368 * FUNCTION: Terminates the current thread
369 * ARGUMENTS:
370 * ExitStatus = Status to pass to the creater
371 * RETURNS: Doesn't
372 */
373 {
374 PsTerminateCurrentThread(ExitStatus);
375 return(STATUS_SUCCESS);
376 }
377
378 NTSTATUS STDCALL
379 NtCallTerminatePorts(PETHREAD Thread)
380 {
381 KIRQL oldIrql;
382 PLIST_ENTRY current_entry;
383 PEPORT_TERMINATION_REQUEST current;
384
385 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
386 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
387 &Thread->TerminationPortList);
388 {
389 current = CONTAINING_RECORD(current_entry,
390 EPORT_TERMINATION_REQUEST,
391 ThreadListEntry);
392 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
393 LpcSendTerminationPort(current->Port,
394 Thread->CreateTime);
395 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
396 }
397 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
398 return(STATUS_SUCCESS);
399 }
400
401 NTSTATUS STDCALL
402 NtRegisterThreadTerminatePort(HANDLE TerminationPortHandle)
403 {
404 NTSTATUS Status;
405 PEPORT_TERMINATION_REQUEST Request;
406 PEPORT TerminationPort;
407 KIRQL oldIrql;
408 PETHREAD Thread;
409
410 Status = ObReferenceObjectByHandle(TerminationPortHandle,
411 PORT_ALL_ACCESS,
412 ExPortType,
413 KeGetCurrentThread()->PreviousMode,
414 (PVOID*)&TerminationPort,
415 NULL);
416 if (!NT_SUCCESS(Status))
417 {
418 return(Status);
419 }
420
421 Request = ExAllocatePool(NonPagedPool, sizeof(EPORT_TERMINATION_REQUEST));
422 Request->Port = TerminationPort;
423 Thread = PsGetCurrentThread();
424 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
425 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
426 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
427
428 return(STATUS_SUCCESS);
429 }