370 likes | 543 Views
Software Verification with Blast. Thomas A. Henzinger, Ranjit Jhala, Rupak Majumdar, George Necula, Grégoire Sutre, Wes Weimer UC Berkeley. Motivation. Verification of systems code Locking disciplines Interface specifications Essential for correct operation High rate of bugs
E N D
Software Verification with Blast Thomas A. Henzinger, Ranjit Jhala, Rupak Majumdar, George Necula, Grégoire Sutre, Wes Weimer UC Berkeley
Motivation • Verification of systems code • Locking disciplines • Interface specifications • Essential for correct operation • High rate of bugs • Temporal properties • Require path-sensitive analysis • Swamped by false positives • Really hard to check
Model Checking • Doesn’t scale to low level implementations • Can only model check “abstractions” • Requires human intervention … • Abstract – Check – Refine Loop • Microsoft SLAM Project • [Clarke et. al. 00], [Saidi 00]
Abstraction Seed Abstraction • Program • NO • YES (Trace) SAFE • Feasible • Explanation BUG ??? Abstract-Check-Refine Loop Abstract Is model unsafe ? Check Refine Why infeasible ? Infeasible
Model Checking 101 • Keep searching successors until … • Hit error states: report “bug” ! • Add no new successors: report “safe” • Could take a long time … Init ERROR STATES SYSTEM’S STATE SPACE
Init ERROR STATES Model Checking & Abstraction • Problem: Far too many states • Iterations don’t terminate ! • Solution: Abstract …
Model Checking & Abstraction • Problem: Abstraction too coarse • Solution: Refine abstraction • Make boxes smaller Init ERROR STATES
Model Checking & Abstraction • Problem: Abstraction too coarse • Solution: Refine abstraction • Make boxes smaller Init ERROR STATES
Reachable States Abstract Only Where Required • Abstraction is very expensive • Why abstract regions that are never visited ? • On-the-fly abstraction: driven by the search Init ERROR STATES
Refine Only Where Required • Why be precise everywhere ? • Don’t refine error-free regions Init ERROR STATES ERROR FREE
Refine Only Where Required • Why be precise everywhere ? • Don’t refine error-free regions • Different precision for different regions • Local Refinement : driven by the search Init ERROR STATES ERROR FREE
How to improve • Abstract only where required • Reachable state space is very sparse • Construct the abstraction on-the-fly • Use greater precision only where required • Different precisions/abstractions for different regions • Refine locally • Reuse work from earlier phases • Batch-oriented ) lose work from previous runs • Integrate the three phases • Exploit control flow structure
lock() unlock() unlock() lock() Example Example ( ) { 1: if (*) { 7: do { got_lock = 0; 8: if (*) { 9: lock(); got_lock ++; } 10: if (got_lock) { 11: unlock(); } 12: } while (*) ; } 2: do { lock(); old = new; 3: if (*) { 4: unlock(); new ++; } 5: } while ( new != old); 6: unlock (); return; } Q: Is Error Reachable ?
[>] [>] 2 7 lock(); old = new 3 [>] [>] 4 [new!=old] 5 unlock() new++ [new==old] 6 unlock() ret Example:CFA 1 Example ( ) { 1: if (*) { 7: do { got_lock = 0; 8: if (*) { 9: lock(); got_lock ++; } 10: if (got_lock) { 11: unlock(); } 12: } while (*) ; } 2: do { lock(); old = new; 3: if (*) { 4: unlock(); new ++; } 5: } while ( new != old); 6: unlock (); return; }
1 2 7 3 8 4 9 5 10 11 6 12 ret Example:CFA Example ( ) { 1: if (*) { 7: do { got_lock = 0; 8: if (*) { 9: lock(); got_lock ++; } 10: if (got_lock) { 11: unlock(); } 12: } while (*) ; } 2: do { lock(); old = new; 3: if (*) { 4: unlock(); new ++; } 5: } while ( new != old); 6: unlock (); return; } got_lock=0 [>] [>] lock(); got_lock++ [got_lock != 0] [got_lock == 0] unlock() [>] [>]
1 2 7 lock() 3 8 4 9 unlock() unlock() 5 lock() 10 11 6 12 ret Example:CFA Example ( ) { 1: if (*) { 7: do { got_lock = 0; 8: if (*) { 9: lock(); got_lock ++; } 10: if (got_lock) { 11: unlock(); } 12: } while (*) ; } 2: do { lock(); old = new; 3: if (*) { 4: unlock(); new ++; } 5: } while ( new != old); 6: unlock (); return; } Q: Is Error Reachable ?
1 LOCK=0 1 2 2 7 LOCK=0 3 8 4 9 3 LOCK=1 5 10 11 4 LOCK=1 6 12 ret 5 LOCK=0 6 LOCK=0 Err LOCK=0 Step 1: Search [>] lock(); old = new [>] unlock() new++ [new==old] Set of predicates: LOCK=0, LOCK=1 unlock()
1 LOCK=0 1 2 2 7 LOCK=0 3 8 4 9 3 LOCK=1 5 10 11 4 LOCK=1 6 12 ret 5 LOCK=0 6 LOCK=0 ops Err LOCK=0 n Err Step 2:Analyze Counterexample Q: When can: States that can = wp( >,ops) States at node n = Rn ) check: RnÆ wp( >,ops) = ? ?
1 LOCK=0 1 2 2 7 LOCK=0 3 8 4 9 3 LOCK=1 5 10 11 4 LOCK=1 6 12 ret 5 LOCK=0 6 LOCK=0 Err LOCK=0 Step 2:Analyze Counterexample LOCK=0 Ænew+1 = new lock(); old = new LOCK=1 Æ new+1 = old [>] LOCK=1 Æ new +1 = old unlock(); new++ LOCK=0 Æ new = old [new==old] LOCK=0 unlock() LOCK=0 RnÆ wp (>,ops) = ? ?
1 LOCK=0 1 2 2 7 LOCK=0 3 8 4 9 3 LOCK=1 5 10 11 4 LOCK=1 6 12 ret 5 LOCK=0 6 LOCK=0 Err LOCK=0 Step 2:Analyze Counterexample LOCK=0 Ænew+1 = new lock(); old = new LOCK=1 Æ new+1 = old [>] LOCK=1 Æ new +1 = old unlock(); new++ LOCK=0 Æ new = old Track the predicate: new = old [new==old] LOCK=0 unlock() LOCK=0
1 2 2 7 LOCK=0 3 8 4 9 3 LOCK=1 Æ new = old 5 10 11 4 LOCK=1 Æ new = old 6 12 ret 5 LOCK=0 Æ: new = old 6 2 ? LOCK=0 Æ: new = old µ LOCK =0 Step 3: Resume search 1 LOCK=0 lock(); old = new [>] unlock() new++ Set of predicates: LOCK=0, LOCK=1 [new!=old] [new==old] New predicate: new = old,
1 LOCK=0 1 2 2 7 LOCK=0 3 8 4 9 3 LOCK=1 Æ new = old 5 10 11 4 LOCK=1 Æ new = old 6 12 ret LOCK=1 Æ new=old 5 5 LOCK=0 Æ: new = old 6 2 6 1 ? ? ret LOCK=0Æ new=old Step 3: Resume search [>] [new!=old] [new==old] Set of predicates: LOCK=0, LOCK=1 unlock() LOCK=0 Æ: new = old New predicate: new = old
1 2 7 3 8 4 9 5 10 11 6 12 ret Example:CFA Example ( ) { 1: if (*) { 7: do { got_lock = 0; 8: if (*) { 9: lock(); got_lock ++; } 10: if (got_lock) { 11: unlock(); } 12: } while (*) ; } 2: do { lock(); old = new; 3: if (*) { 4: unlock(); new ++; } 5: } while ( new != old); 6: unlock (); return; } got_lock=0 [>] [>] lock(); got_lock++ [got_lock != 0] [got_lock == 0] unlock() [>] [>]
1 LOCK=0 [>] [>] 1 2 7 2 7 LOCK=0 LOCK=0 3 8 4 9 5 10 11 6 12 ret Err Step 4: Search Right Branch Set of predicates: LOCK=0, LOCK=1 New predicate: (from trace) got_lock = 0
1 LOCK=0 1 2 7 2 7 LOCK=0 LOCK=0 3 8 4 9 5 10 11 6 12 ret 2 2 2 Leaves Covered (Reuse work) Leaves covered: Avoid repeating search when paths merge LOCK=0 Æ … COVERED !
6 ret LOCK=0Æ new=old Reachability Tree LOCK=0 1 7 2 LOCK=0 LOCK=0 8 LOCK=0 Ægot_lock=0 3 LOCK=1 Ænew = old 9 LOCK=0 Æ got_lock=0 10 4 LOCK=1 Æ new = old LOCK=1 Æ got_lock!=0 10 LOCK=1 Æ new=old LOCK=0 Æ : new = old 5 12 5 11 11 ? 6 2 1 ? 12 ? 12 ? LOCK=0 Æ got_lock=0 2 8
6 ret LOCK=0Æ new=old Invariants LOCK=0 1 Regions in the tree are invariants: Invariant Inv (n) for node n = Disjunction of all node-n regions in the tree 2 LOCK=0 3 LOCK=1 Æ new = old 4 LOCK=1 Æ new = old Inv (5) is: LOCK=0 Æ: new = old Ç LOCK=1 Æ new=old Inv (6) is: LOCK=1 Æ new=old LOCK=1 Æ new=old LOCK=0 Æ : new = old 5 5 6 2 1 ? ?
1 2 3 4 5 6 ret Proof Generation • Use the invariants from the tree • Verification Conditions for correctness • Pre ) Inv (1) • Inv (e) = false for error node e • Post (Inv (j), cjk ) ) Inv (k) • These can be formalized as in PCC [new!=old] LOCK=0 Æ: new = old Ç LOCK=1 Æ new=old [new==old] • Inv (1) contains Pre as disjunct • Error node not in tree
1 2 3 4 5 6 ret Proof Generation II Prove : Post ( Inv (i) , cij ) ) Inv (j) Use the tree to break the proof: Post(AÇ B, c) ) D Ç E becomes: Post (A,c) ) D and Post (B,c) ) E [new!=old] LOCK=0 Æ: new = old Ç LOCK=1 Æ new=old Example: Post (Inv (5), new==old) ) Inv (6) Post (LOCK=0 Æ: new=old, new==old) )LOCK=1Æ new=old [new==old] LOCK=1 Æ new=old Post(LOCK=1 Æ new=old, new==old) )LOCK=1 Æ new=old
1 2 3 4 5 6 ret Proof Generation II Prove : Post ( Inv (i) , cij ) ) Inv (j) Use the tree to break the proof: Post(AÇ B, c) ) D Ç E becomes: Post (A,c) ) D and Post (B,c) ) E But these were computed in the forward search! [new!=old] LOCK=0 Æ: new = old Ç LOCK=1 Æ new=old Example: Post (Inv (5), new==old) ) Inv (6) false)LOCK=1Æ new=old [new==old] LOCK=1 Æ new=old LOCK=1 Æ new=old)LOCK=1 Æ new=old
BLAST • Berkeley Lazy Abstraction Software verification Tool • 10K Lines of Ocaml • Analyze Linux/Windows Device Drivers CIL (C ! CFA) LAZY ABSTRACTION REGION STRUCTURE Vampyre (focus) Proof Gen (PCC) Simplify (Post#) BDD Engine (Boolean ops)
start NP CallDriver SKIP1 SKIP2 return child status Skip CallDriver IPC synch MPR3 NP CallDriver prop completion PPC not pending returned MPR completion Complete request CallDriver MPR1 MPR2 DC return not Pend no prop completion synch CallDriver N/A N/A IRP accessible CallDriver start P SKIP2 Mark Pending SKIP1 Skip CallDriver IPC synch MPR3 NP CallDriver prop completion return Pending PPC not pending returned MPR completion Complete request CallDriver MPR1 MPR2 DC no prop completion CallDriver N?A From the SLAM project
Experiments Windows Drivers (IRP Spec – 22 states)
Why Abstract Lazily ? • Reach set is very sparse • Abstract on-the-fly • Only the reachable region • Requires very fast post# • Exploit Control-Flow Structure • Free partitioning of state space • Partition preds: different abstractions • Refine locally: don’t repeat old work
Problems/Future work • Engineering Issues • Program analysis • Partitioning by partial evaluation • Theory of counterexample driven refinement • for all linear and branching time logics
“BLAST! This is why I hate flying!” - Jedi Master Obi-Wan Kenobi in Episode II: Attack of the Clones, 2002