CSE507 Computer-Aided Reasoning for Software Bounded Verification courses.cs.washington.edu/courses/cse507/14au/ Emina Torlak emina@cs.washington.edu
Today 2
Today Last lecture • Full functional verification with Dafny, Boogie, and Z3 2
Today Last lecture • Full functional verification with Dafny, Boogie, and Z3 Today • Bounded verification with Kodkod (Forge, Miniatur, TACO) 2
Today Last lecture • Full functional verification with Dafny, Boogie, and Z3 Today • Bounded verification with Kodkod (Forge, Miniatur, TACO) Announcements • Homework 2 is due today at 11pm • Homework 3 has been released 2
The spectrum of program validation tools Static Analysis Verification Extended Static Bounded Verification Confidence Checking & Symbolic Execution Concolic Testing & Whitebox Fuzzing Ad-hoc Testing Cost (programmer effort, time, expertise) 3
The spectrum of program validation tools Static Analysis Verification Extended E.g., Dafny, Coq, Leon: Static • support for rich (FOL+) Bounded Verification Confidence Checking & Symbolic Execution correctness properties • high annotation overhead (pre/post conditions, Concolic Testing & invariants, etc.) Whitebox Fuzzing Ad-hoc Testing Cost (programmer effort, time, expertise) 3
The spectrum of program validation tools Static Analysis Verification Extended E.g., Astree: Static • small set of fixed Bounded Verification Confidence Checking & Symbolic Execution properties (e.g., “no null dereferences”) • no annotations but must Concolic Testing & deal with false positives Whitebox Fuzzing Ad-hoc Testing Cost (programmer effort, time, expertise) 3
The spectrum of program validation tools Static Analysis Verification Extended E.g., Calysto, Saturn: Static • user-defined assertions Bounded Verification Confidence Checking & Symbolic Execution supported but optional • no annotations • some/low false positives Concolic Testing & Whitebox Fuzzing Ad-hoc Testing Cost (programmer effort, time, expertise) 3
The spectrum of program validation tools Static Analysis Verification Extended E.g., CBMC, Miniatur, Forge, Static TACO, JPF, Klee: Bounded Verification Confidence Checking & Symbolic Execution • optional user-defined harnesses, assertions, and/or FOL+ properties Concolic Testing & • no/low annotations Whitebox Fuzzing • no/low false positives Ad-hoc Testing Cost (programmer effort, time, expertise) 3
The spectrum of program validation tools Static Analysis Verification Extended E.g., SAGE, Pex, CUTE, Static DART: Bounded Verification Confidence Checking & Symbolic Execution • test harnesses and/or user-defined assertions • no annotations Concolic Testing & • no false positives Whitebox Fuzzing Ad-hoc Testing Cost (programmer effort, time, expertise) 3
The spectrum of program validation tools Static Analysis Verification Extended Static Bounded Verification Confidence Checking & Symbolic Execution Concolic Testing & Whitebox Fuzzing Ad-hoc Testing Cost (programmer effort, time, expertise) 3
Bounded verification Bound everything • Execution length • Bitwidth • Heap size (number of objects per type) Sound counterexamples but no proof • Exhaustive search within bounded scope Empirical “small-scope hypothesis” • Bugs usually have small manifestations 4
Bounded verification by example class List { Node head; void reverse() { Node near = head; n2 n1 n0 head next next next Node mid = near.next; this null data: s1 data: s2 data: null Node far = mid.next; near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } n2 n1 n0 next next next head mid.next = near; null this data: s1 data: s2 data: null head = mid; } } class Node { Node next; String data; } 5
Bounded verification by example class List { Node head; void reverse() { Node near = head; n2 n1 n0 head next next next Node mid = near.next; this null data: s1 data: s2 data: null Node far = mid.next; near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } n2 n1 n0 next next next head mid.next = near; null this data: s1 data: s2 data: null head = mid; } } Express the property either by writing a test class Node { Node next; harness or by providing FOL+ contracts . String data; } 5
Pre/post/frame conditions & data invariants class List { Node head; @requires void reverse() { this.head != null && Node near = head; this.head.next != null Node mid = near.next; Node far = mid.next; n2 n1 n0 head next next next this null data: s1 data: s2 data: null near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } mid.next = near; head = mid; } n2 n1 n0 next next next head null this } data: s1 data: s2 data: null class Node { Node next; String data; } 6
Pre/post/frame conditions & data invariants class List { Node head; @requires void reverse() { this.head != null && Node near = head; this.head.next != null Node mid = near.next; Node far = mid.next; n2 n1 n0 head next next next this null data: s1 data: s2 data: null near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } mid.next = near; head = mid; } n2 n1 n0 next next next head null this } data: s1 data: s2 data: null class Node { @invariant no ^next ∩ iden Node next; String data; } 6
Pre/post/frame conditions & data invariants class List { Node head; @requires void reverse() { this.head != null && Node near = head; this.head.next != null Node mid = near.next; Node far = mid.next; n2 n1 n0 head next next next this null data: s1 data: s2 data: null near.next = far; while (far != null ) { mid.next = near; @ensures near = mid; this.head.*next = this.old(head).*old(next) && mid = far; far = far.next; } mid.next = near; head = mid; } n2 n1 n0 next next next head null this } data: s1 data: s2 data: null class Node { @invariant no ^next ∩ iden Node next; String data; } 6
Pre/post/frame conditions & data invariants class List { Node head; @requires void reverse() { this.head != null && Node near = head; this.head.next != null Node mid = near.next; Node far = mid.next; n2 n1 n0 head next next next this null data: s1 data: s2 data: null near.next = far; while (far != null ) { mid.next = near; @ensures near = mid; this.head.*next = this.old(head).*old(next) && mid = far; let N = this.old(head).*old(next) - null | far = far.next; } next = old(next) ++ this.old(head) × null ++ ~(old(next) ∩ N × N) mid.next = near; head = mid; } n2 n1 n0 next next next head null this } data: s1 data: s2 data: null class Node { @invariant no ^next ∩ iden Node next; String data; } 6
Pre/post/frame conditions & data invariants class List { Node head; @requires Pre(this, head, next) void reverse() { this.head != null && Node near = head; this.head.next != null Node mid = near.next; Node far = mid.next; near.next = far; while (far != null ) { mid.next = near; Post(this, old(head), head, old(next), next) @ensures near = mid; this.head.*next = this.old(head).*old(next) && mid = far; let N = this.old(head).*old(next) - null | far = far.next; } next = old(next) ++ this.old(head) × null ++ ~(old(next) ∩ N × N) mid.next = near; head = mid; } } class Node { @invariant no ^next ∩ iden Inv(next) Node next; String data; } 6
A relational model of memory (heap) @invariant Inv(next) @requires Pre(this, head, next) @ensures Post(this, old(head), head, old(next), next) void reverse() { Node near = head; Node mid = near.next; Node far = mid.next; near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } mid.next = near; head = mid; } n2 n1 n0 head next next next this null data: s1 data: s2 data: null 7
A relational model of memory (heap) @invariant Inv(next) Fields as binary relations @requires Pre(this, head, next) @ensures Post(this, old(head), head, old(next), next) ‣ head : { ⟨ this, n2 ⟩ }, next : { ⟨ n2, n1 ⟩ , … } void reverse() { Node near = head; Node mid = near.next; Node far = mid.next; near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } mid.next = near; head = mid; } n2 n1 n0 head next next next this null data: s1 data: s2 data: null 7
A relational model of memory (heap) @invariant Inv(next) Fields as binary relations @requires Pre(this, head, next) @ensures Post(this, old(head), head, old(next), next) ‣ head : { ⟨ this, n2 ⟩ }, next : { ⟨ n2, n1 ⟩ , … } Types as sets (unary relations) void reverse() { Node near = head; ‣ List : { ⟨ this ⟩ }, Node : { ⟨ n0 ⟩ , ⟨ n1 ⟩ , ⟨ n2 ⟩ } Node mid = near.next; Node far = mid.next; near.next = far; while (far != null ) { mid.next = near; near = mid; mid = far; far = far.next; } mid.next = near; head = mid; } n2 n1 n0 head next next next this null data: s1 data: s2 data: null 7
Recommend
More recommend