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