1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ex/harderr.c 5 * PURPOSE: Error Functions and Status/Exception Dispatching/Raising 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 #define TAG_ERR ' rrE' 16 17 /* GLOBALS ****************************************************************/ 18 19 BOOLEAN ExReadyForErrors = FALSE; 20 PVOID ExpDefaultErrorPort = NULL; 21 PEPROCESS ExpDefaultErrorPortProcess = NULL; 22 23 /* FUNCTIONS ****************************************************************/ 24 25 /*++ 26 * @name ExpSystemErrorHandler 27 * 28 * For now it's a stub 29 * 30 * @param ErrorStatus 31 * FILLME 32 * 33 * @param NumberOfParameters 34 * FILLME 35 * 36 * @param UnicodeStringParameterMask 37 * FILLME 38 * 39 * @param Parameters 40 * FILLME 41 * 42 * @param ValidResponseOptions 43 * FILLME 44 * 45 * @param Response 46 * FILLME 47 * 48 * @return None 49 * 50 * @remarks None 51 * 52 *--*/ 53 NTSTATUS 54 NTAPI 55 ExpSystemErrorHandler(IN NTSTATUS ErrorStatus, 56 IN ULONG NumberOfParameters, 57 IN ULONG UnicodeStringParameterMask, 58 IN PULONG_PTR Parameters, 59 IN BOOLEAN Shutdown) 60 { 61 ULONG_PTR BugCheckParameters[MAXIMUM_HARDERROR_PARAMETERS] = {0, 0, 0, 0}; 62 ULONG i; 63 64 /* Sanity check */ 65 ASSERT(NumberOfParameters <= MAXIMUM_HARDERROR_PARAMETERS); 66 67 /* 68 * KeBugCheck expects MAXIMUM_HARDERROR_PARAMETERS parameters, 69 * but we might get called with less, so use a local buffer here. 70 */ 71 for (i = 0; i < NumberOfParameters; i++) 72 { 73 /* Copy them over */ 74 BugCheckParameters[i] = Parameters[i]; 75 } 76 77 /* FIXME: STUB */ 78 KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR, 79 ErrorStatus, 80 (ULONG_PTR)BugCheckParameters, 81 0, 82 0); 83 return STATUS_SUCCESS; 84 } 85 86 /*++ 87 * @name ExpRaiseHardError 88 * 89 * For now it's a stub 90 * 91 * @param ErrorStatus 92 * FILLME 93 * 94 * @param NumberOfParameters 95 * FILLME 96 * 97 * @param UnicodeStringParameterMask 98 * FILLME 99 * 100 * @param Parameters 101 * FILLME 102 * 103 * @param ValidResponseOptions 104 * FILLME 105 * 106 * @param Response 107 * FILLME 108 * 109 * @return None 110 * 111 * @remarks None 112 * 113 *--*/ 114 NTSTATUS 115 NTAPI 116 ExpRaiseHardError(IN NTSTATUS ErrorStatus, 117 IN ULONG NumberOfParameters, 118 IN ULONG UnicodeStringParameterMask, 119 IN PULONG_PTR Parameters, 120 IN ULONG ValidResponseOptions, 121 OUT PULONG Response) 122 { 123 PEPROCESS Process = PsGetCurrentProcess(); 124 PETHREAD Thread = PsGetCurrentThread(); 125 UCHAR Buffer[PORT_MAXIMUM_MESSAGE_LENGTH]; 126 PHARDERROR_MSG Message = (PHARDERROR_MSG)Buffer; 127 NTSTATUS Status; 128 HANDLE PortHandle; 129 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 130 PAGED_CODE(); 131 132 /* Check if this error will shutdown the system */ 133 if (ValidResponseOptions == OptionShutdownSystem) 134 { 135 /* Check for privilege */ 136 if (!SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode)) 137 { 138 /* No rights */ 139 *Response = ResponseNotHandled; 140 return STATUS_PRIVILEGE_NOT_HELD; 141 } 142 143 /* Don't handle any new hard errors */ 144 ExReadyForErrors = FALSE; 145 } 146 147 /* Check if hard errors are not disabled */ 148 if (!Thread->HardErrorsAreDisabled) 149 { 150 /* Check if we can't do errors anymore, and this is serious */ 151 if ((!ExReadyForErrors) && (NT_ERROR(ErrorStatus))) 152 { 153 /* Use the system handler */ 154 ExpSystemErrorHandler(ErrorStatus, 155 NumberOfParameters, 156 UnicodeStringParameterMask, 157 Parameters, 158 (PreviousMode != KernelMode) ? TRUE: FALSE); 159 } 160 } 161 162 /* Enable hard error processing if it is enabled for the process 163 * or if the exception status forces it */ 164 if ((Process->DefaultHardErrorProcessing & 1) || 165 (ErrorStatus & 0x10000000)) 166 { 167 /* Check if we have an exception port */ 168 if (Process->ExceptionPort) 169 { 170 /* Use the port */ 171 PortHandle = Process->ExceptionPort; 172 } 173 else 174 { 175 /* Use our default system port */ 176 PortHandle = ExpDefaultErrorPort; 177 } 178 } 179 else 180 { 181 /* Don't process the error */ 182 PortHandle = NULL; 183 } 184 185 /* If hard errors are disabled, do nothing */ 186 if (Thread->HardErrorsAreDisabled) PortHandle = NULL; 187 188 /* Now check if we have a port */ 189 if (PortHandle) 190 { 191 /* Check if this is the default process */ 192 if (Process == ExpDefaultErrorPortProcess) 193 { 194 /* We can't handle the error, check if this is critical */ 195 if (NT_ERROR(ErrorStatus)) 196 { 197 /* It is, invoke the system handler */ 198 ExpSystemErrorHandler(ErrorStatus, 199 NumberOfParameters, 200 UnicodeStringParameterMask, 201 Parameters, 202 (PreviousMode != KernelMode) ? TRUE: FALSE); 203 204 /* If we survived, return to caller */ 205 *Response = ResponseReturnToCaller; 206 return STATUS_SUCCESS; 207 } 208 } 209 210 /* Setup the LPC Message */ 211 Message->h.u1.Length = (sizeof(HARDERROR_MSG) << 16) | 212 (sizeof(HARDERROR_MSG) - sizeof(PORT_MESSAGE)); 213 Message->h.u2.ZeroInit = 0; 214 Message->h.u2.s2.Type = LPC_ERROR_EVENT; 215 Message->Status = ErrorStatus &~ 0x10000000; 216 Message->ValidResponseOptions = ValidResponseOptions; 217 Message->UnicodeStringParameterMask = UnicodeStringParameterMask; 218 Message->NumberOfParameters = NumberOfParameters; 219 KeQuerySystemTime(&Message->ErrorTime); 220 221 /* Copy the parameters */ 222 if (Parameters) RtlMoveMemory(&Message->Parameters, 223 Parameters, 224 sizeof(ULONG_PTR) * NumberOfParameters); 225 226 /* Send the LPC Message */ 227 Status = LpcRequestWaitReplyPort(PortHandle, 228 (PVOID)Message, 229 (PVOID)Message); 230 if (NT_SUCCESS(Status)) 231 { 232 /* Check what kind of response we got */ 233 if ((Message->Response != ResponseReturnToCaller) && 234 (Message->Response != ResponseNotHandled) && 235 (Message->Response != ResponseAbort) && 236 (Message->Response != ResponseCancel) && 237 (Message->Response != ResponseIgnore) && 238 (Message->Response != ResponseNo) && 239 (Message->Response != ResponseOk) && 240 (Message->Response != ResponseRetry) && 241 (Message->Response != ResponseYes) && 242 (Message->Response != ResponseTryAgain) && 243 (Message->Response != ResponseContinue)) 244 { 245 /* Reset to a default one */ 246 Message->Response = ResponseReturnToCaller; 247 } 248 249 /* Set the response */ 250 *Response = Message->Response; 251 } 252 else 253 { 254 /* Set the response */ 255 *Response = ResponseReturnToCaller; 256 } 257 } 258 else 259 { 260 /* Set defaults */ 261 *Response = ResponseReturnToCaller; 262 Status = STATUS_SUCCESS; 263 } 264 265 /* Return status */ 266 return Status; 267 } 268 269 /*++ 270 * @name ExRaiseAccessViolation 271 * @implemented 272 * 273 * The ExRaiseAccessViolation routine can be used with structured exception 274 * handling to throw a driver-determined exception for a memory access 275 * violation that occurs when a driver processes I/O requests. 276 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true 277 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm 278 * 279 * @return None 280 * 281 * @remarks None 282 * 283 *--*/ 284 VOID 285 NTAPI 286 ExRaiseAccessViolation(VOID) 287 { 288 /* Raise the Right Status */ 289 RtlRaiseStatus(STATUS_ACCESS_VIOLATION); 290 } 291 292 /*++ 293 * @name ExRaiseDatatypeMisalignment 294 * @implemented 295 * 296 * ExRaiseDatatypeMisalignment raises an exception with the exception 297 * code set to STATUS_DATATYPE_MISALIGNMENT 298 * See: MSDN / DDK 299 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm 300 * 301 * @return None 302 * 303 * @remarks None 304 * 305 *--*/ 306 VOID 307 NTAPI 308 ExRaiseDatatypeMisalignment(VOID) 309 { 310 /* Raise the Right Status */ 311 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); 312 } 313 314 /*++ 315 * @name ExSystemExceptionFilter 316 * @implemented 317 * 318 * TODO: Add description 319 * 320 * @return FILLME 321 * 322 * @remarks None 323 * 324 *--*/ 325 LONG 326 NTAPI 327 ExSystemExceptionFilter(VOID) 328 { 329 return KeGetPreviousMode() != KernelMode ? 330 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; 331 } 332 333 /*++ 334 * @name ExRaiseHardError 335 * @implemented 336 * 337 * See NtRaiseHardError 338 * 339 * @param ErrorStatus 340 * Error Code 341 * 342 * @param NumberOfParameters 343 * Number of optional parameters in Parameters array 344 * 345 * @param UnicodeStringParameterMask 346 * Optional string parameter (can be only one per error code) 347 * 348 * @param Parameters 349 * Array of ULONG parameters for use in error message string 350 * 351 * @param ValidResponseOptions 352 * See HARDERROR_RESPONSE_OPTION for possible values description 353 * 354 * @param Response 355 * Pointer to HARDERROR_RESPONSE enumeration 356 * 357 * @return None 358 * 359 * @remarks None 360 * 361 *--*/ 362 NTSTATUS 363 NTAPI 364 ExRaiseHardError(IN NTSTATUS ErrorStatus, 365 IN ULONG NumberOfParameters, 366 IN ULONG UnicodeStringParameterMask, 367 IN PULONG_PTR Parameters, 368 IN ULONG ValidResponseOptions, 369 OUT PULONG Response) 370 { 371 SIZE_T Size; 372 UNICODE_STRING CapturedParams[MAXIMUM_HARDERROR_PARAMETERS]; 373 ULONG i; 374 PVOID UserData = NULL; 375 PHARDERROR_USER_PARAMETERS UserParams; 376 PWSTR BufferBase; 377 ULONG SafeResponse; 378 NTSTATUS Status; 379 PAGED_CODE(); 380 381 /* Check if we have parameters */ 382 if (Parameters) 383 { 384 /* Check if we have strings */ 385 if (UnicodeStringParameterMask) 386 { 387 /* Calculate the required size */ 388 Size = FIELD_OFFSET(HARDERROR_USER_PARAMETERS, Buffer[0]); 389 390 /* Loop each parameter */ 391 for (i = 0; i < NumberOfParameters; i++) 392 { 393 /* Check if it's part of the mask */ 394 if (UnicodeStringParameterMask & (1 << i)) 395 { 396 /* Copy it */ 397 RtlMoveMemory(&CapturedParams[i], 398 (PVOID)Parameters[i], 399 sizeof(UNICODE_STRING)); 400 401 /* Increase the size */ 402 Size += CapturedParams[i].MaximumLength; 403 } 404 } 405 406 /* Allocate the user data region */ 407 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 408 &UserData, 409 0, 410 &Size, 411 MEM_COMMIT, 412 PAGE_READWRITE); 413 if (!NT_SUCCESS(Status)) return Status; 414 415 /* Set the pointers to our data */ 416 UserParams = UserData; 417 BufferBase = UserParams->Buffer; 418 419 /* Loop parameters again */ 420 for (i = 0; i < NumberOfParameters; i++) 421 { 422 /* Check if we're in the mask */ 423 if (UnicodeStringParameterMask & (1 << i)) 424 { 425 /* Update the base */ 426 UserParams->Parameters[i] = (ULONG_PTR)&UserParams->Strings[i]; 427 428 /* Copy the string buffer */ 429 RtlMoveMemory(BufferBase, 430 CapturedParams[i].Buffer, 431 CapturedParams[i].MaximumLength); 432 433 /* Set buffer */ 434 CapturedParams[i].Buffer = BufferBase; 435 436 /* Copy the string structure */ 437 UserParams->Strings[i] = CapturedParams[i]; 438 439 /* Update the pointer */ 440 BufferBase += CapturedParams[i].MaximumLength; 441 } 442 else 443 { 444 /* No need to copy any strings */ 445 UserParams->Parameters[i] = Parameters[i]; 446 } 447 } 448 } 449 else 450 { 451 /* Just keep the data as is */ 452 UserData = Parameters; 453 } 454 } 455 456 /* Now call the worker function */ 457 Status = ExpRaiseHardError(ErrorStatus, 458 NumberOfParameters, 459 UnicodeStringParameterMask, 460 UserData, 461 ValidResponseOptions, 462 &SafeResponse); 463 464 /* Check if we had done user-mode allocation */ 465 if ((UserData) && (UserData != Parameters)) 466 { 467 /* We did! Delete it */ 468 Size = 0; 469 ZwFreeVirtualMemory(NtCurrentProcess(), 470 &UserData, 471 &Size, 472 MEM_RELEASE); 473 } 474 475 /* Return status and the response */ 476 *Response = SafeResponse; 477 return Status; 478 } 479 480 /*++ 481 * @name NtRaiseHardError 482 * @implemented 483 * 484 * This function sends HARDERROR_MSG LPC message to listener 485 * (typically CSRSS.EXE). See NtSetDefaultHardErrorPort for more information 486 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html 487 * 488 * @param ErrorStatus 489 * Error Code 490 * 491 * @param NumberOfParameters 492 * Number of optional parameters in Parameters array 493 * 494 * @param UnicodeStringParameterMask 495 * Optional string parameter (can be only one per error code) 496 * 497 * @param Parameters 498 * Array of ULONG_PTR parameters for use in error message string 499 * 500 * @param ValidResponseOptions 501 * See HARDERROR_RESPONSE_OPTION for possible values description 502 * 503 * @param Response 504 * Pointer to HARDERROR_RESPONSE enumeration 505 * 506 * @return Status 507 * 508 * @remarks NtRaiseHardError is easy way to display message in GUI 509 * without loading Win32 API libraries 510 * 511 *--*/ 512 NTSTATUS 513 NTAPI 514 NtRaiseHardError(IN NTSTATUS ErrorStatus, 515 IN ULONG NumberOfParameters, 516 IN ULONG UnicodeStringParameterMask, 517 IN PULONG_PTR Parameters, 518 IN ULONG ValidResponseOptions, 519 OUT PULONG Response) 520 { 521 NTSTATUS Status = STATUS_SUCCESS; 522 PULONG_PTR SafeParams = NULL; 523 ULONG SafeResponse; 524 UNICODE_STRING SafeString; 525 ULONG i; 526 ULONG ParamSize = 0; 527 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 528 529 /* Validate parameter count */ 530 if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) 531 { 532 /* Fail */ 533 return STATUS_INVALID_PARAMETER_2; 534 } 535 536 /* Make sure we have some at least */ 537 if ((Parameters != NULL) && (NumberOfParameters == 0)) 538 { 539 /* Fail */ 540 return STATUS_INVALID_PARAMETER_2; 541 } 542 543 /* Check if we were called from user-mode */ 544 if (PreviousMode != KernelMode) 545 { 546 /* First validate the responses */ 547 switch (ValidResponseOptions) 548 { 549 /* Check all valid cases */ 550 case OptionAbortRetryIgnore: 551 case OptionOk: 552 case OptionOkCancel: 553 case OptionRetryCancel: 554 case OptionYesNo: 555 case OptionYesNoCancel: 556 case OptionShutdownSystem: 557 break; 558 559 /* Anything else is invalid */ 560 default: 561 return STATUS_INVALID_PARAMETER_4; 562 } 563 564 /* Check if we have parameters */ 565 if (Parameters) 566 { 567 /* Calculate size of the parameters */ 568 ParamSize = sizeof(ULONG_PTR) * NumberOfParameters; 569 570 /* Allocate a safe buffer */ 571 SafeParams = ExAllocatePoolWithTag(PagedPool, ParamSize, TAG_ERR); 572 if (!SafeParams) 573 { 574 return STATUS_INSUFFICIENT_RESOURCES; 575 } 576 } 577 578 /* Enter SEH Block */ 579 _SEH2_TRY 580 { 581 /* Validate the response pointer */ 582 ProbeForWriteUlong(Response); 583 584 /* Check if we have parameters */ 585 if (Parameters) 586 { 587 /* Validate the parameter pointers */ 588 ProbeForRead(Parameters, ParamSize, sizeof(ULONG_PTR)); 589 590 /* Copy them */ 591 RtlCopyMemory(SafeParams, Parameters, ParamSize); 592 593 /* Now check if there's strings in it */ 594 if (UnicodeStringParameterMask) 595 { 596 /* Loop every string */ 597 for (i = 0; i < NumberOfParameters; i++) 598 { 599 /* Check if this parameter is a string */ 600 if (UnicodeStringParameterMask & (1 << i)) 601 { 602 /* Probe the structure */ 603 ProbeForRead((PVOID)SafeParams[i], 604 sizeof(UNICODE_STRING), 605 sizeof(ULONG_PTR)); 606 607 /* Capture it */ 608 RtlCopyMemory(&SafeString, 609 (PVOID)SafeParams[i], 610 sizeof(UNICODE_STRING)); 611 612 /* Probe the buffer */ 613 ProbeForRead(SafeString.Buffer, 614 SafeString.MaximumLength, 615 sizeof(UCHAR)); 616 } 617 } 618 } 619 } 620 } 621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 622 { 623 /* Free captured buffer */ 624 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR); 625 626 /* Return the exception code */ 627 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 628 } 629 _SEH2_END; 630 631 /* Call the system function directly, because we probed */ 632 ExpRaiseHardError(ErrorStatus, 633 NumberOfParameters, 634 UnicodeStringParameterMask, 635 SafeParams, 636 ValidResponseOptions, 637 &SafeResponse); 638 } 639 else 640 { 641 /* Reuse variable */ 642 SafeParams = Parameters; 643 644 /* 645 * Call the Executive Function. It will probe and copy pointers to 646 * user-mode 647 */ 648 ExRaiseHardError(ErrorStatus, 649 NumberOfParameters, 650 UnicodeStringParameterMask, 651 SafeParams, 652 ValidResponseOptions, 653 &SafeResponse); 654 } 655 656 /* Check if we were called in user-mode */ 657 if (PreviousMode != KernelMode) 658 { 659 /* That means we have a buffer to free */ 660 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR); 661 662 /* Enter SEH Block for return */ 663 _SEH2_TRY 664 { 665 /* Return the response */ 666 *Response = SafeResponse; 667 } 668 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 669 { 670 /* Get the exception code */ 671 Status = _SEH2_GetExceptionCode(); 672 } 673 _SEH2_END; 674 } 675 else 676 { 677 /* Return the response */ 678 *Response = SafeResponse; 679 } 680 681 /* Return status */ 682 return Status; 683 } 684 685 /*++ 686 * @name NtSetDefaultHardErrorPort 687 * @implemented 688 * 689 * NtSetDefaultHardErrorPort is typically called only once. After call, 690 * kernel set BOOLEAN flag named _ExReadyForErrors to TRUE, and all other 691 * tries to change default port are broken with STATUS_UNSUCCESSFUL error code 692 * See: http://www.windowsitlibrary.com/Content/356/08/2.html 693 * http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html 694 * 695 * @param PortHandle 696 * Handle to named port object 697 * 698 * @return Status 699 * 700 * @remarks Privileges: SE_TCB_PRIVILEGE 701 * 702 *--*/ 703 NTSTATUS 704 NTAPI 705 NtSetDefaultHardErrorPort(IN HANDLE PortHandle) 706 { 707 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 708 NTSTATUS Status = STATUS_UNSUCCESSFUL; 709 710 /* Check if we have the Privilege */ 711 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) 712 { 713 DPRINT1("NtSetDefaultHardErrorPort: Caller requires " 714 "the SeTcbPrivilege privilege!\n"); 715 return STATUS_PRIVILEGE_NOT_HELD; 716 } 717 718 /* Only called once during bootup, make sure we weren't called yet */ 719 if (!ExReadyForErrors) 720 { 721 /* Reference the port */ 722 Status = ObReferenceObjectByHandle(PortHandle, 723 0, 724 LpcPortObjectType, 725 PreviousMode, 726 (PVOID*)&ExpDefaultErrorPort, 727 NULL); 728 if (NT_SUCCESS(Status)) 729 { 730 /* Save the data */ 731 ExpDefaultErrorPortProcess = PsGetCurrentProcess(); 732 ExReadyForErrors = TRUE; 733 } 734 } 735 736 /* Return status to caller */ 737 return Status; 738 } 739 740 VOID 741 __cdecl 742 _purecall(VOID) 743 { 744 /* Not supported in Kernel Mode */ 745 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED); 746 } 747 748 /* EOF */ 749