Developing Drivers with the Windows Driver Foundation (Pro Developer)

Driver Verifier is an extremely useful tool for detecting errors in kernel-mode drivers. It tests and traps many conditions that might otherwise go unnoticed in normal operation. Driver Verifier verifies that drivers are not making illegal function calls or causing system corruption. Driver Verifier is installed in all versions of Windows Vista, Windows Server 2003, Windows XP, and Windows 2000.

Tip 

All kernel-mode drivers that are developed internally at Microsoft must pass Driver Verifier with standard settings. Microsoft developers recommend that you enable Driver Verifier as early as possible and use it throughout the development of your driver.

Driver Verifier has evolved over time, so versions of Driver Verifier installed in later releases of Windows have more features than earlier versions. A significant enhancement in Driver Verifier for Windows Vista is the ability to activate most options and to add and remove drivers for verification without rebooting the computer.

Depending on the options that are enabled and the number of drivers being verified, running Driver Verifier can affect performance on the test system. When you are testing your driver for performance-related issues, be sure to disable Driver Verifier so it does not skew the results.

 Note  Driver Verifier validates the use of the kernel-mode WDM function calls. When you run Driver Verifier on your WDF driver, the output might contain the names of functions that your driver does not call explicitly but that the framework calls on your driver's behalf. The discussion in this section lists the underlying WDM function names.

 Tip  See "Driver Verifier" in the WDK-online at http://go.microsoft.com/fwlink/?LinkId=79793. See also "Driver Verifier in Windows Vista" on the WHDC Web site-online at http://go.microsoft.com/fwlink/?LinkId=79588.

When to Use Driver Verifier

You can run Driver Verifier throughout the development and test process to help find problems early in the development life cycle, when they are easier and cheaper to correct. Driver Verifier can identify conditions such as memory corruption, mishandled IRPs, invalid DMA buffer usage, and possible deadlocks. Because Driver Verifier is installed with Windows, it can also be used to troubleshoot problems on customers' computers.

How Driver Verifier Works

Driver Verifier operates on drivers that are installed and running on the system. Driver Verifier works by intercepting certain system calls from the driver and injecting faults or constraining resources, to test the driver's behavior under extreme conditions.

When Driver Verifier runs, it always checks whether the driver uses memory at an improper IRQL, improperly acquires or releases spin locks, improperly allocates or frees memory, or frees pool memory allocations without first removing timers. When the driver is unloaded, Driver Verifier always checks to see that it has properly released its resources.

Table 21-2 lists options that you can specify to direct Driver Verifier to perform additional checks.

Table 21-2: Driver Verifier Options

Open table as spreadsheet

When this option is active…

Driver Verifier…

Deadlock Detection

Monitors the driver's use of spin locks, mutexes, and fast mutexes to detect whether the driver's code has the potential to cause a deadlock. (Windows XP and later)

Disk Integrity Checking

Monitors hard disk access and detects whether the disk is preserving its data correctly. (Windows Server 2003 and later)

DMA Verification

Monitors the driver's use of DMA routines to detect improper use of DMA buffers, adapters, and map registers. (Windows XP and later)

Driver Hang Verification

Times the driver's I/O completion and cancellation routines and reports routines that exceed time limits that you specify. (Windows Vista and later)

Enhanced I/O Verification

Monitors the calls of several I/O manager routines and performs stress testing of Plug and Play IRPs, power IRPs, and WMI IRPs. (Windows XP and later)

Force IRQL Checking

Places extreme pressure on the driver by invalidating pageable code. If the driver attempts to access paged memory at the wrong IRQL or while holding a spin lock, Driver Verifier detects this behavior.

Force Pending I/O Requests

Tests the driver's response to STATUS_PENDING return values by returning STATUS_PENDING for random calls to IoCallDriver. (Windows Vista and later)

I/O Verification

Allocates the driver's IRPs from a special pool and monitors the driver's I/O handling to detect illegal or inconsistent use of I/O routines. In Windows Vista and later versions of Windows, activating I/O Verification also activates Driver Hang Verification.

IRP Logging

Monitors a driver's use of IRPs and creates a log of IRP use. (Windows Server 2003 and later)

Low Resources Simulation

Randomly fails pool memory allocation requests and other resource requests. By injecting these allocation faults into the system, Driver Verifier tests the driver's ability to cope with a low-resource situation.

