xref: /reactos/ntoskrnl/ps/state.c (revision 4d3df0da)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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