Null Dereference Verification Via Over-approximated Weakest Precondition analysis Ravichandhran Madhavan Microsoft Research, India Joint work with Raghavan Komondoor, Indian Institute of Science
Problem Definition ● Verify absence of Null dereferences ● Demand-driven ● Analyze a dereference in almost real-time ● Sound (i.e, no false negatives) ● No programmer annotations ● Reasonably precise ● Should work on real-world Java programs
Weakest (atleast once) Precondtion ● WP(p,C) ● Constraint on the initial state that ensures that C holds whenever control reaches p ● p may never be reached when WP(p,C) holds ● WP 1 (p,C) ● Constraint on the initial state that ensures that p is reached atleast once in a state satisfying C. ● WP(p,C) = ¬WP 1 (p,¬C)
Null deref verification using WP 1 1: foo(a) { 2: b = null; ➢ We need to show that WP(S4, b != null) = 3: if (a != null) true 4: b.g = 10; ➢ Equivalently, WP 1 (S4, b = null) = false 5: }
Null deref verification using WP 1 〈 a ≠ null 〉 1: foo(a) { 2: b = null; b = null 3: if (a != null) 〈 b = null ∧ a ≠ null 〉 4: b.g = 10; if(a != null) 5: } 〈 b = null 〉 b.g = 10 Dereference of b is not safe
Our approach ● Computing WP and WP 1 is undecidable ● Our approach : compute a condition that is weaker than WP 1 ϕ WP 1 ( r = null ) ϕ= false ⇒ WP 1 ( r = null )= false
Design Goals ● Perform strong updates even in the presence of aliasing ● Incorporate path-sensitivity: track relevant branches ● Perform context-sensitive inter-procedural analysis
Abstract Domain AccessPath ( AP ) → Variable.Fields ∣ Variable Fields → field.Fields ∣ ϵ Atom → AP | null Predicate → Atom op Atom ∣ true ∣ false op → = ∣ ≠ Predicate → Disjunct 2 Disjunct Domain → 2 ● The domain excludes access-paths in which a field repeats. ● Eg: 〈 a.f.g.h = null ,b = null 〉 , 〈 b.h = null 〉 ∈ Domain 〈 a.f.g.h.f = null 〉 ∉ Domain
Illustration if(y != null) T F x = t z = new ... t.next.data = z x.data = y t = t.next 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) T F x = t z = new ... t.next.data = z 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) T F x = t z = new ... 〈 z = null 〉 t.next.data = z 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 o 1 = null 〉 T F x = t z = new ... 〈 z = null 〉 t.next.data = z 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 t.next.data = z 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 t.next.data = z 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 t = t , y = null 〉 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 y = null 〉 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 y = null 〉 〈 false 〉 T F 〈 t ≠ t ,t.data = null 〉 x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 y = null 〉 , 〈 false 〉 T F 〈 false 〉 x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration if(y != null) 〈 y = null 〉 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration 〈 y = null , y ≠ null 〉 if(y != null) 〈 y = null 〉 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Illustration 〈 false 〉 if(y != null) 〈 y = null 〉 〈 false 〉 T F x = t z = new ... 〈 z = null 〉 〈 x = t , y = null 〉 , t.next.data = z 〈 x ≠ t ,t.data = null 〉 〈 t.next.data = null 〉 x.data = y t = t.next 〈 t.data = null 〉 〈 t.data = null 〉 ...= t.data.msg
Simplification rules ap = ap → true { ap 1 = ap 2, ap 1 ≠ ap 2 } → false o i = o j → false o i = null → false o i = o i → true ( over-approximation ) o i = ap 2 ap 1 = new ... ap 1 = ap 2
Simplification rules ap = ap → true { ap 1 = ap 2, ap 1 ≠ ap 2 } → false o i = o j → false o i = null → false o i = o i → true ( over-approximation ) false ap 1 = new ... ap 1 = ap 2
Abstract Interpretation Formulation ● Concrete semantics ● Domain: sets of concrete stores ordered by set inclusion ● Backward collecting semantics – set union is the join operator ● γ(ϕ)= { s ∣ s satisfies ϕ }
Abstract Interpretation Formulation ● Abstract semantics: ϕ 1 ≤ϕ 2 iff ϕ 1 ⊆ϕ 2 ● ● , but the converse does not hold ϕ 1 ⊆ϕ 2 ⇒ϕ 1 ⇒ϕ 2 ● Transfer functions and simplification rules are monotonic ● Abstract transfer functions over-approximate the concrete transfer functions
Inter-procedural analysis F() { Main { A() { ... if(*) ... ... A() D() ... else ... } B() C() } } E() { ... ... B() { C() { ... ... ... } F() C() ... ... D() { D() } ... ... 〈 r = null 〉 E() r.f = ... ... } }
Inter-procedural analysis main A D B C E F
Inter-procedural analysis main A D B C E (S, r = null) ┴ F
Inter-procedural analysis main A ┴ D φ 1 B φ 1 C E (S, r = null) ┴ F
Inter-procedural analysis main A ┴ D φ 1 B φ 1 φ 2 ┴ C E φ 2 (S, r = null) ┴ F
Inter-procedural analysis main A ┴ D φ 1 B φ 1 φ 2 φ 3 C E φ 2 (S, r = null) ┴ F
Inter-procedural analysis main A φ 4 D φ 1 B φ 1 φ 3 C E φ 2 (S, r = null) ┴ F
Inter-procedural analysis main A φ 4 D φ 1 B φ 3 C E φ 2 φ 5 (S, r = null) ┴ ┴ F φ 5
Inter-procedural analysis main A φ 4 D φ 1 B φ 3 C E φ 2 φ 5 (S, r = null) ┴ φ 6 F φ 5
Inter-procedural analysis main A φ 4 D φ 1 B φ 3 C E φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis main A φ 4 D φ 1 B (call C, φ 7 ) φ 3 C E ┴ φ 2 (S, r = null) φ 7 φ 6 φ 5 F φ 6 φ 5
Inter-procedural analysis (call B, φ 8 ) main ┴ A φ 4 D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) (S, r = null) φ 7 φ 7 φ 6 F φ 5
Inter-procedural analysis (call B, φ 8 ) main φ 9 A φ 4 D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis (call B, φ 8 ) main φ 9 (call C, φ 7 ) A φ 4 ┴ D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis (call B, φ 8 ) main φ 9 (call C, φ 7 ) φ 1 A φ 4 ┴ D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis (call B, φ 8 ) main φ 9 (call C, φ 7 ) A φ 4 φ 10 D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis (call B, φ 8 ) (call A, φ 10 ) main φ 9 ┴ (call C, φ 7 ) A φ 4 φ 10 D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis (call B, φ 8 ) (call A, φ 10 ) main φ 9 φ 11 (call C, φ 7 ) A φ 4 φ 10 D φ 1 B (call C, φ 7 ) φ 3 C E φ 8 φ 2 (S, r = null) φ 7 φ 6 F φ 5
Inter-procedural analysis φ 10 φ 11 φ 6 F() { φ 9 Main { A() { ... if(*) ... ... A() φ 4 D() ... else ... φ 1 } φ 5 B() C() φ 3 φ 8 } } E() { ... φ 7 ... B() { C() { φ 7 ... ... ... φ 4 φ 2 } F() C() ... ... D() { D() } ... ... 〈 r = null 〉 E() r.f = ... ... } } φ 1
Recommend
More recommend