Thread/Process Termination/Repeaing Rewrite + Fixes
[reactos.git] / reactos / ntoskrnl / ke / process.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/process.c
5 * PURPOSE: Attaching/Detaching and System Call Tables
6 *
7 * PROGRAMMERS: Alex Ionescu (Implemented Attach/Detach and KeRemoveSystemServiceTable)
8 * Gregor Anich (Bugfixes to Attach Functions)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #include <ntdll/napi.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS *****************************************************************/
19
20 SSDT_ENTRY
21 __declspec(dllexport)
22 KeServiceDescriptorTable[SSDT_MAX_ENTRIES] = {
23 { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT },
24 { NULL, NULL, 0, NULL },
25 { NULL, NULL, 0, NULL },
26 { NULL, NULL, 0, NULL }
27 };
28
29 SSDT_ENTRY
30 KeServiceDescriptorTableShadow[SSDT_MAX_ENTRIES] = {
31 { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT },
32 { NULL, NULL, 0, NULL },
33 { NULL, NULL, 0, NULL },
34 { NULL, NULL, 0, NULL }
35 };
36
37 /* FUNCTIONS *****************************************************************/
38
39 static inline void
40 UpdatePageDirs(PKTHREAD Thread, PKPROCESS Process)
41 {
42 /*
43 * The stack and the thread structure of the current process may be
44 * located in a page which is not present in the page directory of
45 * the process we're attaching to. That would lead to a page fault
46 * when this function returns. However, since the processor can't
47 * call the page fault handler 'cause it can't push EIP on the stack,
48 * this will show up as a stack fault which will crash the entire system.
49 * To prevent this, make sure the page directory of the process we're
50 * attaching to is up-to-date.
51 */
52 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
53 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
54 }
55
56 ULONG
57 STDCALL
58 KeSetProcess(PKPROCESS Process,
59 KPRIORITY Increment)
60 {
61 KIRQL OldIrql;
62 ULONG OldState;
63
64 /* Lock Dispatcher */
65 OldIrql = KeAcquireDispatcherDatabaseLock();
66
67 /* Get Old State */
68 OldState = Process->DispatcherHeader.SignalState;
69
70 /* Signal the Process */
71 Process->DispatcherHeader.SignalState = TRUE;
72 if ((OldState == 0) && IsListEmpty(&Process->DispatcherHeader.WaitListHead) != TRUE) {
73
74 /* Satisfy waits */
75 KiWaitTest((PVOID)Process, Increment);
76 }
77
78 /* Release Dispatcher Database */
79 KeReleaseDispatcherDatabaseLock(OldIrql);
80
81 /* Return the previous State */
82 return OldState;
83 }
84
85 /*
86 * @implemented
87 */
88 VOID
89 STDCALL
90 KeAttachProcess(PKPROCESS Process)
91 {
92 KIRQL OldIrql;
93 PKTHREAD Thread = KeGetCurrentThread();
94
95 DPRINT("KeAttachProcess: %x\n", Process);
96
97 /* Make sure that we are in the right page directory */
98 UpdatePageDirs(Thread, Process);
99
100 /* Lock Dispatcher */
101 OldIrql = KeAcquireDispatcherDatabaseLock();
102
103 /* Crash system if DPC is being executed! */
104 if (KeIsExecutingDpc()) {
105
106 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
107 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
108 }
109
110 /* Check if the Target Process is already attached */
111 if (Thread->ApcState.Process == Process || Thread->ApcStateIndex != OriginalApcEnvironment) {
112
113 DPRINT("Process already Attached. Exitting\n");
114 KeReleaseDispatcherDatabaseLock(OldIrql);
115 } else {
116
117 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
118 }
119 }
120
121 VOID
122 STDCALL
123 KiAttachProcess(PKTHREAD Thread, PKPROCESS Process, KIRQL ApcLock, PRKAPC_STATE SavedApcState)
124 {
125
126 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n", Thread, Process, SavedApcState);
127
128 /* Increase Stack Count */
129 Process->StackCount++;
130
131 /* Swap the APC Environment */
132 KiMoveApcState(&Thread->ApcState, SavedApcState);
133
134 /* Reinitialize Apc State */
135 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
136 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
137 Thread->ApcState.Process = Process;
138 Thread->ApcState.KernelApcInProgress = FALSE;
139 Thread->ApcState.KernelApcPending = FALSE;
140 Thread->ApcState.UserApcPending = FALSE;
141
142 /* Update Environment Pointers if needed*/
143 if (SavedApcState == &Thread->SavedApcState) {
144
145 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
146 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
147 Thread->ApcStateIndex = AttachedApcEnvironment;
148 }
149
150 /* Swap the Processes */
151 KiSwapProcess(Process, SavedApcState->Process);
152
153 /* Return to old IRQL*/
154 KeReleaseDispatcherDatabaseLock(ApcLock);
155
156 DPRINT("KiAttachProcess Completed Sucesfully\n");
157 }
158
159 VOID
160 STDCALL
161 KiSwapProcess(PKPROCESS NewProcess, PKPROCESS OldProcess)
162 {
163 //PKPCR Pcr = KeGetCurrentKpcr();
164
165 /* Do they have an LDT? */
166 if ((NewProcess->LdtDescriptor) || (OldProcess->LdtDescriptor)) {
167
168 /* FIXME : SWitch GDT/IDT */
169 }
170 DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart);
171 Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart);
172
173 /* FIXME: Set IopmOffset in TSS */
174 }
175
176 /*
177 * @implemented
178 */
179 BOOLEAN
180 STDCALL
181 KeIsAttachedProcess(VOID)
182 {
183 /* Return the APC State */
184 return KeGetCurrentThread()->ApcStateIndex;
185 }
186
187 /*
188 * @implemented
189 */
190 VOID
191 STDCALL
192 KeStackAttachProcess(IN PKPROCESS Process,
193 OUT PRKAPC_STATE ApcState)
194 {
195 KIRQL OldIrql;
196 PKTHREAD Thread = KeGetCurrentThread();
197
198 /* Make sure that we are in the right page directory */
199 UpdatePageDirs(Thread, Process);
200
201 OldIrql = KeAcquireDispatcherDatabaseLock();
202
203 /* Crash system if DPC is being executed! */
204 if (KeIsExecutingDpc()) {
205
206 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
207 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
208 }
209
210 /* Check if the Target Process is already attached */
211 if (Thread->ApcState.Process == Process) {
212
213 ApcState->Process = (PKPROCESS)1; /* Meaning already attached to the same Process */
214
215 } else {
216
217 /* Check if the Current Thread is already attached and call the Internal Function*/
218 if (Thread->ApcStateIndex != OriginalApcEnvironment) {
219
220 KiAttachProcess(Thread, Process, OldIrql, ApcState);
221 } else {
222
223 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
224 ApcState->Process = NULL;
225 }
226 }
227 }
228
229 /*
230 * @implemented
231 */
232 VOID STDCALL
233 KeDetachProcess (VOID)
234 {
235 PKTHREAD Thread;
236 KIRQL OldIrql;
237
238 DPRINT("KeDetachProcess()\n");
239
240 /* Get Current Thread and Lock */
241 Thread = KeGetCurrentThread();
242 OldIrql = KeAcquireDispatcherDatabaseLock();
243
244 /* Check if it's attached */
245 DPRINT("Current ApcStateIndex: %x\n", Thread->ApcStateIndex);
246
247 if (Thread->ApcStateIndex == OriginalApcEnvironment) {
248
249 DPRINT1("Invalid detach (thread was not attached)\n");
250 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
251 }
252
253 /* Decrease Stack Count */
254 Thread->ApcState.Process->StackCount--;
255
256 /* Restore the APC State */
257 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
258 Thread->SavedApcState.Process = NULL;
259 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
260 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
261 Thread->ApcStateIndex = OriginalApcEnvironment;
262
263 /* Swap Processes */
264 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
265
266 /* Unlock Dispatcher */
267 KeReleaseDispatcherDatabaseLock(OldIrql);
268 }
269
270 /*
271 * @implemented
272 */
273 VOID
274 STDCALL
275 KeUnstackDetachProcess (
276 IN PRKAPC_STATE ApcState
277 )
278 {
279 KIRQL OldIrql;
280 PKTHREAD Thread;
281
282 /*
283 * If the special "We tried to attach to the process already being
284 * attached to" flag is there, don't do anything
285 */
286 if (ApcState->Process == (PKPROCESS)1) return;
287
288 Thread = KeGetCurrentThread();
289 OldIrql = KeAcquireDispatcherDatabaseLock();
290
291 /* Sorry Buddy, can't help you if you've got APCs or just aren't attached */
292 if ((Thread->ApcStateIndex == OriginalApcEnvironment) || (Thread->ApcState.KernelApcInProgress)) {
293
294 DPRINT1("Invalid detach (Thread not Attached, or Kernel APC in Progress!)\n");
295 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
296 }
297
298 /* Restore the Old APC State if a Process was present */
299 if (ApcState->Process) {
300
301 KiMoveApcState(ApcState, &Thread->ApcState);
302
303 } else {
304
305 /* The ApcState parameter is useless, so use the saved data and reset it */
306 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
307 Thread->SavedApcState.Process = NULL;
308 Thread->ApcStateIndex = OriginalApcEnvironment;
309 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
310 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
311 }
312
313 /* Swap Processes */
314 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
315
316 /* Return to old IRQL*/
317 KeReleaseDispatcherDatabaseLock(OldIrql);
318 }
319
320 /*
321 * @implemented
322 */
323 BOOLEAN STDCALL
324 KeAddSystemServiceTable(PSSDT SSDT,
325 PULONG ServiceCounterTable,
326 ULONG NumberOfServices,
327 PSSPT SSPT,
328 ULONG TableIndex)
329 {
330 /* check if descriptor table entry is free */
331 if ((TableIndex > SSDT_MAX_ENTRIES - 1) ||
332 (KeServiceDescriptorTable[TableIndex].SSDT != NULL) ||
333 (KeServiceDescriptorTableShadow[TableIndex].SSDT != NULL))
334 return FALSE;
335
336 /* initialize the shadow service descriptor table */
337 KeServiceDescriptorTableShadow[TableIndex].SSDT = SSDT;
338 KeServiceDescriptorTableShadow[TableIndex].SSPT = SSPT;
339 KeServiceDescriptorTableShadow[TableIndex].NumberOfServices = NumberOfServices;
340 KeServiceDescriptorTableShadow[TableIndex].ServiceCounterTable = ServiceCounterTable;
341
342 return TRUE;
343 }
344
345 /*
346 * @implemented
347 */
348 BOOLEAN
349 STDCALL
350 KeRemoveSystemServiceTable(IN ULONG TableIndex)
351 {
352 /* Make sure the Index is valid */
353 if (TableIndex > SSDT_MAX_ENTRIES - 1) return FALSE;
354
355 /* Is there a Normal Descriptor Table? */
356 if (!KeServiceDescriptorTable[TableIndex].SSDT) {
357
358 /* Not with the index, is there a shadow at least? */
359 if (!KeServiceDescriptorTableShadow[TableIndex].SSDT) return FALSE;
360 }
361
362 /* Now clear from the Shadow Table. */
363 KeServiceDescriptorTableShadow[TableIndex].SSDT = NULL;
364 KeServiceDescriptorTableShadow[TableIndex].SSPT = NULL;
365 KeServiceDescriptorTableShadow[TableIndex].NumberOfServices = 0;
366 KeServiceDescriptorTableShadow[TableIndex].ServiceCounterTable = NULL;
367
368 /* Check if we should clean from the Master one too */
369 if (TableIndex == 1) {
370
371 KeServiceDescriptorTable[TableIndex].SSDT = NULL;
372 KeServiceDescriptorTable[TableIndex].SSPT = NULL;
373 KeServiceDescriptorTable[TableIndex].NumberOfServices = 0;
374 KeServiceDescriptorTable[TableIndex].ServiceCounterTable = NULL;
375 }
376
377 return TRUE;
378 }
379 /* EOF */