1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/state.c
5 * PURPOSE: Process Manager: Process/Thread State Control
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PRIVATE FUNCTIONS *********************************************************/
17
18 VOID
19 NTAPI
PspQueueApcSpecialApc(IN PKAPC Apc,IN OUT PKNORMAL_ROUTINE * NormalRoutine,IN OUT PVOID * NormalContext,IN OUT PVOID * SystemArgument1,IN OUT PVOID * SystemArgument2)20 PspQueueApcSpecialApc(IN PKAPC Apc,
21 IN OUT PKNORMAL_ROUTINE* NormalRoutine,
22 IN OUT PVOID* NormalContext,
23 IN OUT PVOID* SystemArgument1,
24 IN OUT PVOID* SystemArgument2)
25 {
26 /* Free the APC and do nothing else */
27 ExFreePool(Apc);
28 }
29
30 NTSTATUS
31 NTAPI
PsResumeThread(IN PETHREAD Thread,OUT PULONG PreviousCount OPTIONAL)32 PsResumeThread(IN PETHREAD Thread,
33 OUT PULONG PreviousCount OPTIONAL)
34 {
35 ULONG OldCount;
36 PAGED_CODE();
37
38 /* Resume the thread */
39 OldCount = KeResumeThread(&Thread->Tcb);
40
41 /* Return the count if asked */
42 if (PreviousCount) *PreviousCount = OldCount;
43 return STATUS_SUCCESS;
44 }
45
46 NTSTATUS
47 NTAPI
PsSuspendThread(IN PETHREAD Thread,OUT PULONG PreviousCount OPTIONAL)48 PsSuspendThread(
49 IN PETHREAD Thread,
50 OUT PULONG PreviousCount OPTIONAL)
51 {
52 NTSTATUS Status;
53 ULONG OldCount = 0;
54 PAGED_CODE();
55
56 /* Assume success */
57 Status = STATUS_SUCCESS;
58
59 /* Check if we're suspending ourselves */
60 if (Thread == PsGetCurrentThread())
61 {
62 /* Guard with SEH because KeSuspendThread can raise an exception */
63 _SEH2_TRY
64 {
65 /* Do the suspend */
66 OldCount = KeSuspendThread(&Thread->Tcb);
67 }
68 _SEH2_EXCEPT(_SEH2_GetExceptionCode() == STATUS_SUSPEND_COUNT_EXCEEDED)
69 {
70 /* Get the exception code */
71 Status = _SEH2_GetExceptionCode();
72 }
73 _SEH2_END;
74 }
75 else
76 {
77 /* Acquire rundown protection */
78 if (ExAcquireRundownProtection(&Thread->RundownProtect))
79 {
80 /* Make sure the thread isn't terminating */
81 if (Thread->Terminated)
82 {
83 /* Fail */
84 Status = STATUS_THREAD_IS_TERMINATING;
85 }
86 else
87 {
88 /* Guard with SEH because KeSuspendThread can raise an exception */
89 _SEH2_TRY
90 {
91 /* Do the suspend */
92 OldCount = KeSuspendThread(&Thread->Tcb);
93 }
94 _SEH2_EXCEPT(_SEH2_GetExceptionCode() == STATUS_SUSPEND_COUNT_EXCEEDED)
95 {
96 /* Get the exception code */
97 Status = _SEH2_GetExceptionCode();
98 }
99 _SEH2_END;
100
101 /* Check if it was terminated during the suspend */
102 if (Thread->Terminated)
103 {
104 /* Wake it back up and fail */
105 KeForceResumeThread(&Thread->Tcb);
106 Status = STATUS_THREAD_IS_TERMINATING;
107 OldCount = 0;
108 }
109 }
110
111 /* Release rundown protection */
112 ExReleaseRundownProtection(&Thread->RundownProtect);
113 }
114 else
115 {
116 /* Thread is terminating */
117 Status = STATUS_THREAD_IS_TERMINATING;
118 }
119 }
120
121 /* Write back the previous count */
122 if (PreviousCount) *PreviousCount = OldCount;
123 return Status;
124 }
125
126 NTSTATUS
127 NTAPI
PsResumeProcess(IN PEPROCESS Process)128 PsResumeProcess(IN PEPROCESS Process)
129 {
130 PETHREAD Thread;
131 PAGED_CODE();
132
133 /* Lock the Process */
134 if (!ExAcquireRundownProtection(&Process->RundownProtect))
135 {
136 /* Process is terminating */
137 return STATUS_PROCESS_IS_TERMINATING;
138 }
139
140 /* Get the first thread */
141 Thread = PsGetNextProcessThread(Process, NULL);
142 while (Thread)
143 {
144 /* Resume it */
145 KeResumeThread(&Thread->Tcb);
146
147 /* Move to the next thread */
148 Thread = PsGetNextProcessThread(Process, Thread);
149 }
150
151 /* Unlock the process */
152 ExReleaseRundownProtection(&Process->RundownProtect);
153 return STATUS_SUCCESS;
154 }
155
156 NTSTATUS
157 NTAPI
PsSuspendProcess(IN PEPROCESS Process)158 PsSuspendProcess(IN PEPROCESS Process)
159 {
160 PETHREAD Thread;
161 PAGED_CODE();
162
163 /* Lock the Process */
164 if (!ExAcquireRundownProtection(&Process->RundownProtect))
165 {
166 /* Process is terminating */
167 return STATUS_PROCESS_IS_TERMINATING;
168 }
169
170 /* Get the first thread */
171 Thread = PsGetNextProcessThread(Process, NULL);
172 while (Thread)
173 {
174 /* Resume it */
175 PsSuspendThread(Thread, NULL);
176
177 /* Move to the next thread */
178 Thread = PsGetNextProcessThread(Process, Thread);
179 }
180
181 /* Unlock the process */
182 ExReleaseRundownProtection(&Process->RundownProtect);
183 return STATUS_SUCCESS;
184 }
185
186 /* PUBLIC FUNCTIONS **********************************************************/
187
188 /*
189 * @implemented
190 */
191 NTSTATUS
192 NTAPI
NtAlertThread(IN HANDLE ThreadHandle)193 NtAlertThread(IN HANDLE ThreadHandle)
194 {
195 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
196 PETHREAD Thread;
197 NTSTATUS Status;
198
199 /* Reference the Object */
200 Status = ObReferenceObjectByHandle(ThreadHandle,
201 THREAD_SUSPEND_RESUME,
202 PsThreadType,
203 PreviousMode,
204 (PVOID*)&Thread,
205 NULL);
206 if (NT_SUCCESS(Status))
207 {
208 /*
209 * Do an alert depending on the processor mode. If some kmode code wants to
210 * enforce a umode alert it should call KeAlertThread() directly. If kmode
211 * code wants to do a kmode alert it's sufficient to call it with Zw or just
212 * use KeAlertThread() directly
213 */
214 KeAlertThread(&Thread->Tcb, PreviousMode);
215
216 /* Dereference Object */
217 ObDereferenceObject(Thread);
218 }
219
220 /* Return status */
221 return Status;
222 }
223
224 NTSTATUS
225 NTAPI
NtAlertResumeThread(IN HANDLE ThreadHandle,OUT PULONG SuspendCount)226 NtAlertResumeThread(IN HANDLE ThreadHandle,
227 OUT PULONG SuspendCount)
228 {
229 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
230 PETHREAD Thread;
231 NTSTATUS Status;
232 ULONG PreviousState;
233
234 /* Check if we came from user mode with a suspend count */
235 if ((SuspendCount) && (PreviousMode != KernelMode))
236 {
237 /* Enter SEH for probing */
238 _SEH2_TRY
239 {
240 /* Probe the count */
241 ProbeForWriteUlong(SuspendCount);
242 }
243 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
244 {
245 /* Return the exception code */
246 _SEH2_YIELD(return _SEH2_GetExceptionCode());
247 }
248 _SEH2_END;
249 }
250
251 /* Reference the Object */
252 Status = ObReferenceObjectByHandle(ThreadHandle,
253 THREAD_SUSPEND_RESUME,
254 PsThreadType,
255 PreviousMode,
256 (PVOID*)&Thread,
257 NULL);
258 if (NT_SUCCESS(Status))
259 {
260 /* Call the Kernel Function */
261 PreviousState = KeAlertResumeThread(&Thread->Tcb);
262
263 /* Dereference Object */
264 ObDereferenceObject(Thread);
265
266 /* Check if the caller gave a suspend count */
267 if (SuspendCount)
268 {
269 /* Enter SEH for write */
270 _SEH2_TRY
271 {
272 /* Write state back */
273 *SuspendCount = PreviousState;
274 }
275 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
276 {
277 /* Get exception code */
278 Status = _SEH2_GetExceptionCode();
279 }
280 _SEH2_END;
281 }
282 }
283
284 /* Return status */
285 return Status;
286 }
287
288 NTSTATUS
289 NTAPI
NtResumeThread(IN HANDLE ThreadHandle,OUT PULONG SuspendCount OPTIONAL)290 NtResumeThread(IN HANDLE ThreadHandle,
291 OUT PULONG SuspendCount OPTIONAL)
292 {
293 PETHREAD Thread;
294 ULONG Prev;
295 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
296 NTSTATUS Status;
297 PAGED_CODE();
298
299 /* Check if caller gave a suspend count from user mode */
300 if ((SuspendCount) && (PreviousMode != KernelMode))
301 {
302 /* Enter SEH for probing */
303 _SEH2_TRY
304 {
305 /* Probe the count */
306 ProbeForWriteUlong(SuspendCount);
307 }
308 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
309 {
310 /* Return the exception code */
311 _SEH2_YIELD(return _SEH2_GetExceptionCode());
312 }
313 _SEH2_END;
314 }
315
316 /* Get the Thread Object */
317 Status = ObReferenceObjectByHandle(ThreadHandle,
318 THREAD_SUSPEND_RESUME,
319 PsThreadType,
320 PreviousMode,
321 (PVOID*)&Thread,
322 NULL);
323 if (!NT_SUCCESS(Status)) return Status;
324
325 /* Call the internal function */
326 Status = PsResumeThread(Thread, &Prev);
327
328 /* Check if the caller wanted the count back */
329 if (SuspendCount)
330 {
331 /* Enter SEH for write back */
332 _SEH2_TRY
333 {
334 /* Write the count */
335 *SuspendCount = Prev;
336 }
337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
338 {
339 /* Get the exception code */
340 Status = _SEH2_GetExceptionCode();
341 }
342 _SEH2_END;
343 }
344
345 /* Dereference and return */
346 ObDereferenceObject(Thread);
347 return Status;
348 }
349
350 NTSTATUS
351 NTAPI
NtSuspendThread(IN HANDLE ThreadHandle,OUT PULONG PreviousSuspendCount OPTIONAL)352 NtSuspendThread(IN HANDLE ThreadHandle,
353 OUT PULONG PreviousSuspendCount OPTIONAL)
354 {
355 PETHREAD Thread;
356 ULONG Prev;
357 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
358 NTSTATUS Status;
359 PAGED_CODE();
360
361 /* Check if caller gave a suspend count from user mode */
362 if ((PreviousSuspendCount) && (PreviousMode != KernelMode))
363 {
364 /* Enter SEH for probing */
365 _SEH2_TRY
366 {
367 /* Probe the count */
368 ProbeForWriteUlong(PreviousSuspendCount);
369 }
370 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
371 {
372 /* Return the exception code */
373 _SEH2_YIELD(return _SEH2_GetExceptionCode());
374 }
375 _SEH2_END;
376 }
377
378 /* Get the Thread Object */
379 Status = ObReferenceObjectByHandle(ThreadHandle,
380 THREAD_SUSPEND_RESUME,
381 PsThreadType,
382 PreviousMode,
383 (PVOID*)&Thread,
384 NULL);
385 if (!NT_SUCCESS(Status)) return Status;
386
387 /* Call the internal function */
388 Status = PsSuspendThread(Thread, &Prev);
389 ObDereferenceObject(Thread);
390 if (!NT_SUCCESS(Status)) return Status;
391
392 /* Protect write with SEH */
393 _SEH2_TRY
394 {
395 /* Return the Previous Count */
396 if (PreviousSuspendCount) *PreviousSuspendCount = Prev;
397 }
398 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
399 {
400 /* Get the exception code */
401 Status = _SEH2_GetExceptionCode();
402 }
403 _SEH2_END;
404
405 /* Return */
406 return Status;
407 }
408
409 NTSTATUS
410 NTAPI
NtSuspendProcess(IN HANDLE ProcessHandle)411 NtSuspendProcess(IN HANDLE ProcessHandle)
412 {
413 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
414 PEPROCESS Process;
415 NTSTATUS Status;
416 PAGED_CODE();
417
418 /* Reference the process */
419 Status = ObReferenceObjectByHandle(ProcessHandle,
420 PROCESS_SUSPEND_RESUME,
421 PsProcessType,
422 PreviousMode,
423 (PVOID*)&Process,
424 NULL);
425 if (NT_SUCCESS(Status))
426 {
427 /* Call the internal function */
428 Status = PsSuspendProcess(Process);
429 ObDereferenceObject(Process);
430 }
431
432 /* Return status */
433 return Status;
434 }
435
436 NTSTATUS
437 NTAPI
NtResumeProcess(IN HANDLE ProcessHandle)438 NtResumeProcess(IN HANDLE ProcessHandle)
439 {
440 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
441 PEPROCESS Process;
442 NTSTATUS Status;
443 PAGED_CODE();
444
445 /* Reference the process */
446 Status = ObReferenceObjectByHandle(ProcessHandle,
447 PROCESS_SUSPEND_RESUME,
448 PsProcessType,
449 PreviousMode,
450 (PVOID*)&Process,
451 NULL);
452 if (NT_SUCCESS(Status))
453 {
454 /* Call the internal function */
455 Status = PsResumeProcess(Process);
456 ObDereferenceObject(Process);
457 }
458
459 /* Return status */
460 return Status;
461 }
462
463 NTSTATUS
464 NTAPI
NtTestAlert(VOID)465 NtTestAlert(VOID)
466 {
467 /* Check and Alert Thread if needed */
468 return KeTestAlertThread(ExGetPreviousMode()) ?
469 STATUS_ALERTED : STATUS_SUCCESS;
470 }
471
472 /*++
473 * @name NtQueueApcThreadEx
474 * NT4
475 *
476 * This routine is used to queue an APC from user-mode for the specified
477 * thread.
478 *
479 * @param ThreadHandle
480 * Handle to the Thread.
481 * This handle must have THREAD_SET_CONTEXT privileges.
482 *
483 * @param UserApcReserveHandle
484 * Optional handle to reserve object (introduced in Windows 7), providing ability to
485 * reserve memory before performing stability-critical parts of code.
486 *
487 * @param ApcRoutine
488 * Pointer to the APC Routine to call when the APC executes.
489 *
490 * @param NormalContext
491 * Pointer to the context to send to the Normal Routine.
492 *
493 * @param SystemArgument[1-2]
494 * Pointer to a set of two parameters that contain untyped data.
495 *
496 * @return STATUS_SUCCESS or failure cute from associated calls.
497 *
498 * @remarks The thread must enter an alertable wait before the APC will be
499 * delivered.
500 *
501 *--*/
502 NTSTATUS
503 NTAPI
NtQueueApcThreadEx(IN HANDLE ThreadHandle,IN OPTIONAL HANDLE UserApcReserveHandle,IN PKNORMAL_ROUTINE ApcRoutine,IN PVOID NormalContext,IN OPTIONAL PVOID SystemArgument1,IN OPTIONAL PVOID SystemArgument2)504 NtQueueApcThreadEx(IN HANDLE ThreadHandle,
505 IN OPTIONAL HANDLE UserApcReserveHandle,
506 IN PKNORMAL_ROUTINE ApcRoutine,
507 IN PVOID NormalContext,
508 IN OPTIONAL PVOID SystemArgument1,
509 IN OPTIONAL PVOID SystemArgument2)
510 {
511 PKAPC Apc;
512 PETHREAD Thread;
513 NTSTATUS Status = STATUS_SUCCESS;
514 PAGED_CODE();
515
516 /* Get ETHREAD from Handle */
517 Status = ObReferenceObjectByHandle(ThreadHandle,
518 THREAD_SET_CONTEXT,
519 PsThreadType,
520 ExGetPreviousMode(),
521 (PVOID)&Thread,
522 NULL);
523 if (!NT_SUCCESS(Status)) return Status;
524
525 /* Check if this is a System Thread */
526 if (Thread->SystemThread)
527 {
528 /* Fail */
529 Status = STATUS_INVALID_HANDLE;
530 goto Quit;
531 }
532
533 /* Allocate an APC */
534 Apc = ExAllocatePoolWithQuotaTag(NonPagedPool |
535 POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
536 sizeof(KAPC),
537 TAG_PS_APC);
538 if (!Apc)
539 {
540 /* Fail */
541 Status = STATUS_NO_MEMORY;
542 goto Quit;
543 }
544
545 /* Initialize the APC */
546 KeInitializeApc(Apc,
547 &Thread->Tcb,
548 OriginalApcEnvironment,
549 PspQueueApcSpecialApc,
550 NULL,
551 ApcRoutine,
552 UserMode,
553 NormalContext);
554
555 /* Queue it */
556 if (!KeInsertQueueApc(Apc,
557 SystemArgument1,
558 SystemArgument2,
559 IO_NO_INCREMENT))
560 {
561 /* We failed, free it */
562 ExFreePool(Apc);
563 Status = STATUS_UNSUCCESSFUL;
564 }
565
566 /* Dereference Thread and Return */
567 Quit:
568 ObDereferenceObject(Thread);
569 return Status;
570 }
571
572 /*++
573 * @name NtQueueApcThread
574 * NT4
575 *
576 * This routine is used to queue an APC from user-mode for the specified
577 * thread.
578 *
579 * @param ThreadHandle
580 * Handle to the Thread.
581 * This handle must have THREAD_SET_CONTEXT privileges.
582 *
583 * @param ApcRoutine
584 * Pointer to the APC Routine to call when the APC executes.
585 *
586 * @param NormalContext
587 * Pointer to the context to send to the Normal Routine.
588 *
589 * @param SystemArgument[1-2]
590 * Pointer to a set of two parameters that contain untyped data.
591 *
592 * @return STATUS_SUCCESS or failure cute from associated calls.
593 *
594 * @remarks The thread must enter an alertable wait before the APC will be
595 * delivered.
596 *
597 *--*/
598 NTSTATUS
599 NTAPI
NtQueueApcThread(IN HANDLE ThreadHandle,IN PKNORMAL_ROUTINE ApcRoutine,IN PVOID NormalContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)600 NtQueueApcThread(IN HANDLE ThreadHandle,
601 IN PKNORMAL_ROUTINE ApcRoutine,
602 IN PVOID NormalContext,
603 IN PVOID SystemArgument1,
604 IN PVOID SystemArgument2)
605 {
606 return NtQueueApcThreadEx(ThreadHandle, NULL, ApcRoutine, NormalContext, SystemArgument1, SystemArgument2);
607 }
608
609 /* EOF */
610