Model Checking Software with Boolean Programs Thomas Ball
Model Checking Software with Boolean Programs Thomas Ball Sriram K. Rajamani Rupak Majumdar Todd Millstein http: //research. microsoft. com/slam/
Hardware Verification Design Models, Specifications Temporal Safety Properties Toolset (Model checker) Netlist System Components
SLAM: Linking Designs to Implementations Design Models, Specifications Temporal Safety Properties Toolset Code System Components
Outline n n What is the model schema? n Boolean programs How do we model-check the model? n Bebop How do we discover the model? n Iterative refinement Future Directions
Boolean Programs § Model for representing abstractions of imperative programs in C, C#, Java, etc. § Features: § § Boolean variables Control-flow: sequencing, conditionals, looping, GOTOs Procedures § Call-by-value parameter passing § Recursion Control non-determinism
Model schema: Boolean programs bool x, y; [1] while (true) { [2] if(x == y) [3] y = } else{ [4] x = [5] y = } [6] if (? ) break; } [7] if(x == y) [8] assert (false); { !x; !y; Intent: Each boolean variable represents a predicate: • • • (i < j) (*p==i) && ( (int) p == j) (p T), where T is a “shape type” that describes acyclic linked lists [Cousot-Cousot][Graf-Saidi]
Reachability bool x, y; [1] while (true) { [2] if(x == y) [3] y = } else{ [4] x = [5] y = } [6] if (? ) break; } [7] [8] { !x; !y; if(x == y) assert (false); State: (label, valuation to variables) Finite number of states! Can compute reachable states using model checking
Inter-procedural case § Explicit stack representation is bad § Implicitly represent stack instead § Similar to § model checking push-down automata § interprocedural dataflow analysis § [Hopcroft-Ullman] [Sharir-Pnueli] [Steffen] [Knoop. Steffen] [Reps-Horwitz-Sagiv][Bouajjani-Esparza-Maler] [Esparza-Hansel-Rossmanith-Schwoon]
Results (1) § Reachability in boolean programs can be reduced to context-free language reachability § Symbolic version of [Reps-Horwitz-Sagiv, POPL’ 95] interprocedural data flow analysis n § Complexity of algorithm is O( E 2 ) E = size of interprocedural control flow graph n = max. number of variables in the scope of any label
Results (2) § Program variables + control flow § Exploit locality of variable scoping § Use standard compiler optimizations § Bebop uses hybrid representation § Explicit representation of control flow § Implicit representation of reachable states via BDDs § Hierarchical trace generation
do { //get the write lock Ke. Acquire. Spin. Lock(&dev. Ext->write. List. Lock); n. Packets. Old = n. Packets; request = dev. Ext->Write. List. Head. Va; if(request && request->status){ dev. Ext->Write. List. Head. Va = request->Next; Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); irp = request->irp; if(request->status > 0){ irp->Io. Status = STATUS_SUCCESS; irp->Io. Status. Information = request->Status; } else{ irp->Io. Status = STATUS_UNSUCCESSFUL; irp->Io. Status. Information = request->Status; } Smart. Dev. Free. Block(request); Io. Complete. Request(irp, IO_NO_INCREMENT); n. Packets++; } } while (n. Packets != n. Packets. Old); Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock);
do { //get the write lock Ke. Acquire. Spin. Lock(&dev. Ext->write. List. Lock); n. Packets. Old = n. Packets; request = dev. Ext->Write. List. Head. Va; Question: Is locking protocol respected? if(request && request->status){ dev. Ext->Write. List. Head. Va = request->Next; Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); Dynamic Analysis: irp = request->irp; if(request->status > 0){ irp->Io. Status = STATUS_SUCCESS; irp->Io. Status. Information = request->Status; } else{ irp->Io. Status = STATUS_UNSUCCESSFUL; irp->Io. Status. Information = request->Status; } Smart. Dev. Free. Block(request); Io. Complete. Request(irp, IO_NO_INCREMENT); n. Packets++; } } while (n. Packets != n. Packets. Old); Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); “Don’t know” Traditional Static Analysis: “Don’t know”
do { //get the write lock Ke. Acquire. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Acquire); n. Packets. Old = n. Packets; do { FSM(Acquire); request = dev. Ext->Write. List. Head. Va; if(request && request->status){ dev. Ext->Write. List. Head. Va = request->Next; Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); irp = request->irp; if(request->status > 0){ irp->Io. Status = STATUS_SUCCESS; irp->Io. Status. Information = request->Status; } else{ irp->Io. Status = STATUS_UNSUCCESSFUL; irp->Io. Status. Information = request->Status; } Smart. Dev. Free. Block(request); Io. Complete. Request(irp, IO_NO_INCREMENT); n. Packets++; } } while (n. Packets != n. Packets. Old); Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); if(*){ FSM(Release); if(*) { } else { } } } while(*); FSM(Release);
do { //get the write lock Ke. Acquire. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Acquire); n. Packets. Old = n. Packets; do { FSM(Acquire); (2) request = dev. Ext->Write. List. Head. Va; if(request && request->status){ dev. Ext->Write. List. Head. Va = request->Next; Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); irp = request->irp; if(request->status > 0){ irp->Io. Status = STATUS_SUCCESS; irp->Io. Status. Information = request->Status; } else{ irp->Io. Status = STATUS_UNSUCCESSFUL; irp->Io. Status. Information = request->Status; } Smart. Dev. Free. Block(request); Io. Complete. Request(irp, IO_NO_INCREMENT); n. Packets++; } } while (n. Packets != n. Packets. Old); Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); if(*){ FSM(Release); if(*) { } else { } } } while(*); FSM(Release);
do { //get the write lock do { Ke. Acquire. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Acquire); n. Packets. Old = n. Packets; FSM(Acquire); (2) request = dev. Ext->Write. List. Head. Va; if(*){ if(request && request->status){ dev. Ext->Write. List. Head. Va = request->Next; Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); irp = request->irp; if(request->status > 0){ irp->Io. Status = STATUS_SUCCESS; irp->Io. Status. Information = request->Status; } else{ irp->Io. Status = STATUS_UNSUCCESSFUL; irp->Io. Status. Information = request->Status; } Smart. Dev. Free. Block(request); Io. Complete. Request(irp, IO_NO_INCREMENT); n. Packets++; } } while (n. Packets FSM(Release); if(*) { } else { } } } while(*); != n. Packets. Old); Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release);
/*b: n. Packets. Old=n. Packets */ do { //get the write lock Ke. Acquire. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Acquire); n. Packets. Old = n. Packets; request = dev. Ext->Write. List. Head. Va; if(request && request->status){ dev. Ext->Write. List. Head. Va = request->Next; Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); irp = request->irp; if(request->status > 0){ irp->Io. Status = STATUS_SUCCESS; irp->Io. Status. Information = request->Status; } else{ irp->Io. Status = STATUS_UNSUCCESSFUL; irp->Io. Status. Information = request->Status; } Smart. Dev. Free. Block(request); Io. Complete. Request(irp, IO_NO_INCREMENT); n. Packets++; } } while (n. Packets != n. Packets. Old); Ke. Release. Spin. Lock(&dev. Ext->write. List. Lock); FSM(Release); do { FSM(Acquire); b : = 1; if(*){ FSM(Release); if(*) { } else { } b : = b ? 0 : *; } } while( !b ); FSM(Release);
P B’ B Is L reachable in B? Model checker Is L reachable in P? Yes, path p Abstractor No Return “No” Is p feasible in P? Path Simulator Yes don’t know Return “Yes”, p Return “don’t know” No, explanation
Discover and Refine Abstractions on Demand n Given a reachability query in program P n Check reachability on the boolean program abstraction B of P with bebop simcl ) on P to n Use path simulation (sex. C discover spurious counterexamples (in B) n Use C predicate abstractor (c 2 bp) to refine B
Summary – Technical § Boolean Programs § a model for representing program abstractions § Bebop § § reachability analysis for boolean programs or, a path-sensitive dataflow engine § Iterative refinement of Boolean programs § § use of path simulation (simcl) and automated predicate abstraction (c 2 bp) generate abstract explanations of path infeasibility
Making Analysis Influential w Show the analysis addresses a significant problem n Check correctness of Device Driver Code w Convince us it really works n Demonstrate on real code bases l http: //www. microsoft. com/ddk
Future Directions: Short Term w Summer n Model check a safety property of an NT driver w Autumn n n Specification language Generate source-level instrumentation directly from specification w Winter n Model check many drivers automatically, driven by a single specification
Long Term w Tackle new domains n n n Network protocols Protocols for fault-tolerant computing Com. Apps components w Extensions to framework n n Concurrency (Ball-Chaki-Rajamani) Heap data structures
The Device Driver Challenge w Device Driver Code n http: //www. microsoft. com/ddk/ w “Device Drivers, Deconstructed” n n Description of interfaces/abstractions Catalog of temporal properties l n Ball De. Line Rajamani Coming, January 2001
Open Problems w Specification language and automatic annotation of source programs n [Engler 00] w Generation of abstract predicates by widening
Conclusions w Bring reliability guarantees of hardware to system software w Encourage use of specification by linking specs to code via analysis w Build tools to enable design for reliability and testing
Software Productivity Tools Microsoft Research http: //research. microsoft. com/slam/
Open Questions w Which analyses are right for which problems? w How to get difficult analyses to scale well? n n Are there soundness/completeness tradeoffs? How can static analysis / common design patterns help guide state space search?
Open Questions (2) w Are there opportunities to combine analyses? n n Can we use a cheap flow-insensitive algorithm to focus a more expensive algorithm on juicy places? Can we use expensive local path-sensitive algorithms to improve global flow-insensitive algorithms?
- Slides: 28