390 likes | 692 Views
Lazy Abstraction. Thomas A. Henzinger Ranjit Jhala Rupak Majumdar Grégoire Sutre 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
E N D
Lazy Abstraction Thomas A. Henzinger Ranjit Jhala Rupak Majumdar Grégoire Sutre 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 !
1 1 2 7 2 7 3 8 4 9 5 10 11 6 12 ret Different Abstractions Different predicates for different parts of state space Local refinement: Preserves work on left tree new = old got_lock = 0
2 LOCK=0 Ænew+1 = new LOCK=0 lock(); old = new 3 LOCK=1 [>] unlock(); new++ 4 LOCK=1 [new==old] 5 LOCK=0 unlock() 6 LOCK=0 Err LOCK=0 Predicate Discovery • Information lost in substitution • Keep substitutions explicit • Ask a proof of unsatisfiability • Pick predicates appearing in proof
2 LOCK=0 Ænew+1 = new LOCK=0 lock(); old = new 3 LOCK=1 [>] unlock(); new++ 4 LOCK=1 [new==old] 5 LOCK=0 unlock() 6 LOCK=0 Err LOCK=0 Predicate Discovery Weakest Precondition: wp(Y, x=e) ´Y [e/x] Explicit WP: wp(Y, x=e) ´9 x’. x’ = e ÆY [x’/x] LOCK = 0 Æ 9 old’ new’ LOCK’. old’ = new Æ LOCK’=0 Æ new’ = old’ Æ new’ = new’ + 1 New Predicates from proof of unsatisfiability old’ = new, new’ = old’, new’ = new + 1
Lazy abstraction • For any system, require: • Region representation • Boolean operations: [, Å, : • “Covering” check: µ • post#: Region ! Approx. succ. Region • Forward Search • pre: Region ! Exact pred. Region • Backward counterexample analysis • focus : why a trace is infeasible
CIL (C ! CFA) REGION STRUCTURE Vampyre (focus) Simplify (Post#) BDD Engine (Boolean ops) BLAST • Berkeley Lazy Abstraction Software verification Tool • 10K Lines of Ocaml • Analyze Linux/Windows Device Drivers LAZY ABSTRACTION
Experiments [Not in POPL paper] • Linux Device Drivers (Locking protocol) • 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 • Monolithic vs. Multi-model abstractions • How to partition predicates ? • Predicate-flow analyses ? • Recursion • Summaries tricky with on-the-fly search • Smarter abstractions • Heap data structures ?
:P3 :P4 :P3 P4 P3 P4 P3 :P4 :P1,:P2 P1 : x = y P2 : z = t + y :P1, P2 P1, P2 P3 : x z+1 P4 : *u = x P1, :P2 Karnaugh Map Predicate Abstraction Region Representation: formulas over predicates Set of states Abstract Set: P1P2P4Ç: P1 P2 P3 P4
:P3 :P4 :P3 P4 P3 P4 P3 :P4 :P1,:P2 P1 : x = y P2 : z = t + y :P1, P2 P1, P2 P3 : x z+1 P4 : *u = x P1, :P2 Karnaugh Map Predicate Abstraction • Box: abstract variable valuation • BoxCover(S): Set of boxes covering S • Theorem prover used to compute BoxCover
:P3 :P4 :P3 P4 P3 P4 P3 :P4 :P1,:P2 :P1, P2 P1, P2 P1, :P2 S Post#, Pre • pre(S,op) = { s | 9s’2S. s !op s’} (Weakest Precondition) • post(S,op) = { s | 9s’2S. s’!op s} (Strongest Postcondition) • Abstract Operators: post# post(S,op) µ post#(S,op) • Concrete Operators: pre Classical Weakest Precondition post post(S) post#(S)
Model Checking & Abstraction • Problem: Abstraction too coarse • Solution: Refine abstraction • Make boxes smaller