570 likes | 909 Views
Static Analysis and Verification of Drivers. Adam Shapiro Program Manager. Donn Terry Software Development Engineer PFD. Jakob Lichtenberg Software Development Engineer SDV . Session Outline. Static analysis tools: What they are Benefits PRE f ast for Drivers (PFD)
E N D
Static Analysis and Verification of Drivers Adam ShapiroProgram Manager Donn TerrySoftware Development Engineer PFD Jakob LichtenbergSoftware Development Engineer SDV
Session Outline • Static analysis tools: • What they are • Benefits • PREfast for Drivers (PFD) • Static Driver Verifier (SDV) • Summary
What Is Static Analysis? • Compile-time analysis of the source program: • Like code inspection, but performed by a tool. • Compile-time type-checking a simple example. • Looks for violations of well-defined constraints: • Procedure contracts or API contracts . • Examples of bugs found by Static Analysis: • p = NULL;…f(p); • f() requires pto be non-NULL • Completing the same IRP twice: IoCompleteRequest (IRP); ... IoCompleteRequest (IRP);
Why Static Analysis? Cheap bugs! Rule of thumb • “A defect that costs $1 to fix on the programmer’s desktop costs $100 tofix once it is incorporated into a complete program and many thousands of dollarsif it is identified only after the softwarehas been deployed in the field.” “Building a Better Bug Trap” – The Economist, June 2003
Tools That Make You More Effective • Push-button technology. • 100% path coverage: • At little cost (let a computer do it) • Quickly (minutes or hours versus weeks) • Defects are discovered early: • Even before device hardware is available • Before designing test cases • Often while still coding • Defect reports are easy to use: • A direct reference to the defective path (or point)in the source code reduces cost of debugging
Static Analysis – How Does It Work? • The tool builds an abstract model of a driver and exhaustively inspects execution along all paths: • The abstract model is simpler: it’s reduced... • It’s so much simpler that it’s possible to have it inspected (“simulated”) exhaustively. • Over-approximation of the driver: • The control part remains the same . • All paths are preserved and treated equally . • The data state is over-approximated. • if argument x is not constrained, assume any value . • if (x>0) guards the point of interest, keep track of Boolean (x>0),but not integer value of x: Boolean is simpler than integer. • if (x > 0) { IoCompleteRequest (Irp); }
Static Analysis – Not a Silver Bullet • Does not replace functional testing. • Targets violations of a given set of well-defined constraints. • Principal limitations: • It doesn’t know about every possible error. • Algorithms are based on source code abstraction and heuristics, which results in both false positives and false negatives. • It is a useful tool.
Our Static Tools for Drivers • PREfast For Drivers (PFD): • Lightweight and fast (runs in minutes). • Easy to use early in development – start early: • Use on any code that compiles. • Limited to procedure scope. • Works on any code, C and C++. • Finds many local violations. • Static Driver Verifier (SDV): • Extremely deep analysis (runs in hours). • More useful in the later stages of development: • Requires complete driver. • Works over the whole driver (inter-procedural). • Limited to WDM and KMDF and to C (more planned). • Think: Compile –Time Driver Verifier. • Finds deep bugs.
Driver Tools Relationship Ease of Use Hard Easy Reproducibility Hard A problem has been detected and Windows has been shut down to prevent damage to your computer. DRIVER_IRQL_NOT_LESS_OR_EQUAL If this is the first time you've seen this stop error screen, restart your computer. If this screen appears again, follow these steps: Check to make sure any new hardware or software is properly installed. If this is a new installation, ask your hardware or software Manufacturer for any Windows updates you might need. If problems continue, disable or remove any newly installed hardware or software. Disable BIOS memory options such as caching or shadowing. If you need to use Safe Mode to remove or disable components, restart your computer, press F8 to select Advanced Startup Options, and then select Safe Mode Technical information: *** STOP: 0x00000001 (0x0000000,00000002,0x00000000,0x00000000) Driver Verifier PREfast for drivers Static Driver Verifier Depth Complex
PFD: PREfast For Drivers • Fast (2 to 5 times compile time, usually). • Finds many “inadvertent” errors and some “hard” ones. • Works on code that compiles; doesn’t need to run. • Some things it can find: • Null pointer and uninitialized variable (along an unusual path) • Local leaks (memory and resource) • Mismatched parameters • Forgot to check result • Format/list mismatch • Misuse of IRQLs (some) • Various special cases that are easily missed (such as Cancel IRQL) • Proper use of callback/function pointers
PFD: Driver-Specific Resource Leak void LeakSample(BOOLEAN Option1) { NTSTATUS Status; KIRQL OldIrql; BufInfo *pBufInfo; KeAcquireSpinLock(MyLock,&OldIrql); //... if (Option1) { pBufInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BufInfo), 'fuB_'); if (NULL==pBufInfo) { return STATUS_NO_MEMORY; } //... KeReleaseSpinLock(MyLock, OldIrql); return STATUS_SUCCESS; } //...
PFD: Driver-Specific Resource Leak void LeakSample(BOOLEAN Option1) { NTSTATUS Status; KIRQL OldIrql; BufInfo *pBufInfo; KeAcquireSpinLock(MyLock,&OldIrql); //... if (Option1) { pBufInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BufInfo), 'fuB_'); if (NULL==pBufInfo) { return STATUS_NO_MEMORY; } //... KeReleaseSpinLock(MyLock, OldIrql); return STATUS_SUCCESS; } //... warning 28103: Leaking the resource stored in 'SpinLock:MyLock'.
PFD: Driver-Specific Resource Leak void LeakSample(BOOLEAN Option1) { NTSTATUS Status; KIRQL OldIrql; BufInfo *pBufInfo; KeAcquireSpinLock(MyLock,&OldIrql); //... if (Option1) { pBufInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(BufInfo), 'fuB_'); if (NULL==pBufInfo) { KeReleaseSpinLock(MyLock, OldIrql); return STATUS_NO_MEMORY; } //... KeReleaseSpinLock(MyLock, OldIrql); return STATUS_SUCCESS; } //…
Precisely describe the “part” you’re building and the contract that represents. Enable automatic checking. Tells tools things they can’t infer. Effective (and checked) documentation: Programmers don’t have to guess/experiment. Code and documentation don’t drift apart. Comments are nice, but… Why Annotate?Good Engineering Practice
Record and express the contract: Developers know what the contract is. Is the contract a good one? Automatic checking: that is, “cheap” bugs: The sooner a bug is found, the less expensive it is to fix. Annotated code finds many more bugs (with less noise) than un-annotated code. Code that enters test with fewer “easy” bugs makes testing far more efficient – less wasted time for finding and fixing easy bugs. Annotation Benefits
PFD: Annotations – Example wchar_t * wmemset( __out_ecount(s) wchar_t *p, __in wchar_t v, __in size_ts); __in: the parameter is input to the function __out: the parameter is output from the function __out_ecount(s): the parameter is a buffer with s elements If the parameter p doesn’t contain at least s elements, PFD will yield a warning.
PFD - Callback Annotations wdm.h typedef VOID DRIVER_CANCEL ( __in struct _DEVICE_OBJECT *DeviceObject, __in struct _IRP *Irp ); typedef DRIVER_CANCEL *PDRIVER_CANCEL; Driver.h DRIVER_CANCELMyCancel; Driver.c VOID MyCancel ( struct _DEVICE_OBJECT *DeviceObject, struct _IRP *Irp ) { … }
If your driver uses floating point, you must be very careful to protect the hardware. It’s easy to forget that you used it. It’s very hard to find in test, typically not repeatable, and blue-screen is the preferable symptom. It can span multiple functions. Problem: Floating Point
Example: Floating Point long intSqrt(long i) { return (long) sqrt((double)i); }
Example: Floating Point long intSqrt(long i) { return (long) sqrt((double)i); } … if (KeSaveFloatingPointState(b)) { … intSqrt(…) … KeRestoreFloatingPointState(b); } else // deal with error … intSqrt(…) … …
Example: Floating Point __drv_floatUsed long intSqrt(long i) { return (long) sqrt((double)i); } … if (KeSaveFloatingPointState(b)) { … intSqrt(…) … KeRestoreFloatingPointState(b); } else // deal with error … intSqrt(…) … …
Static Driver Verifier Jakob Lichtenberg Software Development Engineer Microsoft Corporation
X X Static Analysis Tools for Drivers • Two complementary technologies provided in the WDK: • PREfast for Drivers: Look inside every procedure for possible violations. • Static Driver Verifier: Look along paths, cross inter-procedural boundaries. ReadFoo ( PIRP Irp ) { PIRP p = NULL; ... if (status) { IoCompleteRequest(p); } } ReadFoo ( PIRP Irp ) { ... status = Bar (Irp); if (status) { IoCompleteRequest(Irp); } } Bar ( PIRP Irp ) { ... IoCompleteRequest(Irp); return STATUS_SUCCESS; }
Static Analysis Tools for Drivers PREfast for Drivers (PFD): • Lightweight and fast (runs in minutes). • Easy to use early in development – start early. • Use on any code that compiles. • Limited to a procedure scope; each procedure analyzed independently. • Annotations improve precision. • Works on any code, C and C++. • Finds many local violations. Static Driver Verifier (SDV): • Extremely deep analysis (runs in hours). • More useful in the later stages of development. • Requires complete driver. • Works over the whole driver, along every path, crossing inter-procedural boundaries. • Annotations not necessary. • Limited to WDM or KMDF and to C. • Finds a few deep bugs.
Static Driver Verifier • What Static Driver Verifier does: • Global analysis of entire driver. • Looks for violations of DDI constraints. • SDV for WDM: • 68 rules covering aspects such as IRPs, IRQL, Plug and Play, and synchronization rules. • New: SDV for KMDF: • 52 rules covering aspects such as DDI ordering, device initialization, request completion, and request cancellation. • Availability: from Windows Longhorn Server WDK. • Limitations: • Only pure C drivers. • Up to 50 K lines of code.
Example I/O System Irp Driver Dispatch Routine Irp Irp IoCompleteRequest Device Driver Interface
X Example I/O System Irp Driver Dispatch Routine Irp Irp IoCompleteRequest Device Driver Interface
Example • Parport driver sample in Win XP SP1 DDK
X • One defect found Example
First Completion Double Completion
Server 2003 SP1 DDK ü • Rule Passes Example
X Example IO Mngr P4CompleteRequest ( Irp ) PptDispatchClose ( Irp ) IoCompleteRequest( Irp ); PptFdoClose( Irp ) P4CompleteRequestReleaseRemLoc ( Irp )
library.c ü more_code.c driver.c X Concepts SDV
library.c ü more_code.c driver.c X Concepts SDV Rules Verification Engine OS Model
library.c ü more_code.c driver.c X Rules SDV Rules Verification Engine OS Model
Rule 1 A DDI Constraint Is…A Rule
Rule 2 …One More Rule…
Rule 3 …And Yet Another Rule
Rules • SDV comes with: • 68 WDM rules • 52 KMDF rules • Each rule captures an aspect of an interface requirements. • Rules are written in a C-like language and define: • State declarations in form of C-style variables. • Events that are associated with DDI functions.
I/O System Driver Entry Point Device Driver Interface KeRelease SpinLock KeAcquire SpinLock SpinLock Rule
I/O System Driver returns Driver called Unlocked Driver Entry Point Release Abort Acquire Release Locked Acquire Driver returns Device Driver Interface KeRelease SpinLock KeAcquire SpinLock SpinLock Rule state { enum {unlocked, locked} s = unlocked; }
I/O System Driver Entry Point Device Driver Interface KeRelease SpinLock KeAcquire SpinLock SpinLock Rule state { enum {unlocked, locked} s = unlocked; } RunDispatchFunction.exit { if (s != unlocked) abort; } KeAcquireSpinLock.entry { if (s != unlocked) abort; else s = locked; } KeReleaseSpinLock.entry { if (s != locked) abort; else s = unlocked; }
library.c ü more_code.c driver.c X Operating System Model SDV Rules Verification Engine OS Model
Operating System Model I/O System • Exercises the driver: • Calls DriverEntry • Calls Dispatch functions • Calls ISRs • Models certain aspects of the operating system state: • Like the current interrupt request level (IRQL) • Models device driver interface functions: • Like the IoCallDriver DDI Driver Device Driver Interface
Operating System Model I/O System void main() { int choice, status; … status = DriverEntry(…); … status = AddDevice(…); … … switch (choice) { case 0: status = DriverRead(…); break; case 1: status = DriverWrite(…); break; … case 28: status = DriverCreate(…); break; default: status = DriverClose(…); break; } } … Driver Read Write Create Close DriverEntry AddDevice … Device Driver Interface
Operating System Model I/O System NTSTATUS IoCallDriver( …, PIRP Irp ) { int choice; … switch (choice) { case 0: … Irp->PendingReturned = 0; return STATUS_SUCCESS; case 1: … Irp->PendingReturned = 0; return STATUS_UNSUCCESSFUL; default: … Irp->PendingReturned = 1; return STATUS_PENDING;; } } Driver UNSUCCESSFUL SUCCESS PENDING … Device Driver Interface IoCallDriver
library.c ü more_code.c driver.c X Verification Engine SDV Rules Verification Engine OS Model
Verification Engine: Example I/O System • Executes: • Your driver in the context of the operating system model. • While keeping track of the rule state. • While looking for rule violations. • Checks each and every path of the driver. • Implements worst-case behavior. • Symbolic model checker: • Strategy: Throw away many irrelevant details through aggressive, but correct, abstraction. … Driver Read Write Create Close DriverEntry AddDevice UNSUCCESSFUL SUCCESS PENDING … Device Driver Interface IoCallDriver