Miscellaneous Checks

Looks for common causes of driver crashes, such as the mishandling of freed memory. (Windows Vista and later)

Pool Tracking

Checks whether the driver has freed all of its memory allocations when it is unloaded, revealing memory leaks.

Special Pool

Allocates most of the driver's memory requests from a special pool that is monitored for memory overruns, memory underruns, and memory that is accessed after it is freed.

How to Run Drivser Verifier

When you start Driver Verifier, you specify the driver or drivers to verify and the options to apply. Driver Verifier then monitors the behavior of the selected drivers until you either deactivate Driver Verifier or reboot the system. You can direct Driver Verifier output to a log file or use the !verifier debugger extension to view Driver Verifier in a debugger, if one is attached to the system.

Inside Out 

The Verifier utility (Verifier.exe) activates and monitors Driver Verifier. The Verifier utility is located in the %windir%\system directory.

To Use the Verifier Utility as a Command-Line Tool

For example, the following command line activates Driver Verifier with pool tracking (/flags 8) for the driver Xyz.sys:

 Tip  See "The Verifier Command Line" in the WDK for complete details about Verifier command-line options-online at http://go.microsoft.com/fwlink/?LinkId=79788.

To Run Driver Verifier Manager

The Verifier utility starts Driver Verifier Manager, which provides a simple graphical user interface you can use to select settings and drivers to verify.

In subsequent screens, you can select predefined or individual settings and the drivers to verify. Driver Verifier Manager then launches the Verifier utility with those settings enabled.

 Tip  See "Driver Verifier Manager (Windows XP and Later)" in the WDK-online at http://go.microsoft.com/fwlink/?LinkId=79789.

Driver Verifier Examples

The following examples show Verifier commands that use a few typical parameters. In practice, you would combine parameters to activate the checks that make sense for your driver and testing scenarios.

Example 1: Activate Standard Options for a List of Drivers

The following command line activates a set of "standard" Driver Verifier options for the specified drivers after the next boot:

The /standard parameter in Windows XP and later versions activates the following Driver Verifier options:

Deadlock Detection

I/O Verification

DMA Verification

Pool Tracking

Force IRQL Checking

Special Pool

In Windows Vista and later versions, the /standard options also include Security Checks and Miscellaneous Checks.

The /drivers parameter directs Driver Verifier to verify the specified drivers. Driverlist is a list of drivers by binary name, such as Driver.sys. Use a space between driver names.

Example 2: Activate Specific Options for All Drivers

The following command line sets one or more options for all drivers on the system:

The /flags parameter specifies one or more options to activate after the next reboot. Options can be specified in either decimal or hexadecimal format in any combination of the values in Table 21-3.

Table 21-3: Driver Verifier /flags Parameter Options

Open table as spreadsheet

Decimal

Hexadecimal

Option

1

0x1

Special Pool

2

0x2

Force IRQL Checking

4

0x4

Low Resources Simulation

8

0x8

Pool Tracking

16

0x10

I/O Verification

32

0x20

Deadlock Detection (Windows XP and later)

64

0x40

Enhanced I/O Verification (Windows XP and later)

128

0x80

DMA Verification (Windows XP and later)

256

0x100

Security Checks (Windows XP and later)

512

0x200

Force Pending I/O Requests (Windows Vista and later)

1024

0x400

IRP Logging (Windows Server 2003 and later)

2048

0x800

Miscellaneous Checks (Windows Vista and later)

For example, in Windows Vista the /standard parameter shown in Example 1 is the equivalent of /flags 0x9BB.

Example 3: Start or Stop the Verification of a Driver without Rebooting

To start the verification of any driver without rebooting the system, even when the driver is already loaded, use the following command syntax:

The /volatile parameter changes settings without rebooting the computer. Volatile settings take effect immediately.

To remove a driver from the list of drivers to verify, use the /removedriver parameter, as shown in the following example:

The /removedriver parameter removes the driver from the verification list only if the driver is not already loaded. If the driver is already loaded, Driver Verifier continues to monitor that driver until you reboot the system. To minimize overhead until the next reboot, deactivate all Driver Verifier options as shown in Example 5.

Example 4: Activate or Deactivate Options without Rebooting

To enable and disable any option without rebooting, use /volatile with the /flags parameter, as shown in the following example:

