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