/*++ 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" extern ULONG KiBugCheckData[5]; extern ULONG KeBugCheckCount; 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; BOOLEAN ExReadyForErrors = FALSE; BOOLEAN ExpTooLateForErrors = FALSE; HANDLE ExpDefaultErrorPort; extern PVOID PsSystemDllDllBase; 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; PSZ ErrorFormatString; ANSI_STRING Astr; UNICODE_STRING Ustr; OEM_STRING Ostr; PSZ OemCaption; PSZ OemMessage; PSZ UnknownHardError = "Unknown Hard Error"; PVOID UnlockHandle; CONTEXT ContextSave; // 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)); for(Counter=0;Counter < NumberOfParameters;Counter++){ ParameterVector[Counter] = Parameters[Counter]; } for(Counter=0;Counter < NumberOfParameters;Counter++){ if ( UnicodeStringParameterMask & 1<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) { Astr.MaximumLength = Astr.Length + 16; Astr.Buffer = ErrorCaption; Status = RtlUnicodeStringToAnsiString(&Astr, &Ustr, FALSE); if ( !NT_SUCCESS(Status) ) { ExFreePool(ErrorCaption); ErrorCaption = ErrorFormatString = UnknownHardError; } } else { ErrorCaption = ErrorFormatString = UnknownHardError; } } else { ErrorCaption = ExAllocatePoolWithTag(NonPagedPool,strlen(MessageEntry->Text)+16, ' rrE'); if ( ErrorCaption ) { strcpy(ErrorCaption,MessageEntry->Text); } else { ErrorCaption = ErrorFormatString = 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 = strlen(ErrorCaption); while ( Counter && *ErrorFormatString >= ' ' ) { ErrorFormatString++; Counter--; } *ErrorFormatString++ = '\0'; Counter--; while ( Counter && *ErrorFormatString && *ErrorFormatString <= ' ') { ErrorFormatString++; Counter--; } } if (!Counter) { // Oops - Bad Format String. ErrorFormatString = ""; } } } except ( EXCEPTION_EXECUTE_HANDLER ) { ErrorFormatString = UnknownHardError; ErrorCaption = UnknownHardError; } } else { ErrorFormatString = DefaultFormatBuffer; ErrorCaption = UnknownHardError; } try { _snprintf( ExpSystemErrorBuffer, sizeof( ExpSystemErrorBuffer ), "\nSTOP: %lx %s\n", ErrorStatus,ErrorCaption); } except(EXCEPTION_EXECUTE_HANDLER) { _snprintf( ExpSystemErrorBuffer, sizeof( ExpSystemErrorBuffer ), "\nHardError %lx\n", ErrorStatus); } UnlockHandle = MmLockPagableCodeSection((PVOID)ExpSystemErrorHandler); ASSERT(UnlockHandle); // take the caption and convert it to OEM OemCaption = UnknownHardError; OemMessage = 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) { 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 ), 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( FALSE, 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 ); } } NTSTATUS ExpRaiseHardError( IN NTSTATUS ErrorStatus, IN ULONG NumberOfParameters, IN ULONG UnicodeStringParameterMask, IN PULONG_PTR Parameters, IN ULONG ValidResponseOptions, OUT PULONG Response ) { PEPROCESS Process; ULONG_PTR MessageBuffer[PORT_MAXIMUM_MESSAGE_LENGTH/sizeof(ULONG_PTR)]; PHARDERROR_MSG m; NTSTATUS Status; HANDLE ErrorPort; KPROCESSOR_MODE PreviousMode; PAGED_CODE(); m = (PHARDERROR_MSG)&MessageBuffer[0]; PreviousMode = KeGetPreviousMode(); 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; } 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 ( !(PsGetCurrentThread()->HardErrorsAreDisabled) ) { if (ExReadyForErrors == FALSE && NT_ERROR(ErrorStatus)){ 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. 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 { ErrorPort = NULL; } } } 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; } else { ErrorPort = NULL; } ErrorPort = NULL; } } if ( PsGetCurrentThread()->HardErrorsAreDisabled ) { ErrorPort = NULL; } if ( !IS_SYSTEM_THREAD(PsGetCurrentThread()) ) { try { PTEB Teb; Teb = (PTEB)PsGetCurrentThread()->Tcb.Teb; if ( Teb->HardErrorsAreDisabled ) { ErrorPort = NULL; } } except (EXCEPTION_EXECUTE_HANDLER) { ; } } if ( ErrorPort ) { 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 ) { RtlMoveMemory(&m->Parameters,Parameters, sizeof(ULONG_PTR)*NumberOfParameters); } KeQuerySystemTime(&m->ErrorTime); Status = LpcRequestWaitReplyPort(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; } } else { *Response = (ULONG)ResponseReturnToCaller; Status = STATUS_SUCCESS; } 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; PULONG_PTR CapturedParameters; 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; } CapturedParameters = NULL; try { ProbeForWriteUlong(Response); if ( ARGUMENT_PRESENT(Parameters) ) { ProbeForRead( Parameters, sizeof(ULONG_PTR)*NumberOfParameters, sizeof(ULONG_PTR) ); CapturedParameters = ExAllocatePoolWithTag(PagedPool,sizeof(ULONG_PTR)*NumberOfParameters, ' rrE'); if ( !CapturedParameters ) { return STATUS_NO_MEMORY; } RtlMoveMemory(CapturedParameters,Parameters,sizeof(ULONG_PTR)*NumberOfParameters); // probe all strings if ( UnicodeStringParameterMask ) { for(Counter=0;Counter < NumberOfParameters;Counter++){ // if there is a string in this position, // then probe and capture the string if ( UnicodeStringParameterMask & (1<