You can use this command syntax with any Driver Verifier option except SCSI Verification and Disk Integrity Checking. For example, the following command activates the Deadlock Detection option without rebooting:

Example 5: Deactivate All Driver Verifier Options

To deactivate Driver Verifier options without rebooting, use /flags 0 together with /volatile, as shown in the following example:

Driver Verifier continues to monitor the driver by using the options in the automatic checks feature, which cannot be turned off. However, the overhead of automatic checks is approximately 10 percent of the overhead of a typical verification, so it has relatively little impact on performance.

Example 6: Deactivate Driver Verifier

To clear all Driver Verifier settings, use the following command line:

After the next reboot, no drivers will be verified.

Example 7: Use Low Resources Simulation

When the Low Resources Simulation option is active, Driver Verifier fails random instances of the driver's memory allocations as might occur if the driver is running on a computer with insufficient memory. This tests the driver's ability to respond properly to low-memory and other low-resource conditions.

The Low Resources Simulation test fails allocations that are requested by calls to several different functions, including the following:

ExAllocatePoolXxx

MmMapIoSpace

MmMapLockedPagesSpecifyCache

MmProbeAndLockPages

In Windows Vista and later versions, calls to the following functions are also injected with the following faults:

IoAllocateErrorLogEntry

IoAllocateWorkItem

IoAllocateIrp

MmAllocateContiguousMemoryXxx

IoAllocateMdl

MmAllocatePagesForMdl

To enable Low Resources Simulation, use the following command:

The custom settings parameters-Probability, PoolTags, and so on-must appear in the order displayed. If you omit a value, type quotation marks to hold its place.

The following parameters are used for Low Resources Simulation:

The following command enables Low Resources Simulation with a probability of 10 percent (1000/10000) and a delay of 5 minutes for the pool tags named Tag1 and Fred plus the application Notepad.exe:

The following command enables Low Resources Simulation with the default values, except that it extends the delay to 10 minutes:

You can use the /volatile command-line parameter to change Low Resources Simulation settings without rebooting the computer, as in the following command:

Volatile settings take effect immediately.

Example 8: Use Force IRQL Checking

Although kernel-mode drivers cannot access pageable memory at a high IRQL or while holding a spin lock, such an action might not trigger an error if the memory page has not actually been removed from the working set and paged out to disk.

When the Force IRQL Checking option is enabled, Driver Verifier trims all of the driver's pageable code and data-including pageable pool, code, and data-from the working set whenever the selected driver attempts to acquire a spin lock, calls KeSynchronizeExecution, or raises the IRQL to DISPATCH_LEVEL or higher. If the driver attempts to access any of this memory, Driver Verifier issues a bug check.

In Windows Vista, Driver Verifier can detect when certain synchronization objects are allocated in pageable memory. These synchronization objects should not be allocated from pageable memory because the kernel might access them at elevated IRQL. Driver Verifier can detect the following pageable objects:

ERESOURCE

KSEMAPHORE

FAST_MUTEX

KSPIN_LOCK

KEVENT

KTIMER

KMUTEX

 

Force IRQL Checking is included in the /standard settings. To enable Force IRQL Checking individually, use the following command:

The feature is active after the next boot. Use the /volatile parameter to activate or deactivate Force IRQL Checking without rebooting.

How to Use Driver Verifier Information during Debugging

The !verifier debugger extension can be used to monitor and report statistics related to Driver Verifier during a debugging session. The example in this section shows how to use !verifier to debug a crash caused by Driver Verifier's Low Resources Simulation option.

This information applies for any version of Windows when Driver Verifier is using Low Resources Simulation.

Example 1: Use !verifier to View Stack Traces

The easiest crashes to understand are probably those where a driver is accessing a NULL pointer. For this example, inspecting the source code around the path that crashed reveals that the driver called ExAllocatePoolWithTag, the function returned NULL, and the driver did not check the return value, so it crashed when using the pointer.

Understanding the cause of a driver crash is not always trivial, however. Often you can extract useful information by looking at the stack traces for recently injected faults. For example, !verifier 4 in the kernel debugger displays the four most recent injected faults-not necessarily four stack traces. You can specify an additional parameter for !verifier to display more stack traces. The stack trace for the most recent fault appears first.

