|
|
/*++
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<ARRAY_COUNT(ViBugCheckParamTable); paramType++) {
paramMask = ViBugCheckParamTable[paramType].DcParamMask; while(curMask & (paramMask*3)) {
strcat( (char *) paramFormat, ViBugCheckParamTable[paramType].DcParamName );
curMask -= paramMask; } }
VfMessageRetrieveInternalTable( VFMESSAGE_TABLE_IOVERIFIER, &ioVerifierTable );
va_start(arglist, MessageParameterMask);
status = VfBugcheckThrowException( ioVerifierTable, (VFMESSAGE_ERRORID) MessageID, (PCSTR) paramFormat, &arglist );
va_end(arglist);
return status; }
NTSTATUS VfBugcheckThrowException( IN PVFMESSAGE_TEMPLATE_TABLE MessageTable OPTIONAL, IN VFMESSAGE_ERRORID MessageID, IN PCSTR MessageParamFormat, IN va_list * MessageParameters ) /*++
Description:
This routine displays an assert and provides options for removing the breakpoint, changing to just a text-out, etc.
Arguments:
Notes:
The text will automagically be formatted and printed as such:
ASSERTION CLASS: ASSERTION TEXT ASSERTION TEXT ASSERTION TEXT ASSERTION TEXT ...
--*/ { UCHAR finalBuffer[512]; NTSTATUS status; DC_CHECK_DATA dcCheckData; PVOID dcParamArray[3*ARRAY_COUNT(ViBugCheckParamTable)]; BOOLEAN exitAssertion;
//
// Preinit
//
RtlZeroMemory(dcParamArray, sizeof(dcParamArray));
//
// Determine what our basic policy towards this check will be and fill out
// the dcCheckData structure as well as we can.
//
ViBucheckProcessParams( MessageTable, MessageID, MessageParamFormat, MessageParameters, dcParamArray, &dcCheckData );
if (!ViBugcheckApplyControl(&dcCheckData)) {
//
// Nothing to see here, just ignore the assert...
//
return STATUS_SUCCESS; }
//
// We are going to express our disatifaction somehow. Expand out the
// message we've prepared for this scenario.
//
status = ViBugcheckProcessMessageText( sizeof(finalBuffer), (PSTR)finalBuffer, &dcCheckData );
if (!NT_SUCCESS(status)) {
ASSERT(0);
//
// Something went wrong with the index lookup!
//
return status; }
do {
ViBugcheckPrintBuffer(&dcCheckData); ViBugcheckPrintParamData(&dcCheckData); ViBugcheckPrintUrl(&dcCheckData); ViBugcheckHalt(&dcCheckData); ViBugcheckPrompt(&dcCheckData, &exitAssertion);
} while (!exitAssertion);
return status; }
VOID ViBucheckProcessParams( IN PVFMESSAGE_TEMPLATE_TABLE MessageTable OPTIONAL, IN VFMESSAGE_ERRORID MessageID, IN PCSTR MessageParamFormat, IN va_list * MessageParameters, IN PVOID * DcParamArray, OUT PDC_CHECK_DATA DcCheckData ) { PVOID culpritAddress; ULONG i, paramType, paramLen; char ansiDriverName[81]; NTSTATUS status; ULONG paramIndices[ARRAY_COUNT(ViBugCheckParamTable)]; PCSTR format;
//
// First we grab parameter off the stack and slot them appropriately into
// our array of "things".
//
// The array is in groups of three for each possible member of a given type
// (three irps, three routines, three device objects, etc). Items not
// referenced in are set to NULL.
//
RtlZeroMemory(paramIndices, sizeof(paramIndices)); format = MessageParamFormat; while(*format) {
if ((format[0] == ' ')||(format[0] == '%')) {
format++; continue; }
for(paramType = 0; paramType < ARRAY_COUNT(ViBugCheckParamTable); paramType++) {
paramLen = (ULONG)strlen(ViBugCheckParamTable[paramType].DcParamName);
if (!_strnicmp(ViBugCheckParamTable[paramType].DcParamName, format, paramLen)) {
//
// Match! Advance the pointer...
//
format += paramLen;
//
// If the caller specified an index, grab it. Otherwise infer.
//
if ((format[0] >= '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; i<lMargin; i++) classBuf[i] = ' '; classBuf[lMargin] = '\0'; lMarginText = (PSTR)(classBuf+lMargin); lMarginCur = lMargin;
lineStart = lastWord = current = DcCheckData->AssertionText;
//
// 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) {
memcpy(buffer, lineStart, (ULONG)(lastWord-lineStart)*sizeof(UCHAR)); buffer[lastWord-lineStart] = '\0'; DbgPrint("%s\n", buffer);
}
lineStart = lastWord+1; }
lastWord = current; }
current++; }
if ((current - lineStart) >= (rMargin-lMarginCur-1)) {
DbgPrint("%s", lMarginText); lMarginText = (PSTR)classBuf;
if ((lastWord-lineStart)<rMargin) {
memcpy(buffer, lineStart, (ULONG)(lastWord-lineStart)*sizeof(UCHAR)); buffer[lastWord-lineStart] = '\0'; DbgPrint("%s\n", buffer); }
lineStart = lastWord+1; }
if (lineStart<current) {
DbgPrint("%s%s\n", lMarginText, lineStart); } }
VOID FASTCALL ViBugcheckPrintParamData( IN PDC_CHECK_DATA DcCheckData ) { if (DcCheckData->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<enter> 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
|