From Under-approximations to Over-approximations and Back Complementary material By Yuri Meshman yurime@cs.technion.ac.il Example Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Assume we have the following code example. In this case, the ERROR label is not reachable, and we want to prove that with predicate abstraction. First step: we want to know what are all the reachable locations. ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ v1 v2 v3 v4 v5 v6 v2’ v7 v3' v8 v9 ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ where v1 (V, E, π£ππ ) − is a directed acyclic graph v2 π – is a map from nodes to control locations (several nodes can map to the same pc) v3 v4 v5 In the graph example π£π maps to the control reaching line i of code. Apostrophes are used to distinguish different nodes mapped to the same revisited line (e.g. π£2 , π£2 ′). v6 v2’ v7 v3' v8 v9 ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ where v1 π – is a map from edges (E) to actions (instructions) of the program v2 v3 v4 v5 In the graph example π(π£1 , π£2 )=“i=0,x=0;” v6 v2’ v7 v3' v8 v9 ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ where v1 {π‘ππ’π} {π‘ππ’π} v2 v3 {π‘ππ’π} π – is a map from nodes (V) to formulas over program variables. {π‘ππ’π} v4 v5 {π‘ππ’π} In the graph example v3' v6 {π‘ππ’π} v2’ {π‘ππ’π} option1 : all true – represents reachable locations. v7 {π‘ππ’π} {π‘ππ’π} v8 {π‘ππ’π} v9 {π‘ππ’π} ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ where v1 {π‘ππ’π} {π₯ ≥ 0} v2 v3 {π₯ ≥ 0} π – is a map from nodes (V) to formulas over program variables. {π₯ ≥ 0} v4 v5 {π₯ ≥ 0 ∧ π ≥ 0} In the graph example v3' v6 {π₯ ≥ 0} v2’ {π₯ ≥ 0} option2: general formulas over variables – abstracts variables values reaching this location. v7 {π‘ππ’π} {ππππ π} v8 {π₯ ≥ 0} v9 {π‘ππ’π} ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ where v1 {π‘ππ’π} β – an ancestor relation over the nodes {π‘ππ’π} v2 v3 Used to define fixed point, and covered vertexes. If π£2′ is covered by π£2, we don’t need {π‘ππ’π} to explore more iterations of the loop. {π‘ππ’π} v4 v5 {π‘ππ’π} In the graph example v3' v6 {π‘ππ’π} v2’ {π‘ππ’π} v7 {π‘ππ’π} {π‘ππ’π} v8 π£2′ is covered by π£2 if: 1. π£2 β π£2′, 2. π£2′ is dominated by π£2 (all paths from π£π = π£1 pass through it) {π‘ππ’π} 3. π v2 = π(v2′ ) same code line 4. π v2′ → π(v2) – the label for v2′ is v9 {π‘ππ’π} subsumed by v2 label. ARG Definiton Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We want to build an abstract reachability graph for it. ARG: π΄ = V, E, π£ππ , π, π, π, β, βπ‘ where v1 {π‘ππ’π} {π‘ππ’π} v2 v3 {π‘ππ’π} βπ‘ – a fixed linearization of the topological order. Gives us the order by which to traverse the graph. {π‘ππ’π} v4 v5 {π‘ππ’π} In the graph example (one option) v3' v6 {π‘ππ’π} v2’ {π‘ππ’π} π£2′ βπ‘ π£6 βπ‘ π£4 βπ‘ π£5 βπ‘ π£3 βπ‘ π£2. v7 {π‘ππ’π} {π‘ππ’π} v8 {π‘ππ’π} v9 {π‘ππ’π} Post operator in abstract interpretation: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Post operator: Given: - An abstract state u - An operation (instruction from code) - An abstraction level (such as set of predicates) Returns: The successor state abstraction. Post operator in abstract interpretation: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Post operator: Given: - An abstract state u - An operation (instruction from code) - An abstraction level (such as set of predicates) Returns: The successor state abstraction. Definition: Post(u,v)=Ο such that: π π’ ∧ π π’, π£ ⇒ π` Where π π’ is the abstraction of state π’. π π’, π£ is the instruction from code and its interpretation under the abstraction Post operator in abstract interpretation: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Post operator: Given: - An abstract state u - An operation (instruction from code) - An abstraction level (such as set of predicates) Returns: The successor state abstraction. Example Assume you have predicates P1:(i<n) P2:(i<=n) You want to know their values after “i=i+1” (P1`,P2`) on an abstract edge (u,v) If only P1 was true before “i=i+1” we don’t know P1`. -But we know that P2` will be true. -If P1 was False that will mean i>=n held before “i=i+1” which will mean P1 and P2 will be false after it. -And so on.. Post operator in abstract interpretation: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Post operator: Given: - An abstract state u - An operation (instruction from code) - An abstraction level (such as set of predicates) Returns: The successor state abstraction. Example Assume you have predicates P1:(i<n) P2:(i<=n) You want to know their values after “i=i+1” (P1`,P2`) on an abstract edge (u,v) P1’= if ¬P1 then F else unknown - P2’= if P1 then T else if ¬ P1 then F else unknown Post operator run example Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; P1:(i<n) P2:(i<=n) v1 v2 {π‘ππ’π} {π‘ππ’π} The transition from v1 to v2 doesn’t change the predicates Post(v1,v2)=true Post operator run example Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; P1:(i<n) P2:(i<=n) v1 v2 v3 {π‘ππ’π} {π‘ππ’π} {π < π ∧ π ≤ π} The transition from v2 to v3 sets both predicates to true Post(v2,v3)=P1∧P2 Post operator run example Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; P1:(i<n) P2:(i<=n) v1 v2 v3 {π < π ∧ π ≤ π} v4 {π‘ππ’π} {π‘ππ’π} {π < π ∧ π ≤ π} v5 {π < π ∧ π ≤ π} The transition from v3 to v4 or from v3 to v5 doesn’t change the predicates … Post operator run example Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; P1:(i<n) P2:(i<=n) v1 v2 v3 {π < π ∧ π ≤ π} v4 {π‘ππ’π} {π‘ππ’π} {π < π ∧ π ≤ π} v5 v6 {π < π ∧ π ≤ π} {π < π ∧ π ≤ π} The transition from v3 to v4 or v5 doesn’t change the predicates And so does the transition from v4 to v6 or from v5 to v6. So their join is the same. Post operator run example Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; P1:(i<n) P2:(i<=n) v1 v2 v3 {π < π ∧ π ≤ π} v4 {π‘ππ’π} {π‘ππ’π} {π < π ∧ π ≤ π} v5 {π < π ∧ π ≤ π} v6 {π < π ∧ π ≤ π} v2’ {π ≤ π} … P1’= if ¬P1 then F else unknown - P2’= if P1 then T else if (¬ P1 ∧ P2) then F else unknown The transition from v6 to v2’ is as previously discussed Under approximation driven verification: Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; For UD – Post operator will always return true. And we will see refinement, using interpolants. Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 {π‘ππ’π} An initial node π£1 is created and given the label true. π£1 has a single successor π£2 which we will continue to explore. Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 π£1 has a single successor π£2 and as previously mentioned, the Post operator will return true. π£2 has two possible successors, we will continue to explore π£3 for now {π‘ππ’π} {π‘ππ’π} v3 v7 Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 v3 {π‘ππ’π} v4 v3' Post operator will return true for π£3. And in that fashion, the exploration will continue until finishing the loop iteration and reaching the beginning of the loop a second time – a node π£2′. {π‘ππ’π} {π‘ππ’π} π£2′ has two sons, π£3′ – which indicates a second iteration of the loop and π£7 – which indicates exiting the loop after one iteration or more. {π‘ππ’π} v5 {π‘ππ’π} v6 {π‘ππ’π} v2’ {π‘ππ’π} v7 Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 {π‘ππ’π} {π‘ππ’π} v2 v3 {π‘ππ’π} {π‘ππ’π} v4 v3' π£2′-s label is subsumed by the one of π£2 meaning the exploration of π£3′ will not provide new information, and its label will be the same as the one of π£3. This is indicated by the black arrow from π£2′ to π£2. v5 {π‘ππ’π} v6 {π‘ππ’π} v2’ {π‘ππ’π} {π‘ππ’π} v7 Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 {π‘ππ’π} {π‘ππ’π} v2 v3 {π‘ππ’π} {π‘ππ’π} v4 v3' After finishing exploring all the paths, the label of the error node π£8 is not false. So we want to check: 1. if there is a concrete counter part to the 2 paths π£1 → β― → π£8. 2. if not reachable, use interpolants to find new labels that capture why those paths are not reachable. v5 {π‘ππ’π} v6 {π‘ππ’π} v2’ {π‘ππ’π} We describe next, how this Counter Example Guided Abstraction Refinement (CEGAR) phase is done. v7 {π‘ππ’π} {π‘ππ’π} v8 {π‘ππ’π} v9 {π‘ππ’π} Building a formula for CEGAR Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; We ignore all nodes and edges irrelevant to the abstract path to err. And, we add a boolean variable to each node -- for convenience it will be the name of the node. v1 v2 Intuitively, if π£1, π£2, π£3, π£4, π£6, π£2′ , π£7 πππ π£8 are all true then this path will be feasible under concrete execution. v3 v4 v5 Next, we add formulas for edges. Similar to the way it would have been done for Bounded Model Checking. v6 v2’ v7 v8 Building a formula for CEGAR We use Static Single Assignment (SSA) Form. Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Definition: A program is in SSA form if an assignment to each variable appears at most once in its syntax. v1 v2 v3 v4 Therefore we rename variables for which assignments appear more then once. “π₯“ will be π₯0 at lines 1—3 will become π₯1 at line 4 π₯2 at line 5 etc. v5 v6 v2’ v7 v8 Building a formula for CEGAR Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; “6.i = i + 1;” will translate to a formula on the edge π£6, π£2′ : ππππππ π£6, π£2′ = (π1 = π0 + 1) We use the path formulas to capture error execution in the ARG: π6 : π£6 ⇒ (ππππππ π£6, π£2′ ∧ π£2′) v1 v2 v3 v4 v5 v6 v2’ v7 v8 Meaning if π£6 is reached then π(π£6,π£2′) will be taken and π£2′ will be reached. To avoid name conflicts each time a variable appears on left side of an assignment it receives a new subscript (this is SSA). Such as for ππππππ π£6, π£2′ . Building a formula for CEGAR Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; For the graph example we will receive: π1 : π£1 ⇒ (π0 = 0 ∧ π₯0 = 0 ∧ π£2) π2 : π£2 ⇒ ((π0 < π ∧ π£3) ∨ π0 ≥ π ∧ π₯4 = π₯0 ∧ π£7 ) π3 : π£3 ⇒ ( π0 ≤ 2 ∧ π£4 ∨ π0 > 2 ∧ π£5 ) π4 : π£4 ⇒ (π₯1 = 0 ∧ π₯3 = π₯1 ∧ π£6) π5 : π£5 ⇒ (π₯2 = π0 ∧ π₯3 = π₯2 ∧ π£6) π6 : π£6 ⇒ (π1 = π0 + 1 ∧ π£2’) π2 ′: π£2′ ⇒ (π1 ≥ π ∧ π₯4 = π₯3 ∧ π£7) π7 : π£7 ⇒ (π₯4 < 0 ∧ π£8) v1 v2 v3 v4 v5 v6 v2’ v7 v8 The formula π£1 ∧ π1 ∧ π2 ∧ π3 ∧ π4 ∧ π5 ∧ π6 ∧ π2 ′ ∧ π7 is UNSAT Solving the formula for CEGAR Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Definition An interpolant for π΄ ∧ π΅(= ππππ΄π) is πΌ = πΌππ‘ π΄, π΅ such that: 1. π΄ ⇒ πΌ 2. πΌ ∧ π΅ = ππππ΄π 3. πΌ is over the intersection of the variables of π΅ and π΄. Solving the formula for CEGAR Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; Definition An interpolant for π΄ ∧ π΅(= ππππ΄π) is πΌ = πΌππ‘ π΄, π΅ such that: 1. π΄ ⇒ πΌ 2. πΌ ∧ π΅ = ππππ΄π 3. πΌ is over the intersection of the variables of π΅ and π΄. Note: In the following slides links appear to implementation of the formulas in iz3 (for interpolants) and z3 (for general formulas). Pressing the links opens the online z3 or iz3 tool, and pressing play at the opened site should calculate the solutions. Solving the formula for CEGAR An interpolant for π΄ ∧ π΅(= ππππ΄π) is πΌ = πΌππ‘ π΄, π΅ such that: 1. π΄ ⇒ πΌ Foo(int n): 2. πΌ ∧ π΅ = ππππ΄π 3. πΌ is over the intersection of the variables of π΅ and π΄. 1. i=0,x=0; We have: 2. while (i<n) π£1 ∧ π1 ∧ π2 ∧ π3 ∧ π4 ∧ π5 ∧ π2 ′ ∧ π7 3. if (i <= 2) is UNSAT 4. x = 0; else To derive a new label for π£7 we can 5. x = i; calculate an interpolant for 6. i = i + 1; π΅ = π7 and 7. If (x < 0) ′ A = π£1 ∧ π ∧ π ∧ π ∧ π ∧ π ∧ π 1 2 3 4 5 2 8. ERROR βπ‘π‘π://πππ π4ππ’π. πππ/ππ3/π‘π§π A 9. return; We get: I7 = Int A, B = π£7 ∧ π₯4 ≥ 0 v1 v2 v3 v4 v5 v6 v2’ v7 πΌ7 B v8 Solving the formula for CEGAR To derive a new label for π£2′ we can calculate an interpolant for π΅ = π2′ ∧ π7 and A = π£1 ∧ π1 ∧ π2 ∧ π3 ∧ π4 ∧ π5 ∧ π6 http://rise4fun.com/iZ3/5b In that case we will receive: (after transforming to nnf ) πΌ2′ = ( π₯4 ≥ 0 ∨ π₯4! = π₯3 ) ∧ π£2′) ∨ ( π₯4 ≥ 0 ∧ π£7) Informally it means that either execution reaches π£2′ with π₯4 ≥ 0 or it reaches π£7 with π₯4 ≥ 0 . Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 v3 A v4 v5 v6 v2’ πΌ2′ v7 B v8 The resulting formula needs cleaning to get a label for π£6. Cleaning the formula of CEGAR πΌ2′ = ( π₯4 ≥ 0 ∨ π₯4! = π₯3 ) ∧ π£2′) ∨ ( π₯4 ≥ 0 ∧ π£7) We want to extract for v2′ the label (π₯3 Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 v3 A v4 v5 v6 v2’ πΌ2′ v7 B v8 Cleaning the formula of CEGAR If we return to the equations we got interpolants from π₯4 is relevant for π£7 π₯0 is relevant for π£2 ππ is relevant for ππ′ ππ is relevant for π7 B πΌ2′ = ( π₯4 ≥ 0 ∨ π₯4! = π₯3 ) ∧ π£2′) ∨ ( π₯4 ≥ 0 ∧ π£7) We want to extract for v2′ the label π₯3 ≥ 0 . Why x3? π1 : π£1 ⇒ (π0 = 0 ∧ π₯0 = 0 ∧ π£2) π2 : π£2 ⇒ ((π0 < π ∧ π£3) ∨ π0 ≥ π ∧ π₯4 = π₯0 ∧ π£7 ) π3 : π£3 ⇒ ( π0 ≤ 2 ∧ π£4 ∨ π0 > 2 ∧ π£5 ) π4 : π£4 ⇒ (π₯1 = 0 ∧ π₯3 = π₯1 ∧ π£6) π5 : π£5 ⇒ (π₯2 = π0 ∧ π₯3 = π₯2 ∧ π£6) π6 : π£6 ⇒ (π1 = π0 + 1 ∧ π£2’) π2 ′: π£2′ ⇒ (π1 ≥ π ∧ π₯4 = π₯3 ∧ π£7) π7 : π£7 ⇒ (π₯4 < 0 ∧ π£8) Cleaning the formula of CEGAR πΌ2′ = ( π₯4 ≥ 0 ∨ π₯4! = π₯3 ) ∧ π£2′) ∨ ( π₯4 ≥ 0 ∧ π£7) We want to extract for v2′ the label π₯3 ≥ 0 . To do so: we will quantify all the variables out of π£2′ scope - in this case π₯4; and quantify all node-variables other then π£2′ - in this case π£7. To remove the π£2′ variable we set it to true. http://rise4fun.com/Z3/d8km And so we receive π₯3 ≥ 0 . (actually π₯3 > −1 ) Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 v3 A v4 v5 v6 v2’ πΌ2′ v7 B v8 Cleaning the formula of CEGAR πΆπΏπΈπ΄π πΌπ β ∀ π₯ π₯ ∈ π£ππ πΌπ ∧ ¬πππππππ π₯, π’π ⋅ ∀{ππ’π |π’π ∈ π} ⋅ πΌπ [ππ’π ← π] Where π£ππ πΌπ is the set of variables and ππ’π the boolean variable we added. (both were π£π so far) Cleaning the formula of CEGAR πΆπΏπΈπ΄π πΌπ β ∀ π₯ π₯ ∈ π£ππ πΌπ ∧ ¬πππππππ π₯, π’π ⋅ ∀{ππ’π |π’π ∈ π} ⋅ πΌπ [ππ’π ← π] Where π£ππ πΌπ is the set of variables and ππ’π the boolean variable we added. (both were π£π so far) ¬πππππππ π₯, π’π means variables relevant to that node. Cleaning the formula of CEGAR πΆπΏπΈπ΄π πΌπ β ∀ π₯ π₯ ∈ π£ππ πΌπ ∧ ¬πππππππ π₯, π’π ⋅ ∀{ππ’π |π’π ∈ π} ⋅ πΌπ [ππ’π ← π] Where π£ππ πΌπ is the set of variables and ππ’π the boolean variable we added. (both were π£π so far) ¬πππππππ π₯, π’π means variables relevant to that node. Cleaning the formula of CEGAR πΆπΏπΈπ΄π πΌπ β ∀ π₯ π₯ ∈ π£ππ πΌπ ∧ ¬πππππππ π₯, π’π ⋅ ∀{ππ’π |π’π ∈ π} ⋅ πΌπ [ππ’π ← π] Where π£ππ πΌπ is the set of variables and ππ’π the boolean variable we added. (both were π£π so far) ¬πππππππ π₯, π’π means variables relevant to that node. Why is it quantified ∀ for things we want to disappear? Cleaning the formula of CEGAR πΆπΏπΈπ΄π πΌπ β ∀ π₯ π₯ ∈ π£ππ πΌπ ∧ ¬πππππππ π₯, π’π ⋅ ∀{ππ’π |π’π ∈ π} ⋅ πΌπ [ππ’π ← π] Where π£ππ πΌπ is the set of variables and ππ’π the boolean variable we added. (both were π£π so far) ¬πππππππ π₯, π’π means variables relevant to that node. Why is it quantified ∀ for things we want to disappear? For example we did: ∀π£7. πΌ2′ = ∀π£7. ( π₯4 ≥ 0 ∨ π₯4! = π₯3 ) ∧ π£2′) ∨ ( π₯4 ≥ 0 ∧ π£7) We wanted the invariant that holds at node π£2′ regardless of whether π£7 was reachable or not. So we search solution both for when π£7 = π(reachable) and when π£7 = πΉ. Cleaning the formula of CEGAR πΆπΏπΈπ΄π πΌπ β ∀ π₯ π₯ ∈ π£ππ πΌπ ∧ ¬πππππππ π₯, π’π Foo(int n): ⋅ ∀{ππ’π |π’π ∈ π} ⋅ πΌπ [ππ’π ← π] Where π£ππ πΌπ is the set of variables and ππ’π the boolean variable we added. (both were π£π so far) 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; πβπππππ 3(from the paper) Let πΌ′π = πΆπΏπΈπ΄π(πΌπ ). a. If k=1 then πΌ′π ≡ π‘ππ’π and if k=n then πΌ′π ≡ ππππ π b. For any two nodes π’π , π’π ∈ π s.t. π’π , π’π ∈ πΈ : πΌ′π ∧ ππππππ π’π , π’π ⇒ πΌ′π v1 v2 v3 v4 v5 v6 Where ππππππ π’π , π’π is the formula on the edge as shown previously. v2’ v7 v8 Back to Under approximation driven verification: Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; After cleaning we get a new label per each node. v1 {π‘ππ’π} {π₯ ≥ 0} v2 v3 {π₯ ≥ 0} {π₯ ≥ 0} v4 v3' If the label of v2′ is not still subsumed by the label of π£2, we continue to explore π£3′ and iterations 2,3 etc. of the loop. With Post operator returning true as a label for each new node. v5 {π₯ ≥ 0 ∧ π ≥ 0} v6 {π₯ ≥ 0} v2’ {π₯ ≥ 0} In this case, the label of v2′ is still subsumed by the label of π£2 so the algorithm terminates. v7 {π‘ππ’π} {ππππ π} v8 {π₯ ≥ 0} v9 {π‘ππ’π} Over approximation driven verification: Over approximation driven verification: Assuming we started with operator Post as true, and refinement staged returned as described before. Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 {π₯ ≥ 0} v2 v3 {π₯ ≥ 0} {π₯ ≥ 0} v4 v3' We take the predicates it used, in this case π₯ ≥ 0, π ≥ 0 an recalculate Post operator as described before. {π‘ππ’π} v5 {π₯ ≥ 0 ∧ π ≥ 0} v6 {π₯ ≥ 0} v2’ {π₯ ≥ 0} v7 {π‘ππ’π} {ππππ π} v8 {π₯ ≥ 0} v9 {π‘ππ’π} Over approximation driven verification: Statement “i=0,x=0;” sets both predicates to true. Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 {π₯ ≥ 0 ∧ π ≥ 0} {π₯ ≥ 0 ∧ π ≥ 0} {π₯ ≥ 0 ∧ π ≥ 0} And they stay true through the rest of the program. {π‘ππ’π} {π₯ ≥ 0 ∧ π ≥ 0} v3 v4 v5 {π₯ ≥ 0 ∧ π ≥ 0} v6 {π₯ ≥ 0 ∧ π ≥ 0} v2’ {π₯ ≥ 0 ∧ π ≥ 0} {π₯ ≥ 0 ∧ π ≥ 0} v7 v3' {ππππ π} v8 v9 {π₯ ≥ 0 ∧ π ≥ 0} UFO: UFO: In this paper the authors start with UD and after CEGAR continue with the new Post operator they get. Foo(int n): 1. i=0,x=0; 2. while (i<n) 3. if (i <= 2) 4. x = 0; else 5. x = i; 6. i = i + 1; 7. If (x < 0) 8. ERROR 9. return; v1 v2 v3 {π₯ ≥ 0} v4 {π‘ππ’π} π₯ ≥0∧π ≥0 ? Meaning, if π£2 was not still subsumed by the label of π£2 they would have continued exploring from π£2′ with post operator for π₯ ≥ 0, π ≥ 0 . {π‘ππ’π} {π₯ ≥ 0} {π₯ ≥ 0} v5 {π₯ ≥ 0 ∧ π ≥ 0} v6 {π₯ ≥ 0} v2’ {π₯ ≥ 0} v7 v3' {ππππ π} v8 {π₯ ≥ 0} v9 {π‘ππ’π} Boolean/Cartezian Predicate Abstraction Boolean Predicate Abstraction Given predicates π1 , π2 , … , ππ we represent them using boolean vectors (π1 , π2 , … , ππ ) where ππ = π‘ππ’π ↔ ππ = π‘ππ’π. π, π, π , (π1 ∧ π2 ∧ π3) ∨ (¬π1 ∧ π2 ∧ π3) ∨ (π1 ∧ ¬π2 ∧ π3) πΉ, π, π , π, πΉ, π We will have 2π possible states per each program counter location. Cartesian Predicate Abstraction We represent a cross product π1 × π2 × β― × ππ . At each location we store separately per each predicate if it is π‘ππ’π, ππππ π. If the predicate can be both we store “∗”. (π1 ∧ π2 ∧ π3) ∨ (¬π1 ∧ π2 ∧ π3) ∨ (π1 ∧ ¬π2 ∧ π3) (Note that (¬π1 ∧ ¬π2 ) is now also part of the state.) (∗,∗, π) A more compact representation (compared to Boolean) but we loose precision. Results • 105 programs in benchmark • Compared with Wolverine http://www.cprover.org/wolverine/ • 5 versions of UFO 1. 2. 3. 4. 5. Pure UD called ufoNo (Post returns true) With Cartesian Predicate abstraction called ufoCP With Boolean Predicate abstraction called ufoBP Pure OD with Cartesian Predicate abstraction called CP Pure OD with Boolean Predicate abstraction called BP • Reports results for instances that should verify (#Safe) number of instances solved. and for instance where an error should be discovered (#Unsafe) number of instances solved. Results Results • UFO performs much better then Wolverine • cpUFO performs significantly better than all other UFO configurations. • In the next slide we go deeper in to results and per example first for #SAFE instances and then for #UNSAFE • Benchmarks of token ring protocols and SSH servers various hand shaking protocols. • Fastest time at each line emphasized Results a closer look (Safe) Results a closer look (Safe) • Number of refinements goes down as you go down the predicate abstraction • CP failed for all but 3 examples so wasn’t included in results. • No one clear winner in terms of time. Can be seen also from the Unsafe results. Results a closer look (UnSafe) FIN