/*++ Copyright (c) 2000 Microsoft Corporation Module Name: vfbugcheck.c Abstract: This module implements support for verifier bugchecks. Author: Adrian J. Oney (adriao) 20-Apr-1998 Environment: Kernel mode Revision History: AdriaO 02/21/2000 - Moved from ntos\io\ioassert.c --*/ // // Disable W4 level warnings generated by public headers. // #include "vfpragma.h" #include "..\io\iop.h" // Includes vfdef.h #include "vibugcheck.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, VfBugcheckInit) #pragma alloc_text(PAGEVRFY, VfBugcheckThrowIoException) #pragma alloc_text(PAGEVRFY, VfBugcheckThrowException) #pragma alloc_text(PAGEVRFY, ViBucheckProcessParams) #pragma alloc_text(PAGEVRFY, ViBugcheckProcessMessageText) #pragma alloc_text(PAGEVRFY, ViBugcheckApplyControl) #pragma alloc_text(PAGEVRFY, ViBugcheckHalt) #pragma alloc_text(PAGEVRFY, ViBugcheckPrintBuffer) #pragma alloc_text(PAGEVRFY, ViBugcheckPrintParamData) #pragma alloc_text(PAGEVRFY, ViBugcheckPrintUrl) #pragma alloc_text(PAGEVRFY, ViBugcheckPrompt) #endif // ALLOC_PRAGMA #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("PAGEVRFD") #endif ULONG ViBugCheckInitialControl; ULONG ViBugCheckControlOverride; UNICODE_STRING ViBugCheckEmptyString; #ifdef ALLOC_DATA_PRAGMA #pragma const_seg("PAGEVRFC") #endif // // When invoking the driver check macro's, pass Irps first, Routines second, // DevObj's third, and any Status's last... // const DCPARAM_TYPE_ENTRY ViBugCheckParamTable[] = { { DCPARAM_ROUTINE, "Routine" }, { DCPARAM_IRP, "Irp" }, { DCPARAM_IRPSNAP, "Snapshot" }, { DCPARAM_DEVOBJ, "DevObj" }, { DCPARAM_STATUS, "Status" }, { DCPARAM_ULONG, "Ulong" }, { DCPARAM_PVOID, "Pvoid" } }; VOID FASTCALL VfBugcheckInit( VOID ) /*++ Routine Description: This routine initializes the verifier bugcheck support routines. Arguments: None. Return Value: None. --*/ { ViBugCheckInitialControl = 0; ViBugCheckControlOverride = 0; RtlInitUnicodeString(&ViBugCheckEmptyString, NULL); } NTSTATUS VfBugcheckThrowIoException( IN DCERROR_ID MessageID, IN ULONG MessageParameterMask, ... ) /*++ Description: This routine processes an assert and provides options for removing the breakpoint, changing to just a text-out, etc. DCPARAM_IRP*(count)+DCPARAM_ROUTINE*(count)+DCPARAM_DEVOBJ*(count), irp1, irp2, irp3, routine1, .., .., devobj1, count can be a max of 3. Notes: The text will automagically be formatted and printed as such: ASSERTION CLASS: ASSERTION TEXT ASSERTION TEXT ASSERTION TEXT ASSERTION TEXT ... --*/ { PVFMESSAGE_TEMPLATE_TABLE ioVerifierTable; UCHAR paramFormat[9*3*ARRAY_COUNT(ViBugCheckParamTable)+1]; ULONG paramType, paramMask, curMask; NTSTATUS status; va_list arglist; curMask = MessageParameterMask; paramFormat[0] = '\0'; for(paramType=0; paramType= '1') && (format[0] <= '3')) { i = format[0] - '1'; format++; } else { i = paramIndices[paramType]; ASSERT(i < 3); } if (i < 3) { // // Param number is within bounds. // DcParamArray[paramType*3+i] = va_arg(*MessageParameters, PVOID); } // // Update the current parameter index for the given type. // paramIndices[paramType] = i+1; // // Get out early // break; } } if (paramType == ARRAY_COUNT(ViBugCheckParamTable)) { // // We could'nt find an entry matching the format text. Bail. // ASSERT(paramType != ARRAY_COUNT(ViBugCheckParamTable)); break; } } // // Pre-init unhelpful answers... // DcCheckData->DriverName = &ViBugCheckEmptyString; DcCheckData->OffsetIntoImage = 0; DcCheckData->InVerifierList = FALSE; culpritAddress = DcParamArray[0]; // // Extract the culprit's name if possible... // if (culpritAddress) { status = KevUtilAddressToFileHeader( (PVOID) culpritAddress, (PUINT_PTR)(&DcCheckData->OffsetIntoImage), &DcCheckData->DriverName, &DcCheckData->InVerifierList ); if (!NT_SUCCESS(status)) { // // IF we don't know who it is, assert anyway. // DcCheckData->InVerifierList = TRUE; } } // // Record // DcCheckData->CulpritAddress = culpritAddress; DcCheckData->DcParamArray = DcParamArray; DcCheckData->MessageID = MessageID; // // Get an ANSI version of the driver name. // KeBugCheckUnicodeToAnsi( DcCheckData->DriverName, ansiDriverName, sizeof(ansiDriverName) ); // // Retrieve a pointer to the appropriate message data. // VfMessageRetrieveErrorData( MessageTable, MessageID, ansiDriverName, &DcCheckData->BugCheckMajor, &DcCheckData->AssertionClass, &DcCheckData->MessageTextTemplate, &DcCheckData->Control ); } NTSTATUS FASTCALL ViBugcheckProcessMessageText( IN ULONG MaxOutputBufferSize, OUT PSTR OutputBuffer, IN OUT PDC_CHECK_DATA DcCheckData ) { ULONG paramType, maxParameterTypes; ULONG arrayIndex, paramLength; char const* messageHead; PSTR newMessage; LONG charsRemaining, length; // // Get the message text. // messageHead = DcCheckData->MessageTextTemplate; // // Now manually build out the message. // newMessage = OutputBuffer; charsRemaining = (MaxOutputBufferSize/sizeof(UCHAR))-1; maxParameterTypes = ARRAY_COUNT(ViBugCheckParamTable); while(*messageHead != '\0') { if (charsRemaining <= 0) { return STATUS_BUFFER_OVERFLOW; } if (*messageHead != '%') { *newMessage = *messageHead; newMessage++; messageHead++; charsRemaining--; } else { for(paramType = 0; paramType < maxParameterTypes; paramType++) { paramLength = (ULONG)strlen(ViBugCheckParamTable[paramType].DcParamName); // // Do we have a match? // // N.B. - We don't do any case 'de-sensitizing' anywhere, so // everything's cases must match! // if (RtlCompareMemory( messageHead+1, ViBugCheckParamTable[paramType].DcParamName, paramLength*sizeof(UCHAR)) == paramLength*sizeof(UCHAR)) { arrayIndex = paramType*3; messageHead += (paramLength+1); // // Was an index passed in (ie, "3rd" irp requested)? // if ((*messageHead >= '1') && (*messageHead <= '3')) { // // Adjust table index appropriately. // arrayIndex += (*messageHead - '1') ; messageHead++; } if ((arrayIndex < 6) || (arrayIndex >=9)) { // // Normal param, print the pointer // length = _snprintf( newMessage, charsRemaining+1, "%p", DcCheckData->DcParamArray[arrayIndex] ); } else { // // IRP Snapshot, extract the IRP and print that // length = _snprintf( newMessage, charsRemaining+1, "%p", ((PIRP_MINI_SNAPSHOT) DcCheckData->DcParamArray[arrayIndex])->Irp ); } if (length == -1) { return STATUS_BUFFER_OVERFLOW; } charsRemaining -= length; newMessage += length; break; } } if (paramType == maxParameterTypes) { // // Either the message we looked up is malformed, we don't recognize // the %thing it is talking about, or this is %%! // *newMessage = *messageHead; messageHead++; newMessage++; charsRemaining--; if (*messageHead == '%') { messageHead++; } } } } // // Null-terminate it (we have room because we took one off the buffer size // above). // *newMessage = '\0'; DcCheckData->ClassText = DcCheckData->AssertionClass->MessageClassText; DcCheckData->AssertionText = OutputBuffer; return STATUS_SUCCESS; } BOOLEAN FASTCALL ViBugcheckApplyControl( IN OUT PDC_CHECK_DATA DcCheckData ) { ULONG assertionControl; if (ViBugCheckControlOverride) { assertionControl = ViBugCheckControlOverride; } else if (DcCheckData->Control) { // // Initialize the control if appropo // if (!((*DcCheckData->Control) & VFM_FLAG_INITIALIZED)) { *DcCheckData->Control |= ( VFM_FLAG_INITIALIZED | ViBugCheckInitialControl | DcCheckData->AssertionClass->ClassFlags ); } assertionControl = *DcCheckData->Control; } else { assertionControl = ( ViBugCheckInitialControl | DcCheckData->AssertionClass->ClassFlags ); } if (assertionControl & VFM_FLAG_CLEARED) { // // If the breakpoint was cleared, then return, print/rip not. // return FALSE; } if ((!(assertionControl & VFM_IGNORE_DRIVER_LIST)) && (!DcCheckData->InVerifierList)) { // // Not of interest, skip this one. // return FALSE; } // // If there is no debugger, don't halt the machine. We are probably // ripping like mad and the user just wants to be able to boot. // The one exception is if VFM_DEPLOYMENT_FAILURE is set. Then we shall // invoke the driver bugcheck... // if ((!KdDebuggerEnabled) && (!(assertionControl & VFM_DEPLOYMENT_FAILURE))) { return FALSE; } // // Record our intentions and continue. // DcCheckData->AssertionControl = assertionControl; return TRUE; } VOID FASTCALL ViBugcheckHalt( IN PDC_CHECK_DATA DcCheckData ) { PVOID parameterArray[4]; char captionBuffer[256]; char ansiDriverName[81]; // // Do not bugcheck if a kernel debugger is attached, nor if this isn't a // fatal error. // if (KdDebuggerEnabled || (!(DcCheckData->AssertionControl & VFM_DEPLOYMENT_FAILURE))) { return; } // // We are here because VFM_DEPLOYMENT_FAILURE is set. We use // FATAL_UNHANDLED_HARD_ERROR so that we can give a // descriptive text string for the problem. // parameterArray[0] = (PVOID)(ULONG_PTR)(DcCheckData->MessageID); parameterArray[1] = DcCheckData->CulpritAddress; parameterArray[2] = DcCheckData->DcParamArray[3]; parameterArray[3] = DcCheckData->DcParamArray[9]; if (DcCheckData->BugCheckMajor == DRIVER_VERIFIER_IOMANAGER_VIOLATION) { KeBugCheckUnicodeToAnsi( DcCheckData->DriverName, ansiDriverName, sizeof(ansiDriverName) ); _snprintf( captionBuffer, sizeof(captionBuffer), "IO SYSTEM VERIFICATION ERROR in %s (%s %x)\n[%s+%x at %p]\n", ansiDriverName, DcCheckData->ClassText, DcCheckData->MessageID, ansiDriverName, DcCheckData->OffsetIntoImage, DcCheckData->CulpritAddress ); KeBugCheckEx( FATAL_UNHANDLED_HARD_ERROR, DcCheckData->BugCheckMajor, (ULONG_PTR) parameterArray, (ULONG_PTR) captionBuffer, (ULONG_PTR) "" // DcCheckData->AssertionText is too technical ); } else { KeBugCheckEx( DcCheckData->BugCheckMajor, DcCheckData->MessageID, (ULONG_PTR) DcCheckData->DcParamArray[9], (ULONG_PTR) DcCheckData->DcParamArray[15], (ULONG_PTR) DcCheckData->DcParamArray[16] ); } } VOID FASTCALL ViBugcheckPrintBuffer( IN PDC_CHECK_DATA DcCheckData ) { UCHAR buffer[82]; UCHAR classBuf[81]; UCHAR callerBuf[81+40]; UCHAR ansiDriverName[81]; LONG lMargin, i, lMarginCur, rMargin=78; PSTR lineStart, lastWord, current, lMarginText; // // Put down a carriage return // DbgPrint("\n") ; // // Drop a banner if this is a fatal assert or a logo failure. // if (DcCheckData->AssertionControl & (VFM_DEPLOYMENT_FAILURE | VFM_LOGO_FAILURE)) { DbgPrint( "***********************************************************************\n" "* THIS VALIDATION BUG IS FATAL AND WILL CAUSE THE VERIFIER TO HALT *\n" "* WINDOWS (BUGCHECK) WHEN THE MACHINE IS NOT UNDER A KERNEL DEBUGGER! *\n" "***********************************************************************\n" "\n" ); } // // Prepare left margin (ClassText) // if (DcCheckData->ClassText != NULL) { lMargin = (LONG)strlen(DcCheckData->ClassText)+2; DbgPrint("%s: ", DcCheckData->ClassText); } else { lMargin = 0; } if (lMargin+1>=rMargin) { lMargin=0; } for(i=0; iAssertionText; // // Print out culprit if we have him... // if (DcCheckData->CulpritAddress) { if (DcCheckData->DriverName->Length) { KeBugCheckUnicodeToAnsi( DcCheckData->DriverName, (PSTR)ansiDriverName, sizeof(ansiDriverName) ); sprintf((PCHAR)callerBuf, "[%s @ 0x%p] ", ansiDriverName, DcCheckData->CulpritAddress ); } else { sprintf((PCHAR)callerBuf, "[0x%p] ", DcCheckData->CulpritAddress); } DbgPrint("%s", callerBuf); lMarginCur += (LONG)strlen((PCHAR)callerBuf); } // // Format and print our assertion text // while(*current) { if (*current == ' ') { if ((current - lineStart) >= (rMargin-lMarginCur-1)) { DbgPrint("%s", lMarginText); lMarginText = (PSTR)classBuf; lMarginCur = lMargin; if ((lastWord-lineStart)= (rMargin-lMarginCur-1)) { DbgPrint("%s", lMarginText); lMarginText = (PSTR)classBuf; if ((lastWord-lineStart)DcParamArray[3]) { VfPrintDumpIrp((PIRP) DcCheckData->DcParamArray[3]); } if (DcCheckData->DcParamArray[6]) { VfPrintDumpIrpStack( &((PIRP_MINI_SNAPSHOT) DcCheckData->DcParamArray[6])->IoStackLocation ); } } VOID FASTCALL ViBugcheckPrintUrl( IN PDC_CHECK_DATA DcCheckData ) { DbgPrint( "http://www.microsoft.com/hwdq/bc/default.asp?os=%d.%d.%d&major=0x%x&minor=0x%x&lang=0x%x\n", VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION, VER_PRODUCTBUILD, DcCheckData->BugCheckMajor, DcCheckData->MessageID, 9 // English ); } VOID FASTCALL ViBugcheckPrompt( IN PDC_CHECK_DATA DcCheckData, OUT PBOOLEAN ExitAssertion ) { char response[2]; ULONG assertionControl; BOOLEAN waitForInput; assertionControl = DcCheckData->AssertionControl; *ExitAssertion = TRUE; // // Vocalize if so ordered. // if (assertionControl & VFM_FLAG_BEEP) { DbgPrint("%c", 7); } if (assertionControl & VFM_FLAG_ZAPPED) { return; } // // Wait for input... // waitForInput = TRUE; while(waitForInput) { if (DcCheckData->Control) { DbgPrompt( "Break, Ignore, Zap, Remove, Disable all (bizrd)? ", response, sizeof( response )); } else { DbgPrompt( "Break, Ignore, Disable all (bid)? ", response, sizeof( response )); } switch (response[0]) { case 'B': case 'b': DbgPrint("Breaking in... (press g to return to assert menu)\n"); DbgBreakPoint(); waitForInput = FALSE; *ExitAssertion = FALSE; break; case 'I': case 'i': waitForInput = FALSE; break; case 'Z': case 'z': if (DcCheckData->Control) { DbgPrint("Breakpoint zapped (OS will print text and return)\n"); assertionControl |= VFM_FLAG_ZAPPED; assertionControl &=~ VFM_FLAG_BEEP; waitForInput = FALSE; } break; case 'D': case 'd': ViBugCheckControlOverride = VFM_FLAG_CLEARED; DbgPrint("Verification asserts disabled.\n"); waitForInput = FALSE; break; case 'R': case 'r': if (DcCheckData->Control) { DbgPrint("Breakpoint removed\n") ; assertionControl |= VFM_FLAG_CLEARED; waitForInput = FALSE; } break; } } if (DcCheckData->Control) { *DcCheckData->Control = assertionControl; } } #ifdef ALLOC_DATA_PRAGMA #pragma data_seg() #pragma const_seg() #endif