1305 lines
52 KiB
C
1305 lines
52 KiB
C
/*++
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
ioassert.c
|
|
|
|
Abstract:
|
|
This module implements basic support for driver correctness checks (which may be raised to debugger prompts, bugchecks, etc).
|
|
|
|
Author:
|
|
Adrian J. Oney (adriao) 20-Apr-1998
|
|
|
|
Environment:
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
Known BUGBUGs:
|
|
--*/
|
|
|
|
#include "iop.h"
|
|
|
|
#if (( defined(_X86_) ) && ( FPO ))
|
|
#pragma optimize( "y", off ) // disable FPO for consistent stack traces
|
|
#endif
|
|
|
|
#ifndef NO_SPECIAL_IRP
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessTakeLock)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessReleaseLock)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessCheckUnderLock)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessProcessParams)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessProcessMessageText)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessApplyControl)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessThrowBugCheck)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessPrintBuffer)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessPrintParamData)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessPrompt)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessAddressToFileHeader)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessPrintIrp)
|
|
#pragma alloc_text(PAGEVRFY, IopDriverCorrectnessPrintIrpStack)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
PULONG IopDcControlCurrent = NULL;
|
|
LONG IopDcCurrentFrameSkips = -1;
|
|
KIRQL IopDcControlIrql;
|
|
KSPIN_LOCK IopDcControlLock;
|
|
ULONG IopDcControlInitial = (ULONG)-1;
|
|
ULONG IopDcControlOverride = (ULONG)-1;
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg("PAGEVRFY$data")
|
|
#endif
|
|
|
|
|
|
// When invoking the driver check macro's, pass Irps first, Routines second,
|
|
// DevObj's third, and any Status's last...
|
|
|
|
DCPARAM_TYPE_ENTRY DcParamTable[] = {
|
|
{ DCPARAM_IRP, "Irp" },
|
|
{ DCPARAM_ROUTINE, "Routine" },
|
|
{ DCPARAM_DEVOBJ, "DevObj" },
|
|
{ DCPARAM_STATUS, "Status" }
|
|
};
|
|
|
|
typedef struct _DCERROR_MESSAGE
|
|
{
|
|
DCERROR_ID MessageID;
|
|
PCDCERROR_CLASS MessageClass;
|
|
PSTR MessageText;
|
|
} DCERROR_MESSAGE, *PDCERROR_MESSAGE;
|
|
|
|
|
|
// These are the general "classifications" of errors, along with the default
|
|
// flags that will be applied the first time this is hit.
|
|
|
|
// IopDcWdmDriverErrorFatal -
|
|
// Anything in this class will cause the IO verifier to bugcheck if no debugger is present.
|
|
|
|
// IopDcWdmDriverErrorNonFatal -
|
|
// Anything in this class will not cause the IO verifier to bugcheck, but will stop under the debugger.
|
|
|
|
// IopDcWdmDriverWarning -
|
|
// Anything in this class will beep but continue without breaking in.
|
|
|
|
// IopDcWdmDriverPostponed -
|
|
// Anything in this class will merely print and continue.
|
|
|
|
// IopDcWdmCoreError -
|
|
// Issue in a core component (kernel or hal)
|
|
|
|
DCERROR_CLASS IopDcWdmDriverErrorFatal = {DIAG_BEEP | DIAG_FATAL_ERROR | DIAG_WDM_ERROR, "WDM DRIVER ERROR"};
|
|
DCERROR_CLASS IopDcWdmDriverErrorNonFatal = {DIAG_BEEP | DIAG_WDM_ERROR, "WDM DRIVER ERROR"};
|
|
DCERROR_CLASS IopDcWdmDriverWarning = {DIAG_BEEP | DIAG_ZAPPED, "WDM DRIVER WARNING"};
|
|
DCERROR_CLASS IopDcWdmDriverPostponed = {DIAG_ZAPPED, "POSTPONED WDM DRIVER BUG"};
|
|
DCERROR_CLASS IopDcWdmCoreError = {DIAG_BEEP, "CORE DRIVER ERROR"};
|
|
|
|
// This is the table of error messages. Note that we've numbered the error
|
|
// message numbers continuously, so that they can be used as an indice into the
|
|
// table. Also note these constants are published, so if asserts are removed
|
|
// in the future, the current algorithm may need to be replaced by something
|
|
// akin to a binary search (or perhaps padding will need to be added).
|
|
|
|
DCERROR_MESSAGE IopDcMessageTable[DCERROR_MAXIMUM - DCERROR_UNSPECIFIED] = {
|
|
{ DCERROR_UNSPECIFIED, NULL, NULL },
|
|
{ DCERROR_DELETE_WHILE_ATTACHED, &IopDcWdmDriverErrorFatal,
|
|
"A device is deleting itself while there is another device beneath it in "
|
|
"the driver stack. This may be because the caller has forgotten to call "
|
|
"IoDetachDevice first, or the lower driver may have incorrectly deleted "
|
|
"itself." },
|
|
{ DCERROR_DETACH_NOT_ATTACHED, &IopDcWdmDriverErrorFatal,
|
|
"Driver has attempted to detach from device object %DevObj, which is not "
|
|
"attached to anything. This may occur if detach was called twice on the "
|
|
"same device object." },
|
|
{ DCERROR_CANCELROUTINE_FORWARDED, &IopDcWdmDriverErrorFatal,
|
|
"A driver has called IoCallDriver without setting the CancelRoutine in "
|
|
"the Irp to NULL (Irp = %Irp )." },
|
|
{ DCERROR_NULL_DEVOBJ_FORWARDED, &IopDcWdmDriverErrorFatal,
|
|
"Caller has passed in NULL as a DeviceObject. This is fatal (Irp = %Irp )."
|
|
},
|
|
{ DCERROR_QUEUED_IRP_FORWARDED, &IopDcWdmDriverErrorFatal,
|
|
"Caller is forwarding an IRP that is currently queued beneath it! The "
|
|
"code handling IRPs returning STATUS_PENDING in this driver appears to "
|
|
"be broken (Irp = %Irp )." },
|
|
{ DCERROR_NEXTIRPSP_DIRTY, &IopDcWdmDriverErrorFatal,
|
|
"Caller has incorrectly forwarded an IRP (control field not zerod). The "
|
|
"driver should use IoCopyCurrentIrpStackLocationToNext or "
|
|
"IoSkipCurrentIrpStackLocation. (Irp = %Irp )" },
|
|
{ DCERROR_IRPSP_COPIED, &IopDcWdmDriverErrorFatal,
|
|
"Caller has manually copied the stack and has inadvertantly copied the "
|
|
"upper layer's completion routine. Please use "
|
|
"IoCopyCurrentIrpStackLocationToNext. (Irp = %Irp )." },
|
|
{ DCERROR_INSUFFICIENT_STACK_LOCATIONS, &IopDcWdmDriverErrorFatal,
|
|
"This IRP is about to run out of stack locations. Someone may have "
|
|
"forwarded this IRP from another stack (Irp = %Irp )." },
|
|
{ DCERROR_QUEUED_IRP_COMPLETED, &IopDcWdmDriverErrorFatal,
|
|
"Caller is completing an IRP that is currently queued beneath it! The "
|
|
"code handling IRPs returning STATUS_PENDING in this driver appears to be "
|
|
"broken. (Irp = %Irp )" },
|
|
{ DCERROR_FREE_OF_INUSE_TRACKED_IRP, &IopDcWdmDriverErrorFatal,
|
|
"Caller of IoFreeIrp is freeing an IRP that is still in use! (Original "
|
|
"Irp = %Irp1, Irp in usage is %Irp2 )" },
|
|
{ DCERROR_FREE_OF_INUSE_IRP, &IopDcWdmDriverErrorFatal,
|
|
"Caller of IoFreeIrp is freeing an IRP that is still in use! (Irp = %Irp )"
|
|
},
|
|
{ DCERROR_FREE_OF_THREADED_IRP, &IopDcWdmDriverErrorFatal,
|
|
"Caller of IoFreeIrp is freeing an IRP that is still enqueued against a "
|
|
"thread! (Irp = %Irp )" },
|
|
{ DCERROR_REINIT_OF_ALLOCATED_IRP_WITH_QUOTA, &IopDcWdmDriverErrorFatal,
|
|
"Caller of IoInitializeIrp has passed an IRP that was allocated with "
|
|
"IoAllocateIrp. This is illegal and unneccessary, and has caused a quota "
|
|
"leak. Check the documentation for IoReuseIrp if this IRP is being "
|
|
"recycled." },
|
|
{ DCERROR_PNP_IRP_BAD_INITIAL_STATUS, &IopDcWdmDriverErrorNonFatal,
|
|
"Any PNP IRP must have status initialized to STATUS_NOT_SUPPORTED "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_POWER_IRP_BAD_INITIAL_STATUS, &IopDcWdmDriverErrorNonFatal,
|
|
"Any Power IRP must have status initialized to STATUS_NOT_SUPPORTED "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_WMI_IRP_BAD_INITIAL_STATUS, &IopDcWdmDriverErrorNonFatal,
|
|
"Any WMI IRP must have status initialized to STATUS_NOT_SUPPORTED "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_SKIPPED_DEVICE_OBJECT, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has forwarded an Irp while skipping a device object in the stack. "
|
|
"The caller is probably sending IRPs to the PDO instead of to the device "
|
|
"returned by IoAttachDeviceToDeviceStack (Irp = %Irp )." },
|
|
{ DCERROR_BOGUS_FUNC_TRASHED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has trashed or has not properly copied IRP's stack (Irp = %Irp )."
|
|
},
|
|
{ DCERROR_BOGUS_STATUS_TRASHED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has changed the status field of an IRP it does not understand "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_BOGUS_INFO_TRASHED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has changed the information field of an IRP it does not "
|
|
"understand (Irp = %Irp )." },
|
|
{ DCERROR_PNP_FAILURE_FORWARDED, &IopDcWdmDriverErrorNonFatal, // Grrr
|
|
"Non-successful non-STATUS_NOT_SUPPORTED IRP status for IRP_MJ_PNP is "
|
|
"being passed down stack (Irp = %Irp ). Failed PNP IRPs must be completed."
|
|
},
|
|
{ DCERROR_PNP_IRP_STATUS_RESET, &IopDcWdmDriverErrorNonFatal,
|
|
"Previously set IRP_MJ_PNP status has been converted to "
|
|
"STATUS_NOT_SUPPORTED. (Irp = %Irp )." },
|
|
{ DCERROR_PNP_IRP_NEEDS_FDO_HANDLING, &IopDcWdmDriverErrorNonFatal,
|
|
"FDO caller has not handled a required IRP. The FDO must either fail the "
|
|
"IRP or set the IRP's status if it is not going change the IRP's status "
|
|
"using a completion routine. (Irp = %Irp )." },
|
|
{ DCERROR_PNP_IRP_FDO_HANDS_OFF, &IopDcWdmDriverErrorNonFatal,
|
|
"FDO caller has responded to an IRP that is reserved for PDO use only. "
|
|
"Stop it. (Irp = %Irp )" },
|
|
{ DCERROR_POWER_FAILURE_FORWARDED, &IopDcWdmDriverErrorNonFatal, // Grrr
|
|
"Non-successful non-STATUS_NOT_SUPPORTED IRP status for IRP_MJ_POWER is "
|
|
"being passed down stack (Irp = %Irp ). Failed POWER IRPs must be "
|
|
"completed." },
|
|
{ DCERROR_POWER_IRP_STATUS_RESET, &IopDcWdmDriverErrorNonFatal,
|
|
"Previously set IRP_MJ_POWER status has been converted to "
|
|
"STATUS_NOT_SUPPORTED. (Irp = %Irp )." },
|
|
{ DCERROR_INVALID_STATUS, &IopDcWdmDriverErrorNonFatal,
|
|
"Driver has returned a suspicious status. This is probably due to an "
|
|
"uninitiaized variable bug in the driver. (Irp = %Irp )" },
|
|
{ DCERROR_UNNECCESSARY_COPY, &IopDcWdmDriverWarning,
|
|
"Caller has copied the Irp stack but not set a completion routine. "
|
|
"This is inefficient, use IoSkipCurrentIrpStackLocation instead "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_SHOULDVE_DETACHED, &IopDcWdmDriverErrorFatal,
|
|
"An IRP dispatch handler has not properly detached from the stack below "
|
|
"it upon receiving a remove IRP. DeviceObject = %DevObj - Dispatch = "
|
|
"%Routine - Irp = %Irp" },
|
|
{ DCERROR_SHOULDVE_DELETED, &IopDcWdmDriverErrorFatal,
|
|
"An IRP dispatch handler has not properly deleted it's device object upon "
|
|
"receiving a remove IRP. DeviceObject = %DevObj - Dispatch = %Routine - "
|
|
"Irp = %Irp" },
|
|
{ DCERROR_MISSING_DISPATCH_FUNCTION, &IopDcWdmDriverErrorNonFatal,
|
|
"This driver has not filled out a dispatch routine for a required IRP "
|
|
"major function (Irp = %Irp )." },
|
|
{ DCERROR_WMI_IRP_NOT_FORWARDED, &IopDcWdmDriverErrorNonFatal,
|
|
"IRP_MJ_SYSTEM_CONTROL has been completed by someone other than the "
|
|
"ProviderId. This IRP should either have been completed earlier or "
|
|
"should have been passed down (Irp = %Irp ). The IRP was targetted at "
|
|
"DeviceObject %DevObj" },
|
|
{ DCERROR_DELETED_PRESENT_PDO, &IopDcWdmDriverErrorFatal,
|
|
"An IRP dispatch handler for a PDO has deleted it's device object, but "
|
|
"the hardware has not been reported as missing in a bus relations query. "
|
|
"DeviceObject = %DevObj - Dispatch = %Routine - Irp = %Irp " },
|
|
{ DCERROR_BUS_FILTER_ERRONEOUSLY_DETACHED, &IopDcWdmDriverErrorFatal,
|
|
"A Bus Filter's IRP dispatch handler has detached upon receiving a remove "
|
|
"IRP when the PDO is still alive. Bus Filters must clean up in "
|
|
"FastIoDetach callbacks. DeviceObject = %DevObj - Dispatch = %Routine - "
|
|
"Irp = %Irp" },
|
|
{ DCERROR_BUS_FILTER_ERRONEOUSLY_DELETED, &IopDcWdmDriverErrorFatal,
|
|
"An IRP dispatch handler for a bus filter has deleted it's device object, "
|
|
"but the PDO is still present! Bus filters must clean up in FastIoDetach "
|
|
"callbacks. DeviceObject = %DevObj - Dispatch = %Routine - Irp = %Irp" },
|
|
{ DCERROR_INCONSISTANT_STATUS, &IopDcWdmDriverErrorFatal,
|
|
"An IRP dispatch handler ( %Routine ) has returned a status that is "
|
|
"inconsistent with the Irp's IoStatus.Status field. ( Irp = %Irp - "
|
|
"Irp->IoStatus.Status = %Status1 - returned = %Status2 )" },
|
|
{ DCERROR_UNINITIALIZED_STATUS, &IopDcWdmDriverErrorNonFatal,
|
|
"An IRP dispatch handler has returned a status that is illegal "
|
|
"(0xFFFFFFFF). This is probably due to an uninitialized stack variable. "
|
|
"Please do an ln on address %lx and file a bug. (Irp = %Irp )" },
|
|
{ DCERROR_IRP_RETURNED_WITHOUT_COMPLETION, &IopDcWdmDriverErrorFatal,
|
|
"An IRP dispatch handler has returned without passing down or completing "
|
|
"this Irp or someone forgot to return STATUS_PENDING. (Irp = %Irp )." },
|
|
{ DCERROR_COMPLETION_ROUTINE_PAGABLE, &IopDcWdmDriverErrorFatal,
|
|
"IRP completion routines must be in nonpagable code, and this one is not: "
|
|
"%Routine. (Irp = %Irp )" },
|
|
{ DCERROR_PENDING_BIT_NOT_MIGRATED, &IopDcWdmDriverErrorNonFatal,
|
|
"A driver's completion routine ( %Routine ) has not marked the IRP "
|
|
"pending if the PendingReturned field was set in the IRP passed to it. "
|
|
"This may cause the OS to hang, especially if an error is returned by the "
|
|
" stack. (Irp = %Irp )" },
|
|
{ DCERROR_CANCELROUTINE_ON_FORWARDED_IRP, &IopDcWdmDriverErrorFatal,
|
|
"A cancel routine has been set for an IRP that is currently being "
|
|
"processed by drivers lower in the stack, possibly stomping their cancel "
|
|
"routine (Irp = %Irp, Routine=%Routine )." },
|
|
{ DCERROR_PNP_IRP_NEEDS_PDO_HANDLING, &IopDcWdmDriverErrorNonFatal,
|
|
"PDO has not responded to a required IRP (Irp = %Irp )" },
|
|
{ DCERROR_TARGET_RELATION_LIST_EMPTY, &IopDcWdmDriverErrorNonFatal,
|
|
"PDO has forgotten to fill out the device relation list with the PDO for "
|
|
"the TargetDeviceRelation query (Irp = %Irp )" },
|
|
{ DCERROR_TARGET_RELATION_NEEDS_REF, &IopDcWdmDriverErrorFatal,
|
|
"The code implementing the TargetDeviceRelation query has not called "
|
|
"ObReferenceObject on the PDO (Irp = %Irp )." },
|
|
{ DCERROR_BOGUS_PNP_IRP_COMPLETED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has completed a IRP_MJ_PNP it didn't understand instead of "
|
|
"passing it down (Irp = %Irp )." },
|
|
{ DCERROR_SUCCESSFUL_PNP_IRP_NOT_FORWARDED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has completed successful IRP_MJ_PNP instead of passing it down "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_UNTOUCHED_PNP_IRP_NOT_FORWARDED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has completed untouched IRP_MJ_PNP (instead of passing the irp "
|
|
"down) or non-PDO has failed the irp using illegal value of "
|
|
"STATUS_NOT_SUPPORTED. (Irp = %Irp )." },
|
|
{ DCERROR_BOGUS_POWER_IRP_COMPLETED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has completed a IRP_MJ_POWER it didn't understand instead of "
|
|
"passing it down (Irp = %Irp )." },
|
|
{ DCERROR_SUCCESSFUL_POWER_IRP_NOT_FORWARDED, &IopDcWdmDriverErrorFatal,
|
|
"Caller has completed successful IRP_MJ_POWER instead of passing it down "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_UNTOUCHED_POWER_IRP_NOT_FORWARDED, &IopDcWdmDriverErrorNonFatal,
|
|
"Caller has completed untouched IRP_MJ_POWER (instead of passing the irp "
|
|
"down) or non-PDO has failed the irp using illegal value of "
|
|
"STATUS_NOT_SUPPORTED. (Irp = %Irp )." },
|
|
{ DCERROR_PNP_QUERY_CAP_BAD_VERSION, &IopDcWdmDriverErrorFatal,
|
|
"The version field of the query capabilities structure in a query "
|
|
"capabilities IRP was not properly initialized. (Irp = %Irp )." },
|
|
{ DCERROR_PNP_QUERY_CAP_BAD_SIZE, &IopDcWdmDriverErrorFatal,
|
|
"The size field of the query capabilities structure in a query "
|
|
"capabilities IRP was not properly initialized. (Irp = %Irp )." },
|
|
{ DCERROR_PNP_QUERY_CAP_BAD_ADDRESS, &IopDcWdmDriverErrorNonFatal,
|
|
"The address field of the query capabilities structure in a query "
|
|
"capabilities IRP was not properly initialized to -1. (Irp = %Irp )." },
|
|
{ DCERROR_PNP_QUERY_CAP_BAD_UI_NUM, &IopDcWdmDriverErrorNonFatal,
|
|
"The UI Number field of the query capabilities structure in a query "
|
|
"capabilities IRP was not properly initialized to -1. (Irp = %Irp )." },
|
|
{ DCERROR_RESTRICTED_IRP, &IopDcWdmDriverErrorFatal,
|
|
"A driver has sent an IRP that is restricted for system use only. "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_REINIT_OF_ALLOCATED_IRP_WITHOUT_QUOTA, &IopDcWdmDriverWarning,
|
|
"Caller of IoInitializeIrp has passed an IRP that was allocated with "
|
|
"IoAllocateIrp. This is illegal, unneccessary, and negatively impacts "
|
|
"performace in normal use. Check the documentation for IoReuseIrp if "
|
|
"this IRP is being recycled." },
|
|
{ DCERROR_UNFORWARDED_IRP_COMPLETED, &IopDcWdmDriverWarning,
|
|
"The caller of IoCompleteRequest is completing an IRP that has never "
|
|
"been forwarded via a call to IoCallDriver or PoCallDriver. This may "
|
|
"be a bug. (Irp = %Irp )." },
|
|
{ DCERROR_DISPATCH_CALLED_AT_BAD_IRQL, &IopDcWdmDriverErrorFatal,
|
|
"A driver has forwarded an IRP at an IRQL that is illegal for this major"
|
|
" code. "
|
|
"(Irp = %Irp )." },
|
|
{ DCERROR_BOGUS_MINOR_STATUS_TRASHED, &IopDcWdmDriverErrorFatal,
|
|
"Caller has changed the status field of an IRP it does not understand "
|
|
"(Irp = %Irp )." }
|
|
};
|
|
|
|
typedef struct _DC_OVERRIDE_TABLE
|
|
{
|
|
DCERROR_ID MessageID;
|
|
PSTR DriverName;
|
|
PDCERROR_CLASS ReplacementClass;
|
|
} DC_OVERRIDE_TABLE, *PDC_OVERRIDE_TABLE;
|
|
|
|
|
|
// This table contains things we've postponed.
|
|
DC_OVERRIDE_TABLE IopDcOverrideTable[] = {
|
|
// These exist because verifier.exe cannot specify kernels or hals.
|
|
// We still want a mechanism to allow complaints.
|
|
{ DCERROR_UNSPECIFIED, "HAL.DLL", &IopDcWdmCoreError },
|
|
{ DCERROR_UNSPECIFIED, "NTOSKRNL.EXE", &IopDcWdmCoreError },
|
|
{ DCERROR_UNSPECIFIED, "NTKRNLMP.EXE", &IopDcWdmCoreError },
|
|
{ DCERROR_UNSPECIFIED, "NTKRNLPA.EXE", &IopDcWdmCoreError },
|
|
{ DCERROR_UNSPECIFIED, "NTKRPAMP.EXE", &IopDcWdmCoreError },
|
|
|
|
// ADRIAO BUGBUG 08/10/1999 -
|
|
// NDIS doesn't call the shutdown handlers at power-off because many are
|
|
// unstable and that adds seconds to shutdown. This is an unsafe design as
|
|
// the miniport, which owns an IRQ, may find it's ports drop out from under
|
|
// it when the *parent* powers off.
|
|
|
|
{ DCERROR_SUCCESSFUL_POWER_IRP_NOT_FORWARDED, "NDIS.SYS", &IopDcWdmDriverPostponed },
|
|
|
|
// ADRIAO BUGBUG 08/10/1999 -
|
|
// ACPI and PCI have to work together to handle wait-wake. In the
|
|
// current design, ACPI.SYS gets an interface and does all the work itself.
|
|
// The proper design should move the queueing to PCI, or tell PCI to leave
|
|
// wait-wake IRPs alone for the given device. Cutting off any other bus filters is a bad design.
|
|
{ DCERROR_SUCCESSFUL_POWER_IRP_NOT_FORWARDED, "ACPI.SYS", &IopDcWdmDriverPostponed },
|
|
|
|
// ADRIAO BUGBUG 08/20/1999 -
|
|
// This is on QueryCapabilities, which PCMCIA caches but should not.
|
|
// NeilSa has postponed this one till NT 5.1
|
|
{ DCERROR_SUCCESSFUL_PNP_IRP_NOT_FORWARDED, "PCMCIA.SYS", &IopDcWdmDriverPostponed },
|
|
|
|
// ADRIAO BUGBUG 08/20/1999 -
|
|
// PCMCIA completes Queries for S-IRPs. NeilSa has postponed this one
|
|
// till NT 5.1
|
|
|
|
{ DCERROR_SUCCESSFUL_POWER_IRP_NOT_FORWARDED, "PCMCIA.SYS", &IopDcWdmDriverPostponed },
|
|
|
|
// ADRIAO BUGBUG 08/21/1999 -
|
|
// SCSIPORT doesn't forward S0 Irps if the system is already in S0.
|
|
// Consider if a PDO succeeds a Query-S1 IRP and then waits for either a
|
|
// Set to S1, a new Query, or a Set to S0 meaning no transition will take
|
|
// place after all. A filter between SCSIPORT and the PDO could fail the
|
|
// Query-S1 on the way up. SCSIPORT knows the system has decided to stay in
|
|
// S0, but it cuts such knowledge off from the PDO. Luckily today's list of
|
|
// likely PDO's don't have such logic though. PeterWie has postponed this
|
|
// one till NT 5.1
|
|
{ DCERROR_SUCCESSFUL_POWER_IRP_NOT_FORWARDED, "SCSIPORT.SYS", &IopDcWdmDriverPostponed }
|
|
};
|
|
|
|
#ifdef ALLOC_DATA_PRAGMA
|
|
#pragma data_seg()
|
|
#endif
|
|
|
|
PCHAR KeBugCheckUnicodeToAnsi(IN PUNICODE_STRING UnicodeString, OUT PCHAR AnsiBuffer, IN ULONG MaxAnsiLength);
|
|
|
|
|
|
VOID IopDriverCorrectnessTakeLock(IN PULONG ControlNew, IN LONG StackFramesToSkip)
|
|
/*++
|
|
Description:
|
|
Worker routine for KD_ASSERT_OUT, takes the assertion spinlock.
|
|
We do this because we *must* use a macro to instantiate a caller-specific
|
|
tag and we cannot pass in that value along with a variable printf style
|
|
debug output. Hence we must "pre-program" the output routine with the breakpoint ID, and use a spinlock to protect ourselves
|
|
--*/
|
|
{
|
|
ASSERT(IovpInitCalled);
|
|
ExAcquireSpinLock(&IopDcControlLock, &IopDcControlIrql);
|
|
ASSERT(IopDcControlCurrent == NULL);
|
|
IopDcControlCurrent = ControlNew;
|
|
IopDcCurrentFrameSkips = StackFramesToSkip;
|
|
}
|
|
|
|
|
|
VOID IopDriverCorrectnessReleaseLock(VOID)
|
|
/*++
|
|
Description:
|
|
Worker routine for KD_ASSERT_OUT, see IopDiagSetAssertLock
|
|
--*/
|
|
{
|
|
IopDcControlCurrent = NULL;
|
|
IopDcCurrentFrameSkips = -1;
|
|
ExReleaseSpinLock(&IopDcControlLock, IopDcControlIrql);
|
|
}
|
|
|
|
|
|
NTSTATUS IopDriverCorrectnessCheckUnderLock(IN DCERROR_ID MessageID, IN ULONG MessageParameterMask, ...)
|
|
/*++
|
|
Description:
|
|
This routine displays an assert and provides options for removing the breakpoint, changing to just a text-out, etc.
|
|
|
|
This routine is used as part of a macro for providing "Zappable" traps -
|
|
IE, users get the option of changing how a specific instance of
|
|
WDM_DEBUG_OUT in the code is handled (default is spew and break, options are spew or blow away for this boot).
|
|
|
|
Macro must call IopDiagSetAssertLock, IopDiagAssertPrintfUnderLock, and IopDiagReleaseAssertLock in order
|
|
|
|
DCPARAM_IRP*(count)+DCPARAM_ROUTINE*(count)+DCPARAM_DEVOBJ*(count),
|
|
irp1,
|
|
irp2,
|
|
irp3,
|
|
routine1,
|
|
..,
|
|
..,
|
|
devobj1,
|
|
|
|
count can be a max of 3.
|
|
|
|
Arguments:
|
|
Control - if present, this variable determines whether we will actually do anything.
|
|
|
|
if DIAG_ZAPPED is set we will print the assert text and return.
|
|
if DIAG_CLEARED is set, we will neither print or stop.
|
|
if DIAG_BEEP is set, we will beep if not zapped.
|
|
|
|
StackFramesToSkip - If this parameter is -1, it is not used.
|
|
Otherwise, the assertion code will attempt to walk back the appropriate number of frames and
|
|
determine the caller to blame based on return address.
|
|
IrpToFlag - IRP to print out failure data for if mishandling of this IRP was the source of the error.
|
|
AddressToFlag - Address that the assert should be blaimed on. This should be null if StackFramesToSkip is not -1.
|
|
AssertionClass - Text describing the overall class of the assert
|
|
AssertionText - Text specifically describing the assert. Carriage returns should not be embedded in the text, this
|
|
routine will automatically ensure words are not split across the screen.
|
|
|
|
Notes:
|
|
The text will automagically be formatted and printed as such:
|
|
ASSERTION CLASS: ASSERTION TEXT ASSERTION TEXT ASSERTION TEXT ASSERTION TEXT ...
|
|
--*/
|
|
{
|
|
va_list arglist;
|
|
UCHAR finalBuffer[512];
|
|
NTSTATUS status;
|
|
DC_CHECK_DATA dcCheckData;
|
|
PVOID dcParamArray[3 * sizeof(DcParamTable) / sizeof(DCPARAM_TYPE_ENTRY)];
|
|
BOOLEAN exitAssertion;
|
|
|
|
va_start(arglist, MessageParameterMask);
|
|
|
|
// Determine what our basic policy towards this check will be and fill out the dcCheckData structure as well as we can.
|
|
IopDriverCorrectnessProcessParams(IopDcControlCurrent,
|
|
IopDcCurrentFrameSkips,
|
|
MessageID,
|
|
MessageParameterMask,
|
|
&arglist,
|
|
dcParamArray,
|
|
&dcCheckData);
|
|
|
|
va_end(arglist);
|
|
|
|
if (!IopDriverCorrectnessApplyControl(&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 = IopDriverCorrectnessProcessMessageText(sizeof(finalBuffer), finalBuffer, &dcCheckData);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(0);
|
|
|
|
// Something went wrong with the index lookup!
|
|
return status;
|
|
}
|
|
|
|
do {
|
|
IopDriverCorrectnessPrintBuffer(&dcCheckData);
|
|
IopDriverCorrectnessPrintParamData(&dcCheckData);
|
|
IopDriverCorrectnessThrowBugCheck(&dcCheckData);
|
|
IopDriverCorrectnessPrompt(&dcCheckData, &exitAssertion);
|
|
} while (!exitAssertion);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID IopDriverCorrectnessProcessParams(
|
|
IN OUT PULONG Control OPTIONAL,
|
|
IN LONG StackFramesToSkip,
|
|
IN DCERROR_ID MessageID,
|
|
IN ULONG MessageParameterMask,
|
|
IN va_list * MessageParameters,
|
|
IN PVOID * DcParamArray,
|
|
OUT PDC_CHECK_DATA DcCheckData)
|
|
{
|
|
PVOID returnAddress[1];
|
|
PVOID baseOfImage, culpritAddress;
|
|
ULONG stackHash;
|
|
PLDR_DATA_TABLE_ENTRY dataTableEntry;
|
|
PIMAGE_NT_HEADERS ntHeaders;
|
|
ULONG i, currentMessageMask, paramType, maxParameterTypes, paramMask;
|
|
ULONG tableIndex;
|
|
char ansiDriverName[81];
|
|
|
|
tableIndex = MessageID - DCERROR_UNSPECIFIED;
|
|
|
|
// Retrieve a pointer to the appropriate message text.
|
|
if (tableIndex >= sizeof(IopDcMessageTable) / sizeof(DCERROR_MESSAGE)) {
|
|
// Bogus message index!
|
|
ASSERT(0);
|
|
tableIndex = 0;
|
|
}
|
|
|
|
ASSERT(IopDcMessageTable[tableIndex].MessageID == MessageID);
|
|
|
|
// OK, we're going to do *something*. Process the incoming parameters.
|
|
maxParameterTypes = sizeof(DcParamTable) / sizeof(DCPARAM_TYPE_ENTRY);
|
|
|
|
// 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.
|
|
currentMessageMask = MessageParameterMask;
|
|
|
|
for (paramType = 0; paramType < maxParameterTypes; paramType++) {
|
|
paramMask = DcParamTable[paramType].DcParamMask;
|
|
for (i = 0; i < 3; i++) {
|
|
if (currentMessageMask&(paramMask * 3)) {
|
|
currentMessageMask -= paramMask;
|
|
DcParamArray[paramType * 3 + i] = va_arg(*MessageParameters, PVOID);
|
|
} else {
|
|
DcParamArray[paramType * 3 + i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this fires, part of the MessageParameterMask was not understood, probably a bug by the caller!
|
|
ASSERT(currentMessageMask == 0);
|
|
|
|
// Pre-init unhelpful answers...
|
|
culpritAddress = NULL;
|
|
DcCheckData->OffsetIntoImage = 0;
|
|
DcCheckData->InVerifierList = TRUE;
|
|
RtlInitUnicodeString(&DcCheckData->DriverName, NULL);
|
|
|
|
// Blame caller if appropriate
|
|
if (StackFramesToSkip != -1) {
|
|
if (RtlCaptureStackBackTrace(StackFramesToSkip + 2, 1, returnAddress, &stackHash) == 1) {
|
|
culpritAddress = returnAddress[0];
|
|
}
|
|
} else {
|
|
// %Routine1
|
|
culpritAddress = DcParamArray[3];
|
|
}
|
|
|
|
// Extract the culprit's name if possible...
|
|
if (culpritAddress) {
|
|
baseOfImage = IopDriverCorrectnessAddressToFileHeader((PVOID)culpritAddress, &dataTableEntry);
|
|
if (baseOfImage != NULL) {
|
|
// If we found a match, drill into the dataTableEntry's to get
|
|
if (MmIsAddressValid(dataTableEntry->DllBase) != FALSE) {
|
|
ntHeaders = RtlImageNtHeader(dataTableEntry->DllBase);
|
|
RtlCopyMemory(&DcCheckData->DriverName, &dataTableEntry->BaseDllName, sizeof(UNICODE_STRING));
|
|
DcCheckData->OffsetIntoImage = (PUCHAR)culpritAddress - (PUCHAR)baseOfImage;
|
|
}
|
|
|
|
// Now record whether this is in the verifying table.
|
|
if (!(dataTableEntry->Flags & LDRP_IMAGE_VERIFYING)) {
|
|
DcCheckData->InVerifierList = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
DcCheckData->CulpritAddress = culpritAddress;
|
|
DcCheckData->DcParamArray = DcParamArray;
|
|
DcCheckData->TableIndex = tableIndex;
|
|
DcCheckData->MessageID = MessageID;
|
|
DcCheckData->Control = Control;
|
|
DcCheckData->AssertionClass = IopDcMessageTable[DcCheckData->TableIndex].MessageClass;
|
|
|
|
// Get an ANSI version of the driver name and root through the override table.
|
|
KeBugCheckUnicodeToAnsi(&DcCheckData->DriverName, ansiDriverName, sizeof(ansiDriverName));
|
|
|
|
for (i = 0; i < sizeof(IopDcOverrideTable) / sizeof(DC_OVERRIDE_TABLE); i++) {
|
|
if ((IopDcOverrideTable[i].MessageID == MessageID) || (IopDcOverrideTable[i].MessageID == DCERROR_UNSPECIFIED)) {
|
|
if (!_stricmp(ansiDriverName, IopDcOverrideTable[i].DriverName)) {
|
|
// We have a match, override the error class. Note that as
|
|
// Control is not per-driver, but per assert, we null out
|
|
// the passed in Control so that we don't apply this override policy against every driver!
|
|
DcCheckData->Control = NULL;
|
|
DcCheckData->AssertionClass = IopDcOverrideTable[i].ReplacementClass;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN IopDriverCorrectnessApplyControl(IN OUT PDC_CHECK_DATA DcCheckData)
|
|
{
|
|
ULONG assertionControl;
|
|
|
|
if (IopDcControlOverride) {
|
|
assertionControl = IopDcControlOverride;
|
|
} else if (DcCheckData->Control) {
|
|
// Initialize the control if appropo
|
|
if (!((*DcCheckData->Control)&DIAG_INITIALIZED)) {
|
|
*DcCheckData->Control |= (DIAG_INITIALIZED | IopDcControlInitial | DcCheckData->AssertionClass->ClassFlags);
|
|
}
|
|
|
|
assertionControl = *DcCheckData->Control;
|
|
} else {
|
|
assertionControl = (IopDcControlInitial | DcCheckData->AssertionClass->ClassFlags);
|
|
}
|
|
|
|
if (assertionControl&DIAG_CLEARED) {
|
|
// If the breakpoint was cleared, then return, print/rip not.
|
|
return FALSE;
|
|
}
|
|
|
|
if ((!(assertionControl&DIAG_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 DIAG_FATAL_ERROR is set. Then we shall invoke the driver bugcheck...
|
|
if ((!KdDebuggerEnabled) && (!(assertionControl&DIAG_FATAL_ERROR))) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Record our intentions and continue.
|
|
DcCheckData->AssertionControl = assertionControl;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS IopDriverCorrectnessProcessMessageText(
|
|
IN ULONG MaxOutputBufferSize,
|
|
OUT PSTR OutputBuffer,
|
|
IN OUT PDC_CHECK_DATA DcCheckData)
|
|
{
|
|
ULONG paramType, maxParameterTypes;
|
|
ULONG arrayIndex, paramLength;
|
|
PSTR messageHead, newMessage;
|
|
LONG charsRemaining, length;
|
|
|
|
// Get the message text.
|
|
messageHead = IopDcMessageTable[DcCheckData->TableIndex].MessageText;
|
|
|
|
// Now manually build out the message.
|
|
newMessage = OutputBuffer;
|
|
charsRemaining = (MaxOutputBufferSize / sizeof(UCHAR)) - 1;
|
|
maxParameterTypes = sizeof(DcParamTable) / sizeof(DCPARAM_TYPE_ENTRY);
|
|
|
|
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 = strlen(DcParamTable[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, DcParamTable[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++;
|
|
}
|
|
|
|
length = _snprintf(newMessage, charsRemaining + 1, "%p", DcCheckData->DcParamArray[arrayIndex]);
|
|
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;
|
|
}
|
|
|
|
|
|
VOID IopDriverCorrectnessThrowBugCheck(IN PDC_CHECK_DATA DcCheckData)
|
|
{
|
|
PVOID parameterArray[4];
|
|
char captionBuffer[256];
|
|
char ansiDriverName[81];
|
|
UNICODE_STRING unicodeString;
|
|
|
|
// Do not bugcheck if a kernel debugger is attached, nor if this isn't a fatal error.
|
|
if (KdDebuggerEnabled || (!(DcCheckData->AssertionControl&DIAG_FATAL_ERROR))) {
|
|
return;
|
|
}
|
|
|
|
// We are here because DIAG_FATAL_ERROR is set. We use
|
|
// FATAL_UNHANDLED_HARD_ERROR so that we can give a descriptive text string for the problem.
|
|
parameterArray[0] = (PVOID)DcCheckData->MessageID;
|
|
parameterArray[1] = DcCheckData->CulpritAddress;
|
|
parameterArray[2] = DcCheckData->DcParamArray[0];
|
|
parameterArray[3] = DcCheckData->DcParamArray[6];
|
|
|
|
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,
|
|
DRIVER_VERIFIER_IOMANAGER_VIOLATION,
|
|
(ULONG_PTR)parameterArray,
|
|
(ULONG_PTR)captionBuffer,
|
|
(ULONG_PTR) "" // DcCheckData->AssertionText is too technical
|
|
);
|
|
}
|
|
|
|
|
|
VOID IopDriverCorrectnessPrintBuffer(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 carraige return
|
|
DbgPrint("\n");
|
|
|
|
// Drop a banner if this is a fatal assert.
|
|
if (DcCheckData->AssertionControl&DIAG_FATAL_ERROR) {
|
|
DbgPrint("*******\n"
|
|
"* THIS DRIVER 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 = 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 = 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, ansiDriverName, sizeof(ansiDriverName));
|
|
sprintf(callerBuf, "[%s @ 0x%p] ", ansiDriverName, DcCheckData->CulpritAddress);
|
|
} else {
|
|
sprintf(callerBuf, "[0x%p] ", DcCheckData->CulpritAddress);
|
|
}
|
|
|
|
DbgPrint("%s", callerBuf);
|
|
lMarginCur += strlen(callerBuf);
|
|
}
|
|
|
|
// Format and print our assertion text
|
|
while (*current) {
|
|
if (*current == ' ') {
|
|
if ((current - lineStart) >= (rMargin - lMarginCur - 1)) {
|
|
DbgPrint("%s", lMarginText);
|
|
lMarginText = 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 = 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 IopDriverCorrectnessPrintParamData(IN PDC_CHECK_DATA DcCheckData)
|
|
{
|
|
if (DcCheckData->DcParamArray[0]) {
|
|
IopDriverCorrectnessPrintIrp((PIRP)DcCheckData->DcParamArray[0]);
|
|
}
|
|
}
|
|
|
|
|
|
VOID IopDriverCorrectnessPrompt(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&DIAG_BEEP) {
|
|
DbgPrint("%c", 7);
|
|
}
|
|
|
|
if (assertionControl&DIAG_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 |= DIAG_ZAPPED;
|
|
assertionControl &= ~DIAG_BEEP;
|
|
waitForInput = FALSE;
|
|
}
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
IopDcControlOverride = DIAG_CLEARED;
|
|
DbgPrint("Verification asserts disabled.\n");
|
|
//assertionControl |= DIAG_CLEARED;
|
|
waitForInput = FALSE;
|
|
break;
|
|
case 'R':
|
|
case 'r':
|
|
if (DcCheckData->Control) {
|
|
DbgPrint("Breakpoint removed\n");
|
|
assertionControl |= DIAG_CLEARED;
|
|
waitForInput = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DcCheckData->Control) {
|
|
*DcCheckData->Control = assertionControl;
|
|
}
|
|
}
|
|
|
|
|
|
PVOID IopDriverCorrectnessAddressToFileHeader(IN PVOID Address, OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
|
|
/*++
|
|
Routine Description:
|
|
This function returns the base of an image that contains the specified Address.
|
|
An image contains the address if the address is within the ImageBase, and the ImageBase plus the size of the virtual image.
|
|
Arguments:
|
|
Address - Supplies an address to resolve to a loader entry.
|
|
DataTableEntry - Suppies a pointer to a variable that receives the address of the data table entry that describes the image.
|
|
Return Value:
|
|
NULL - No image was found that contains the passed in address.
|
|
NON-NULL - Returns the base address of the image that contain the address.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ModuleListHead;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLIST_ENTRY Next;
|
|
UINT_PTR Bounds;
|
|
PVOID ReturnBase = NULL, Base;
|
|
|
|
ModuleListHead = &PsLoadedModuleList;
|
|
|
|
// It would be nice if we could call MiLookupDataTableEntry, but it's pageable, so we do what the bugcheck stuff does...
|
|
Next = ModuleListHead->Flink;
|
|
if (Next != NULL) {
|
|
while (Next != ModuleListHead) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Next = Next->Flink;
|
|
Base = Entry->DllBase;
|
|
Bounds = (UINT_PTR)Base + Entry->SizeOfImage;
|
|
if ((UINT_PTR)Address >= (UINT_PTR)Base && (UINT_PTR)Address < Bounds) {
|
|
*DataTableEntry = Entry;
|
|
ReturnBase = Base;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReturnBase;
|
|
}
|
|
|
|
|
|
BOOLEAN IopIsMemoryRangeReadable(IN PVOID Location, IN size_t Length)
|
|
{
|
|
while (((ULONG_PTR)Location & (sizeof(ULONG_PTR) - 1)) && (Length > 0)) {
|
|
// Check to determine if the move will succeed before actually performing the operation.
|
|
if (MmIsAddressValid(Location) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
((PCHAR)Location)++;
|
|
Length--;
|
|
}
|
|
|
|
while (Length > (sizeof(ULONG_PTR) - 1)) {
|
|
// Check to determine if the move will succeed before actually performing the operation.
|
|
if (MmIsAddressValid(Location) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
((PCHAR)Location) += sizeof(ULONG_PTR);
|
|
Length -= sizeof(ULONG_PTR);
|
|
}
|
|
|
|
while (Length > 0) {
|
|
// Check to determine if the move will succeed before actually performing the operation.
|
|
if (MmIsAddressValid(Location) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
((PCHAR)Location)++;
|
|
Length--;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID IopDriverCorrectnessPrintIrp(IN PIRP IrpToFlag)
|
|
{
|
|
PIO_STACK_LOCATION irpSpCur;
|
|
PIO_STACK_LOCATION irpSpNxt;
|
|
|
|
// First see if we can touch the IRP header
|
|
if (!IopIsMemoryRangeReadable(IrpToFlag, sizeof(IRP))) {
|
|
return;
|
|
}
|
|
|
|
// OK, get the next two stack locations...
|
|
irpSpNxt = IoGetNextIrpStackLocation(IrpToFlag);
|
|
irpSpCur = IoGetCurrentIrpStackLocation(IrpToFlag);
|
|
|
|
if (IopIsMemoryRangeReadable(irpSpNxt, 2 * sizeof(IO_STACK_LOCATION))) {
|
|
// Both are present, print the best one!
|
|
if (irpSpNxt->MinorFunction == irpSpCur->MinorFunction) {
|
|
// Looks forwarded
|
|
IopDriverCorrectnessPrintIrpStack(irpSpNxt);
|
|
} else if (irpSpNxt->MinorFunction == 0) {
|
|
// Next location is probably currently zero'd
|
|
IopDriverCorrectnessPrintIrpStack(irpSpCur);
|
|
} else {
|
|
DbgPrint("Next: >");
|
|
IopDriverCorrectnessPrintIrpStack(irpSpNxt);
|
|
DbgPrint("Current: ");
|
|
IopDriverCorrectnessPrintIrpStack(irpSpCur);
|
|
}
|
|
} else if (IopIsMemoryRangeReadable(irpSpCur, sizeof(IO_STACK_LOCATION))) {
|
|
IopDriverCorrectnessPrintIrpStack(irpSpCur);
|
|
} else if (IopIsMemoryRangeReadable(irpSpNxt, sizeof(IO_STACK_LOCATION))) {
|
|
IopDriverCorrectnessPrintIrpStack(irpSpNxt);
|
|
}
|
|
}
|
|
|
|
|
|
PCHAR IrpMajorNames[] = {
|
|
"IRP_MJ_CREATE", // 0x00
|
|
"IRP_MJ_CREATE_NAMED_PIPE", // 0x01
|
|
"IRP_MJ_CLOSE", // 0x02
|
|
"IRP_MJ_READ", // 0x03
|
|
"IRP_MJ_WRITE", // 0x04
|
|
"IRP_MJ_QUERY_INFORMATION", // 0x05
|
|
"IRP_MJ_SET_INFORMATION", // 0x06
|
|
"IRP_MJ_QUERY_EA", // 0x07
|
|
"IRP_MJ_SET_EA", // 0x08
|
|
"IRP_MJ_FLUSH_BUFFERS", // 0x09
|
|
"IRP_MJ_QUERY_VOLUME_INFORMATION", // 0x0a
|
|
"IRP_MJ_SET_VOLUME_INFORMATION", // 0x0b
|
|
"IRP_MJ_DIRECTORY_CONTROL", // 0x0c
|
|
"IRP_MJ_FILE_SYSTEM_CONTROL", // 0x0d
|
|
"IRP_MJ_DEVICE_CONTROL", // 0x0e
|
|
"IRP_MJ_INTERNAL_DEVICE_CONTROL", // 0x0f
|
|
"IRP_MJ_SHUTDOWN", // 0x10
|
|
"IRP_MJ_LOCK_CONTROL", // 0x11
|
|
"IRP_MJ_CLEANUP", // 0x12
|
|
"IRP_MJ_CREATE_MAILSLOT", // 0x13
|
|
"IRP_MJ_QUERY_SECURITY", // 0x14
|
|
"IRP_MJ_SET_SECURITY", // 0x15
|
|
"IRP_MJ_POWER", // 0x16
|
|
"IRP_MJ_SYSTEM_CONTROL", // 0x17
|
|
"IRP_MJ_DEVICE_CHANGE", // 0x18
|
|
"IRP_MJ_QUERY_QUOTA", // 0x19
|
|
"IRP_MJ_SET_QUOTA", // 0x1a
|
|
"IRP_MJ_PNP", // 0x1b
|
|
NULL
|
|
};
|
|
|
|
|
|
#define MAX_NAMED_MAJOR_IRPS 0x1b
|
|
|
|
|
|
PCHAR PnPIrpNames[] = {
|
|
"IRP_MN_START_DEVICE", // 0x00
|
|
"IRP_MN_QUERY_REMOVE_DEVICE", // 0x01
|
|
"IRP_MN_REMOVE_DEVICE - ", // 0x02
|
|
"IRP_MN_CANCEL_REMOVE_DEVICE", // 0x03
|
|
"IRP_MN_STOP_DEVICE", // 0x04
|
|
"IRP_MN_QUERY_STOP_DEVICE", // 0x05
|
|
"IRP_MN_CANCEL_STOP_DEVICE", // 0x06
|
|
"IRP_MN_QUERY_DEVICE_RELATIONS", // 0x07
|
|
"IRP_MN_QUERY_INTERFACE", // 0x08
|
|
"IRP_MN_QUERY_CAPABILITIES", // 0x09
|
|
"IRP_MN_QUERY_RESOURCES", // 0x0A
|
|
"IRP_MN_QUERY_RESOURCE_REQUIREMENTS", // 0x0B
|
|
"IRP_MN_QUERY_DEVICE_TEXT", // 0x0C
|
|
"IRP_MN_FILTER_RESOURCE_REQUIREMENTS", // 0x0D
|
|
"INVALID_IRP_CODE", //
|
|
"IRP_MN_READ_CONFIG", // 0x0F
|
|
"IRP_MN_WRITE_CONFIG", // 0x10
|
|
"IRP_MN_EJECT", // 0x11
|
|
"IRP_MN_SET_LOCK", // 0x12
|
|
"IRP_MN_QUERY_ID", // 0x13
|
|
"IRP_MN_QUERY_PNP_DEVICE_STATE", // 0x14
|
|
"IRP_MN_QUERY_BUS_INFORMATION", // 0x15
|
|
"IRP_MN_DEVICE_USAGE_NOTIFICATION", // 0x16
|
|
"IRP_MN_SURPRISE_REMOVAL", // 0x17
|
|
"IRP_MN_QUERY_LEGACY_BUS_INFORMATION", // 0x18
|
|
NULL
|
|
};
|
|
|
|
#define MAX_NAMED_PNP_IRP 0x18
|
|
|
|
PCHAR WmiIrpNames[] = {
|
|
"IRP_MN_QUERY_ALL_DATA", // 0x00
|
|
"IRP_MN_QUERY_SINGLE_INSTANCE", // 0x01
|
|
"IRP_MN_CHANGE_SINGLE_INSTANCE", // 0x02
|
|
"IRP_MN_CHANGE_SINGLE_ITEM", // 0x03
|
|
"IRP_MN_ENABLE_EVENTS", // 0x04
|
|
"IRP_MN_DISABLE_EVENTS", // 0x05
|
|
"IRP_MN_ENABLE_COLLECTION", // 0x06
|
|
"IRP_MN_DISABLE_COLLECTION", // 0x07
|
|
"IRP_MN_REGINFO", // 0x08
|
|
"IRP_MN_EXECUTE_METHOD", // 0x09
|
|
NULL
|
|
};
|
|
|
|
#define MAX_NAMED_WMI_IRP 0x9
|
|
|
|
PCHAR PowerIrpNames[] = {
|
|
"IRP_MN_WAIT_WAKE", // 0x00
|
|
"IRP_MN_POWER_SEQUENCE", // 0x01
|
|
"IRP_MN_SET_POWER", // 0x02
|
|
"IRP_MN_QUERY_POWER", // 0x03
|
|
NULL
|
|
};
|
|
|
|
#define MAX_NAMED_POWER_IRP 0x3
|
|
|
|
|
|
VOID IopDriverCorrectnessPrintIrpStack(IN PIO_STACK_LOCATION IrpSp)
|
|
{
|
|
if ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && (IrpSp->MinorFunction == IRP_MN_SCSI_CLASS)) {
|
|
DbgPrint("IRP_MJ_SCSI");
|
|
} else if (IrpSp->MajorFunction <= MAX_NAMED_MAJOR_IRPS) {
|
|
DbgPrint(IrpMajorNames[IrpSp->MajorFunction]);
|
|
} else if (IrpSp->MajorFunction == 0xFF) {
|
|
DbgPrint("IRP_MJ_BOGUS");
|
|
} else {
|
|
DbgPrint("IRP_MJ_??");
|
|
}
|
|
|
|
switch (IrpSp->MajorFunction) {
|
|
case IRP_MJ_SYSTEM_CONTROL:
|
|
DbgPrint(".");
|
|
if (IrpSp->MinorFunction <= MAX_NAMED_WMI_IRP) {
|
|
DbgPrint(WmiIrpNames[IrpSp->MinorFunction]);
|
|
} else if (IrpSp->MinorFunction == 0xFF) {
|
|
DbgPrint("IRP_MN_BOGUS");
|
|
} else {
|
|
DbgPrint("(Bogus)\n");
|
|
}
|
|
DbgPrint("\n");
|
|
break;
|
|
case IRP_MJ_PNP:
|
|
DbgPrint(".");
|
|
if (IrpSp->MinorFunction <= MAX_NAMED_PNP_IRP) {
|
|
DbgPrint(PnPIrpNames[IrpSp->MinorFunction]);
|
|
} else if (IrpSp->MinorFunction == 0xFF) {
|
|
DbgPrint("IRP_MN_BOGUS");
|
|
} else {
|
|
DbgPrint("(Bogus)\n");
|
|
}
|
|
|
|
switch (IrpSp->MinorFunction) {
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
switch (IrpSp->Parameters.QueryDeviceRelations.Type) {
|
|
case BusRelations:
|
|
DbgPrint("(BusRelations)");
|
|
break;
|
|
case EjectionRelations:
|
|
DbgPrint("(EjectionRelations)");
|
|
break;
|
|
case PowerRelations:
|
|
DbgPrint("(PowerRelations)");
|
|
break;
|
|
case RemovalRelations:
|
|
DbgPrint("(RemovalRelations)");
|
|
break;
|
|
case TargetDeviceRelation:
|
|
DbgPrint("(TargetDeviceRelation)");
|
|
break;
|
|
default:
|
|
DbgPrint("(Bogus)\n");
|
|
break;
|
|
}
|
|
break;
|
|
case IRP_MN_QUERY_INTERFACE:
|
|
break;
|
|
case IRP_MN_QUERY_DEVICE_TEXT:
|
|
switch (IrpSp->Parameters.QueryId.IdType) {
|
|
case DeviceTextDescription:
|
|
DbgPrint("(DeviceTextDescription)");
|
|
break;
|
|
case DeviceTextLocationInformation:
|
|
DbgPrint("(DeviceTextLocationInformation)");
|
|
break;
|
|
default:
|
|
DbgPrint("(Bogus)\n");
|
|
break;
|
|
}
|
|
break;
|
|
case IRP_MN_WRITE_CONFIG:
|
|
case IRP_MN_READ_CONFIG:
|
|
DbgPrint("(WhichSpace=%x, Buffer=%x, Offset=%x, Length=%x)",
|
|
IrpSp->Parameters.ReadWriteConfig.WhichSpace,
|
|
IrpSp->Parameters.ReadWriteConfig.Buffer,
|
|
IrpSp->Parameters.ReadWriteConfig.Offset,
|
|
IrpSp->Parameters.ReadWriteConfig.Length);
|
|
break;
|
|
case IRP_MN_SET_LOCK:
|
|
if (IrpSp->Parameters.SetLock.Lock) DbgPrint("(True)");
|
|
else DbgPrint("(False)");
|
|
break;
|
|
case IRP_MN_QUERY_ID:
|
|
switch (IrpSp->Parameters.QueryId.IdType) {
|
|
case BusQueryDeviceID:
|
|
DbgPrint("(BusQueryDeviceID)");
|
|
break;
|
|
case BusQueryHardwareIDs:
|
|
DbgPrint("(BusQueryHardwareIDs)");
|
|
break;
|
|
case BusQueryCompatibleIDs:
|
|
DbgPrint("(BusQueryCompatibleIDs)");
|
|
break;
|
|
case BusQueryInstanceID:
|
|
DbgPrint("(BusQueryInstanceID)");
|
|
break;
|
|
default:
|
|
DbgPrint("(Bogus)\n");
|
|
break;
|
|
}
|
|
break;
|
|
case IRP_MN_QUERY_BUS_INFORMATION:
|
|
// BUGBUG: Should print out
|
|
break;
|
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
|
switch (IrpSp->Parameters.UsageNotification.Type) {
|
|
case DeviceUsageTypeUndefined:
|
|
DbgPrint("(DeviceUsageTypeUndefined");
|
|
break;
|
|
case DeviceUsageTypePaging:
|
|
DbgPrint("(DeviceUsageTypePaging");
|
|
break;
|
|
case DeviceUsageTypeHibernation:
|
|
DbgPrint("(DeviceUsageTypeHibernation");
|
|
break;
|
|
case DeviceUsageTypeDumpFile:
|
|
DbgPrint("(DeviceUsageTypeDumpFile");
|
|
break;
|
|
default:
|
|
DbgPrint("(Bogus)\n");
|
|
break;
|
|
}
|
|
if (IrpSp->Parameters.UsageNotification.InPath) {
|
|
DbgPrint(", InPath=TRUE)");
|
|
} else {
|
|
DbgPrint(", InPath=FALSE)");
|
|
}
|
|
break;
|
|
case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
|
|
// BUGBUG: Should print out
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
DbgPrint("\n");
|
|
break;
|
|
case IRP_MJ_POWER:
|
|
DbgPrint(".");
|
|
if (IrpSp->MinorFunction <= MAX_NAMED_POWER_IRP) {
|
|
DbgPrint(PowerIrpNames[IrpSp->MinorFunction]);
|
|
} else if (IrpSp->MinorFunction == 0xFF) {
|
|
DbgPrint("IRP_MN_BOGUS");
|
|
} else {
|
|
DbgPrint("(Bogus)\n");
|
|
}
|
|
DbgPrint("\n");
|
|
break;
|
|
default:
|
|
DbgPrint("\n");
|
|
break;
|
|
}
|
|
}
|
|
#endif // NO_SPECIAL_IRP
|