If you encounter a crash dump or a debugger break while fault injection is enabled, you can use the stack trace to see if fault injection was the root cause. For example, if the debugger indicated a crash in win32k!GreEnableEUDC, Listing 21-1 would tell you that the crash was the result of the last induced fault.

Listing 21-1: Example of crash in win32k!GreEnableEUDC

kd> !verifier 4 Resource fault injection history: Tracker @ 8354A000 (# entries: 80, size: 80, depth: 8) Entry @ 8354B258 (index 75) Thread: C2638220 816760CB nt!VerifierExAllocatePoolWithTag+0x49 A4720443 win32k!bDeleteAllFlEntry+0x15d A4720AB0 win32k!GreEnableEUDC+0x70 A47218FA win32k!CleanUpEUDC+0x37 A473998E win32k!GdiMultiUserFontCleanup+0x5 815AEACC nt!MiDereferenceSession+0x74 8146D3B4 nt!MmCleanProcessAddressSpace+0x112 815DF739 nt!PspExitThread+0x603 Entry @ 8354B230 (index 74) Thread: 8436D770 816760CB nt!VerifierExAllocatePoolWithTag+0x49 A462141C win32k!Win32AllocPool+0x13 A4725F94 win32k!StubGdiAlloc+0x10 A4631A93 win32k!ExAllocateFromPagedLookasideList+0x27 A47261A4 win32k!AllocateObject+0x23 A4726F76 win32k!HmgAlloc+0x25 A47509D8 win32k!DCMEMOBJ::DCMEMOBJ+0x3b A4717D61 win32k!GreCreateDisplayDC+0x31 Entry @ 8354B208 (index 73) Thread: D6B4B9B8 816760CB nt!VerifierExAllocatePoolWithTag+0x49 A462141C win32k!Win32AllocPool+0x13 A46C2759 win32k!PALLOCMEM+0x17 A477CCF2 win32k!bComputeGISET+0x82 A477D07D win32k!PFEMEMOBJ::bInit+0x248 A475DC18 win32k!PFFMEMOBJ::bAddEntry+0x6c A475E3E6 win32k!PFFMEMOBJ::bLoadFontFileTable+0x81 A475BADE win32k!PUBLIC_PFTOBJ::bLoadFonts+0x2c4 Entry @ 8354B1E0 (index 72) Thread: CCA0A480 816760CB nt!VerifierExAllocatePoolWithTag+0x49 813B8C30 fltmgr!FltpAllocateIrpCtrl+0x122 813CB1C9 fltmgr!FltpCreate+0x28d 81675275 nt!IovCallDriver+0x1b1 8141EDF1 nt!IofCallDriver+0x1f 81566106 nt!IopParseDevice+0xde6 815B9916 nt!ObpLookupObjectName+0x61a 815B72D5 nt!ObOpenObjectByName+0xf7

The most recent allocation failure was induced on the GreEnableEUDC code path. Remember that GreEnableEUDC was the function that crashed in this example. Note that the allocation failure occurred in the context of thread C2638220. If you run !thread -1 and the address of the current thread is C2638220, then it is even more probable that the most recent fault was related to the current crash. You should review the source code around that area, looking for a code path that could cause that kind of crash.

Often, the current crash is related to the most recently injected failure. If looking at the most recent stack trace is not helpful, look at other stack traces. If these are not helpful either, you can use !verifier 4 80 to display the most recent 0x80 stack traces; one of these might prove to be useful.

Example 2: Use !verifier to Display Fault and Pool Allocation Counters

Driver Verifier keeps track of the number of faults injected since the last reboot. Also, Driver Verifier keeps track of the number of attempted pool allocations. These two numbers can help you understand the following:

Driver Verifier's pool allocation counters can be displayed by using !verifier as shown in Listing 21-2.

Listing 21-2: Example display of pool allocation counters

!verifier Verify Level 5 ... enabled options are: Special pool Inject random low-resource API failures Summary of All Verifier Statistics RaiseIrqls 0x2c671f AcquireSpinLocks 0xca1a02 Synch Executions 0x10a623 Trims 0x0 Pool Allocations Attempted 0x862e0e Pool Allocations Succeeded 0x8626e3 Pool Allocations Succeeded SpecialPool 0x768060 Pool Allocations With NO TAG 0x0 Pool Allocations Failed 0x34f Resource Allocations Failed Deliberately 0x3f5

Категории