Developing Drivers with the Windows Driver Foundation (Pro Developer)

SDV explores code paths in a device driver by symbolically executing the driver source code. It places a driver in a hostile environment and systematically tests all code paths by seeking violations of usage rules-KMDF or WDM rules, depending on the type of driver. The symbolic execution makes few assumptions about the state of the Windows operating system or the initial state of the driver, so SDV can exercise code in situations that are difficult to assess through traditional testing. To verify a driver, SDV performs the following three-step process:

SDV supplies its own operating system model for the verification. The verification engine exhaustively analyzes the driver code paths and attempts to prove that the driver violated the rules by interacting improperly with the operating system model that SDV supplied.

When SDV proves that the driver violated a rule, it declares a defect and assigns a Fail result to the verification. If SDV cannot prove a violation, it assigns a Pass result to the verification.

The SDV verification engine verifies one rule at a time until it has verified all selected rules. While performing the verification, SDV writes status messages to the command line, along with messages that report errors, as described in "How to View SDV Reports" later in this chapter.

About SDV Rules

SDV rules are written in a language called Specification Language for Interface Checking (of C), or SLIC. When SDV prepares a driver for verification, it uses SLIC language constructs to instrument a driver with additional C statements that describe proper interaction between the driver and the operating system model. Only SDV performs this instrumentation; you cannot do it manually. The results are applied to a copy of the driver's source code; your actual driver is not modified.

An SDV rule can include several elements:

A guardian can be specified in one of two ways:

A with guard construct

Explicitly references the guardian variable and also the procedure that provides its entry as the single watch-up point.

A watch construct

Involves an entry or exit action augmented with an argument position ($1, $2, …, $return) to implicitly reference the guardian. The set of watch-up points for the guardian consists of the watch action procedure call sites as they appear in the driver's code. Then, during verification, the rule is applied in the iterative manner, along the loop over the set of watch-up points-once for each watch-up point.

For example, the RequestCompleted.slic SDV KMDF rule checks that each request presented to the driver's default I/O queue is completed by the driver-with some exceptions-as specified by the framework. The RequestCompleted.slic rule uses the following construct:

state{ enum {INIT, OK} t = INIT; enum {INIT1, REQPRESENTED} s = INIT1; } with guard (sdv_main, hrequest)

In this example, t and s are state variables that have two possible values and are initialized by the values INIT and INIT1, respectively.

This construct tells SDV that the sdv_main procedure is the single watch-up point for the pointer variable hrequest:

} with guard (sdv_main, hrequest)

where:

In the RequestCompleted.slic rule, the following entry actions are present, among others:

fun_WDF_IO_QUEUE_IO_READ.entry[guard $2] {…} sdv_WdfRequestComplete.entry[guard $1] {…}

These actions mean that any call to the driver's EvtIoRead callback or to WdfRequestComplete will be instrumented to check the value of its second or, respectively, first parameter against the value of hrequest that is being watched.

As another example, consider the SDV WDM rule SpinLoc.slic, which checks that every spinlock is acquired and released in alternation. The rule uses the following construct:

watch sdv_KeAcquireSpinLock.exit.$1;

This rule tells SDV that the set of watch-up points includes all calls from the driver to KeAcquireSpinLock. If SDV finds two calls to KeAcquireSpinLock in the driver's source code, SDV dumps the references to these two calls into a file called Slamwatchpoints.txt and runs two separate verification sessions, one for each call.

How SDV Applies Rules to Driver Code

When you run SDV, you select which rules to verify. During verification, SDV examines every execution path in the driver code and tries to prove that the driver violates the selected rules. If SDV fails to prove a violation, it reports a Pass result-that the driver complies with the rules. If the driver has libraries, SDV analyzes every execution path in its entirety, including the path parts that belong to libraries, if the libraries have been processed with SDV before running the verification.

To verify the selected rules, SDV creates a hostile model of the driver's environment in which several worst-case scenarios can happen, such as operating system calls continually failing. SDV systematically tests all possible execution paths in the driver, looking for violations of usage rules that define proper interaction between a driver and the framework, and proper interaction between a driver and the operating system kernel. For example, request-cancellation rules for KMDF drivers specify usage rules for DDI functions that are involved in canceling a request that was presented to the driver's default I/O queue.

Certain SDV rules are preconditions for other rules. That is, if precondition rule A presents a Pass or Fail result, that result determines whether rule B applies for the driver. For example, if the result of the precondition rule FDODriver is Pass, then the FDOPowerPolicyOwnerAPI rule applies for the driver; if the FDODriver result is Fail, then the FDOPowerPolicyOwnerAPI rule does not apply for this driver. Therefore, in the latter case, you should disregard any verification results for this driver that are related to that rule.

Because SDV tests for serious errors in obscure paths that are unlikely to be encountered even in thorough testing, running SDV with all rules can take a long time and use substantial amounts of physical memory, depending on the size of the driver.

To take advantage of SDV rules for KMDF drivers, you should declare callback functions with the corresponding function role types listed in Dispatch_routines.h-and in this chapter-and defined in WDF header files. The function role types are available to your driver when you include Wdf.h.

See "How to Annotate KMDF Driver Source Code for SDV" later in this chapter for more information about how to take advantage of SDV rules in drivers. See also "KMDF Rules for SDV" later in this chapter for details about the specific rules for KMDF drivers.

 Info  See "Thorough Static Analysis of Device Drivers" from the EuroSys 2006 conference for more details-online at http://go.microsoft.com/fwlink/?LinkId=80612.

Категории