/*++ Copyright (c) 1991 Microsoft Corporation Module Name: harderr.c Abstract: This module implements NT Hard Error APIs Author: Mark Lucovsky (markl) 04-Jul-1991 Revision History: --*/ #include "exp.h" NTSTATUS ExpRaiseHardError ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, IN PULONG_PTR Parameters, IN ULONG ValidResponseOptions, OUT PULONG Response ); VOID ExpSystemErrorHandler ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, IN PULONG_PTR Parameters, IN BOOLEAN CallShutdown ); #if defined(ALLOC_PRAGMA) #pragma alloc_text(PAGE, NtRaiseHardError) #pragma alloc_text(PAGE, NtSetDefaultHardErrorPort) #pragma alloc_text(PAGE, ExRaiseHardError) #pragma alloc_text(PAGE, ExpRaiseHardError) #pragma alloc_text(PAGELK, ExpSystemErrorHandler) #endif #define HARDERROR_MSG_OVERHEAD (sizeof(HARDERROR_MSG) - sizeof(PORT_MESSAGE)) #define HARDERROR_API_MSG_LENGTH \ sizeof(HARDERROR_MSG)<<16 | (HARDERROR_MSG_OVERHEAD) PEPROCESS ExpDefaultErrorPortProcess; #define STARTING 0 #define STARTED 1 #define SHUTDOWN 2 BOOLEAN ExReadyForErrors = FALSE; ULONG HardErrorState = STARTING; BOOLEAN ExpTooLateForErrors = FALSE; HANDLE ExpDefaultErrorPort; extern PVOID PsSystemDllDllBase; #ifdef _X86_ #pragma optimize("y", off) // RtlCaptureContext needs EBP to be correct #endif VOID ExpSystemErrorHandler ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, IN PULONG_PTR Parameters, IN BOOLEAN CallShutdown ) { ULONG Counter; ANSI_STRING AnsiString; NTSTATUS Status; ULONG_PTR ParameterVector[MAXIMUM_HARDERROR_PARAMETERS]; CHAR DefaultFormatBuffer[32]; CHAR ExpSystemErrorBuffer[256]; PMESSAGE_RESOURCE_ENTRY MessageEntry; PSZ ErrorCaption; CHAR const* ErrorFormatString; ANSI_STRING Astr; UNICODE_STRING Ustr; OEM_STRING Ostr; PSZ OemCaption; PSZ OemMessage; static char const* UnknownHardError = "Unknown Hard Error"; CONTEXT ContextSave; PAGED_CODE(); // // This handler is called whenever a hard error occurs before the // default handler has been installed. // // This is done regardless of whether or not the process has chosen // default hard error processing. // // // Capture the callers context as closely as possible into the debugger's // processor state area of the Prcb // // N.B. There may be some prologue code that shuffles registers such that // they get destroyed. // // This code is here only for crash dumps. // RtlCaptureContext (&KeGetCurrentPrcb()->ProcessorState.ContextFrame); KiSaveProcessorControlState (&KeGetCurrentPrcb()->ProcessorState); ContextSave = KeGetCurrentPrcb()->ProcessorState.ContextFrame; DefaultFormatBuffer[0] = '\0'; RtlZeroMemory (ParameterVector, sizeof(ParameterVector)); RtlCopyMemory (ParameterVector, Parameters, NumberOfParameters * sizeof (ULONG_PTR)); for (Counter = 0; Counter < NumberOfParameters; Counter += 1) { if (UnicodeStringParameterMask & 1 << Counter) { strcat(DefaultFormatBuffer," %s"); Status = RtlUnicodeStringToAnsiString (&AnsiString, (PUNICODE_STRING)Parameters[Counter], TRUE); if (NT_SUCCESS (Status)) { ParameterVector[Counter] = (ULONG_PTR)AnsiString.Buffer; } else { ParameterVector[Counter] = (ULONG_PTR)L"???"; } } else { strcat(DefaultFormatBuffer," %x"); } } strcat(DefaultFormatBuffer,"\n"); ErrorFormatString = (char const *)DefaultFormatBuffer; ErrorCaption = (PSZ) UnknownHardError; // // HELP where do I get the resource from ! // if (PsSystemDllDllBase != NULL) { try { // // If we are on a DBCS code page, we have to use ENGLISH resource // instead of default resource because HalDisplayString() can only // display ASCII characters on the blue screen. // Status = RtlFindMessage (PsSystemDllDllBase, 11, NlsMbCodePageTag ? MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) : 0, ErrorStatus, &MessageEntry); if (!NT_SUCCESS(Status)) { ErrorCaption = (PSZ) UnknownHardError; ErrorFormatString = (char const *)UnknownHardError; } else { if (MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE) { // // Message resource is Unicode. Convert to ANSI. // RtlInitUnicodeString (&Ustr, (PCWSTR)MessageEntry->Text); Astr.Length = (USHORT) RtlUnicodeStringToAnsiSize (&Ustr); ErrorCaption = ExAllocatePoolWithTag (NonPagedPool, Astr.Length+16, ' rrE'); if (ErrorCaption != NULL) { Astr.MaximumLength = Astr.Length + 16; Astr.Buffer = ErrorCaption; Status = RtlUnicodeStringToAnsiString(&Astr, &Ustr, FALSE); if (!NT_SUCCESS(Status)) { ExFreePool(ErrorCaption); ErrorCaption = (PSZ) UnknownHardError; ErrorFormatString = (char const *)UnknownHardError; } } else { ErrorCaption = (PSZ) UnknownHardError; ErrorFormatString = (char const *) UnknownHardError; } } else { ErrorCaption = ExAllocatePoolWithTag(NonPagedPool, strlen((PCHAR)MessageEntry->Text)+16, ' rrE'); if (ErrorCaption != NULL) { strcpy(ErrorCaption,(PCHAR)MessageEntry->Text); } else { ErrorFormatString = (char const *)UnknownHardError; ErrorCaption = (PSZ) UnknownHardError; } } if (ErrorCaption != UnknownHardError) { // // It's assumed the Error String from the message table // is in the format: // // {ErrorCaption}\r\n\0ErrorFormatString\0. // // Parse out the caption. // ErrorFormatString = ErrorCaption; Counter = (ULONG) strlen(ErrorCaption); while (Counter && *ErrorFormatString >= ' ') { ErrorFormatString += 1; Counter -= 1; } *(char*)ErrorFormatString++ = '\0'; Counter -= 1; while (Counter && *ErrorFormatString && *ErrorFormatString <= ' ') { ErrorFormatString += 1; Counter -= 1; } } if (!Counter) { // Oops - Bad Format String. ErrorFormatString = (char const *)""; } } } except (EXCEPTION_EXECUTE_HANDLER) { ErrorFormatString = (char const *)UnknownHardError; ErrorCaption = (PSZ) UnknownHardError; } } try { _snprintf (ExpSystemErrorBuffer, sizeof (ExpSystemErrorBuffer), "\nSTOP: %lx %s\n", ErrorStatus, ErrorCaption); } except(EXCEPTION_EXECUTE_HANDLER) { _snprintf (ExpSystemErrorBuffer, sizeof (ExpSystemErrorBuffer), "\nHardError %lx\n", ErrorStatus); } ASSERT(ExPageLockHandle); MmLockPagableSectionByHandle(ExPageLockHandle); // // Take the caption and convert it to OEM. // OemCaption = (PSZ) UnknownHardError; OemMessage = (PSZ) UnknownHardError; RtlInitAnsiString (&Astr, ExpSystemErrorBuffer); Status = RtlAnsiStringToUnicodeString (&Ustr, &Astr, TRUE); if (!NT_SUCCESS(Status)) { goto punt1; } // // Allocate the OEM string out of nonpaged pool so that bugcheck // can read it. // Ostr.Length = (USHORT)RtlUnicodeStringToOemSize(&Ustr); Ostr.MaximumLength = Ostr.Length; Ostr.Buffer = ExAllocatePoolWithTag(NonPagedPool, Ostr.Length, ' rrE'); OemCaption = Ostr.Buffer; if (Ostr.Buffer != NULL) { Status = RtlUnicodeStringToOemString (&Ostr, &Ustr, FALSE); if (!NT_SUCCESS(Status)) { goto punt1; } } // // Can't do much of anything after calling HalDisplayString... // punt1: try { _snprintf (ExpSystemErrorBuffer, sizeof (ExpSystemErrorBuffer), (const char *)ErrorFormatString, ParameterVector[0], ParameterVector[1], ParameterVector[2], ParameterVector[3]); } except(EXCEPTION_EXECUTE_HANDLER) { _snprintf (ExpSystemErrorBuffer, sizeof (ExpSystemErrorBuffer), "Exception Processing Message %lx Parameters %lx %lx %lx %lx", ErrorStatus, ParameterVector[0], ParameterVector[1], ParameterVector[2], ParameterVector[3]); } RtlInitAnsiString (&Astr, ExpSystemErrorBuffer); Status = RtlAnsiStringToUnicodeString (&Ustr, &Astr, TRUE); if (!NT_SUCCESS(Status)) { goto punt2; } // // Allocate the OEM string out of nonpaged pool so that bugcheck // can read it. // Ostr.Length = (USHORT) RtlUnicodeStringToOemSize (&Ustr); Ostr.MaximumLength = Ostr.Length; Ostr.Buffer = ExAllocatePoolWithTag (NonPagedPool, Ostr.Length, ' rrE'); OemMessage = Ostr.Buffer; if (Ostr.Buffer) { Status = RtlUnicodeStringToOemString (&Ostr, &Ustr, FALSE); if (!NT_SUCCESS(Status)) { goto punt2; } } punt2: ASSERT (sizeof(PVOID) == sizeof(ULONG_PTR)); ASSERT (sizeof(ULONG) == sizeof(NTSTATUS)); // // We don't come back from here. // if (CallShutdown) { PoShutdownBugCheck (TRUE, FATAL_UNHANDLED_HARD_ERROR, (ULONG)ErrorStatus, (ULONG_PTR)&(ParameterVector[0]), (ULONG_PTR)OemCaption, (ULONG_PTR)OemMessage); } else { KeBugCheckEx (FATAL_UNHANDLED_HARD_ERROR, (ULONG)ErrorStatus, (ULONG_PTR)&(ParameterVector[0]), (ULONG_PTR)OemCaption, (ULONG_PTR)OemMessage); } } #ifdef _X86_ #pragma optimize("", on) #endif NTSTATUS ExpRaiseHardError ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, IN PULONG_PTR Parameters, IN ULONG ValidResponseOptions, OUT PULONG Response ) { PTEB Teb; PETHREAD Thread; PEPROCESS Process; ULONG_PTR MessageBuffer[PORT_MAXIMUM_MESSAGE_LENGTH/sizeof(ULONG_PTR)]; PHARDERROR_MSG m; NTSTATUS Status; HANDLE ErrorPort; KPROCESSOR_MODE PreviousMode; BOOLEAN DoingShutdown; PAGED_CODE(); m = (PHARDERROR_MSG)&MessageBuffer[0]; PreviousMode = KeGetPreviousMode(); DoingShutdown = FALSE; if (ValidResponseOptions == OptionShutdownSystem) { // // Check to see if the caller has the privilege to make this call. // if (!SeSinglePrivilegeCheck (SeShutdownPrivilege, PreviousMode)) { return STATUS_PRIVILEGE_NOT_HELD; } ExReadyForErrors = FALSE; HardErrorState = SHUTDOWN; DoingShutdown = TRUE; } Thread = PsGetCurrentThread(); Process = PsGetCurrentProcess(); // // If the default handler is not installed, then // call the fatal hard error handler if the error // status is error // // Let GDI override this since it does not want to crash the machine // when a bad driver was loaded via MmLoadSystemImage. // if ((Thread->CrossThreadFlags & PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED) == 0) { if (NT_ERROR(ErrorStatus) && (HardErrorState == STARTING || DoingShutdown)) { ExpSystemErrorHandler ( ErrorStatus, NumberOfParameters, UnicodeStringParameterMask, Parameters, (BOOLEAN)((PreviousMode != KernelMode) ? TRUE : FALSE)); } } // // If the process has an error port, then if it wants default // handling, use its port. If it disabled default handling, then // return the error to the caller. If the process does not // have a port, then use the registered default handler. // ErrorPort = NULL; if (Process->ExceptionPort) { if (Process->DefaultHardErrorProcessing & 1) { ErrorPort = Process->ExceptionPort; } else { // // If error processing is disabled, check the error override // status. // if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) { ErrorPort = Process->ExceptionPort; } } } else { if (Process->DefaultHardErrorProcessing & 1) { ErrorPort = ExpDefaultErrorPort; } else { // // If error processing is disabled, check the error override // status. // if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) { ErrorPort = ExpDefaultErrorPort; } } } if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED) != 0) { ErrorPort = NULL; } if ((ErrorPort != NULL) && (!IS_SYSTEM_THREAD(Thread))) { Teb = (PTEB)PsGetCurrentThread()->Tcb.Teb; try { if (Teb->HardErrorMode & RTL_ERRORMODE_FAILCRITICALERRORS) { ErrorPort = NULL; } } except (EXCEPTION_EXECUTE_HANDLER) { ; } } if (ErrorPort == NULL) { *Response = (ULONG)ResponseReturnToCaller; return STATUS_SUCCESS; } if (Process == ExpDefaultErrorPortProcess) { if (NT_ERROR(ErrorStatus)) { ExpSystemErrorHandler (ErrorStatus, NumberOfParameters, UnicodeStringParameterMask, Parameters, (BOOLEAN)((PreviousMode != KernelMode) ? TRUE : FALSE)); } *Response = (ULONG)ResponseReturnToCaller; Status = STATUS_SUCCESS; return Status; } m->h.u1.Length = HARDERROR_API_MSG_LENGTH; m->h.u2.ZeroInit = LPC_ERROR_EVENT; m->Status = ErrorStatus & ~HARDERROR_OVERRIDE_ERRORMODE; m->ValidResponseOptions = ValidResponseOptions; m->UnicodeStringParameterMask = UnicodeStringParameterMask; m->NumberOfParameters = NumberOfParameters; if (Parameters != NULL) { try { RtlCopyMemory (&m->Parameters, Parameters, sizeof(ULONG_PTR)*NumberOfParameters); } except (EXCEPTION_EXECUTE_HANDLER) { } } KeQuerySystemTime(&m->ErrorTime); Status = LpcRequestWaitReplyPortEx (ErrorPort, (PPORT_MESSAGE) m, (PPORT_MESSAGE) m); if (NT_SUCCESS(Status)) { switch (m->Response) { case ResponseReturnToCaller : case ResponseNotHandled : case ResponseAbort : case ResponseCancel : case ResponseIgnore : case ResponseNo : case ResponseOk : case ResponseRetry : case ResponseYes : case ResponseTryAgain : case ResponseContinue : break; default: m->Response = (ULONG)ResponseReturnToCaller; break; } *Response = m->Response; } return Status; } NTSTATUS NtRaiseHardError ( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, IN PULONG_PTR Parameters, IN ULONG ValidResponseOptions, OUT PULONG Response ) { NTSTATUS Status; ULONG_PTR CapturedParameters[MAXIMUM_HARDERROR_PARAMETERS]; KPROCESSOR_MODE PreviousMode; ULONG LocalResponse; UNICODE_STRING CapturedString; ULONG Counter; PAGED_CODE(); if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) { return STATUS_INVALID_PARAMETER_2; } if (ARGUMENT_PRESENT(Parameters) && NumberOfParameters == 0) { return STATUS_INVALID_PARAMETER_2; } PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { switch (ValidResponseOptions) { case OptionAbortRetryIgnore : case OptionOk : case OptionOkCancel : case OptionRetryCancel : case OptionYesNo : case OptionYesNoCancel : case OptionShutdownSystem : case OptionOkNoWait : case OptionCancelTryContinue: break; default : return STATUS_INVALID_PARAMETER_4; } try { ProbeForWriteUlong(Response); if (ARGUMENT_PRESENT(Parameters)) { ProbeForRead (Parameters, sizeof(ULONG_PTR)*NumberOfParameters, sizeof(ULONG_PTR)); RtlCopyMemory (CapturedParameters, Parameters, sizeof(ULONG_PTR)*NumberOfParameters); // // Probe all strings. // if (UnicodeStringParameterMask) { for (Counter = 0;Counter < NumberOfParameters; Counter += 1) { // // if there is a string in this position, // then probe and capture the string // if (UnicodeStringParameterMask & (1<