973 lines
23 KiB
C
973 lines
23 KiB
C
|
/*++
|
||
|
|
||
|
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 = TRUE;
|
||
|
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_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_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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|