Verification Condition Generation Jorge Sousa Pinto Departamento de Informática / Universidade do Minho jsp@di.uminho.pt www.di.uminho.pt/~jsp
Outline (1) - From Hoare Logic to VCGen algorithms: an architecture for program verification - Design by Contract and JML - Case studies
Outline (2) - Tool Demo: Caduceus + Coq (also Simplify)
From Hoare Logic to VCGen Algorithms
Hoare Logic
Hoare Logic Note side conditions in Consequence rule are often represented as premises
Consequence Rule Alternative Presentations Careful with interpretation Scope of quantification Different inference systems
Problems with System for Backward Proof Construction Two desirable properties are missing: Sub-formula property Unambiguous choice of rule The consequence rule causes ambiguity. It is always possible to strengthen (weaken) pre(post) condition. Its presence is however necessary to make possible the application of rules for skip, assignment, and while
A Goal-directed System The consequence rule can be removed by distributing sideconditions over the remaining rules The result is a system with one rule for each program construct, which can be unconditionally applied (contrary to the original system) Side conditions will have to be present for skip, assignment, and while rules
Goal-directed Hoare Logic
Goal-directed Hoare Logic How can equivalence of systems be formulated and proved?
Goal-directed Hoare Logic This system still does not enjoy sub-formula property In fact we got rid of one rule that did not enjoy the property, but another rule has lost the property Moreover the sequence command rule did not enjoy the property and still doesn t
Program Annotations User is asked to provide assertions that describe program behaviour The system now enjoys the subformula property
When working on a fully annotated program, the goal-directed Hoare system can be used to mechanically construct a unique proof tree for each program Observation: out notion of program corresponds to parsed programs, already represented as abstract syntax trees Each complete program annotation corresponds to a unique proof tree
Example: Fibonacci
Annotated Program
(continued)
Verification Conditions Side conditions of Hoare Logic rules are usually checked in the form of implication formulas called verification conditions VCs are purely first-order, not containing program constructs. Can be checked / discharged using any standard proof tool (theorem prover or proof assistant) with support for the data types of the language. Universal Closure may be required [ ]
Question What does it mean when the verification of a program fails? The HL proof tree can always be constructed, but the VCs may not all be dischargeable errors in program, specification, or annotations
P.V. Architectures How can a proof tool be used for verifying programs with Hoare Logic? Two possibilities: Encode Hoare Logic directly in proof tool and reason about program constructs Two-phase architecture: (i) use Hoare Logic to construct a proof tree a set of verification conditions (ii) use a general-purpose proof tool to discharge verification conditions and
Two-phase Architecture Hoare Logic Annotated VCGen Program??? Counter Examples Proof Tool Proof Obligations First Order Logic
Improving VG generation Intermediate annotations in sequences of commands are not necessary: they can be inferred Instead of annotations, we will resort to a tree construction strategy Different possibilities exist: for the Fibonacci example we used forward propagation of preconditions We will now look in detail at backward propagation of postconditions through the notion of weakest precondition
Example
Weakest Preconditions Given program C and a postcondition Q, we calculate an assertion prec(c,q) such that {prec(c,q)} C {Q} is valid and moreover if {P} C {Q} is valid for some P then P is stronger than prec(c,q). Thus prec(c,q) is the weakest precondition that grants the truth of postcondition Q after execution of C.
Question Can the weakest precondition of a loop be calculated statically? Not really, but for annotated loops the invariant is the weakest precondition required to prove any postcondition
Weakest Precond. Algorithm
VCGen Note [.] notation for the universal closure of an assertion
Fibonacci annotated with WP
Example
Example Many trivial VCs
Correctness of VCGen Let VC = vc ({P} C {Q}) where {P} C {Q} is an arbitrary Hoare triple. Then a proof tree can be constructed that has as leaves exactly the elements of VC (together with instances of axioms).
Improved VCGen
Example
Example
Program Annotation and Design by Contract
Why Annotate Programs? - A practical and accessible interface specification method - Specify the semantics together with the syntax - Do not worry about following a prescribed design method, as is the case with most formal methodologies - Light formal methods for everyday programmers?
Design by Contract - Dynamic checking - Test-case generation - Documentation: register design decisions and implementation steps - Static Checking - Program Verification!
Design by Contract - A software development method, initiated with Eiffel, based on contracts between clients and classes (dynamically-checked) - Client guarantees certain (pre-)conditions before invoking methods and may then assume other (post-)conditions after invocation
Design by Contract - Class must ensure certain (post-)conditions hold after methods have been called and may for this effect assume given (pre-)conditions - Advantages: reasoning/modularity; blame assignment; eliminate defensive checking (practical and efficient!!!)
JML (Java Modelling Language) - A standard annotation language for JML - Is itself very close to Java (easy to learn) - Many tools have adhered to the standard and are now JML-compliant - Imperative subset has been adapted to other languages (C)
JML Assertions preconditions: keyword requires postconditions: keyword ensures (class and loop) invariants: keywords invariant and loop_invariant
JML Assertions Added as special comments in Java files /*@... @*/ //@... Properties written as Java boolean expressions With extra operators...
JML Operators Quantification: (\forall... ;... ;...) (\exists... ;... ;...) variable value at entry: \old(...) method return value: \result
Class Invariants Universal properties of class and instance variables (valid all the time) Must be preserved by all the methods in a class Implicitly, it is as if they were part of every pre- and postcondition
Other JML Stuff exceptions (keyword signals) frame conditions pure methods: pure non_null annotations ad hoc assertions: \assert
Static Checking - Dynamic checking verifies only the execution paths followed in one run of the program - Static checking examines all possible execution paths - The location of the warnings that are issued is not where they occur (as in run-time) but where they are created - Typically unsound and incomplete to increase costeffectiveness (automatic theorem prover, not interactive)
Case Study and Exercises
Exercise 1 void swap(int X[], int a, int b) { aux = X[a]; X[a] = X[b]; X[b] = aux; } 1. Write specification 2. Prove correctness of function
Exercise 2 Recall the partition function used by the quicksort algorithm. Verify informally: 1. Write a Specification 2. Examine suggested implementation 3. Identify loop invariant 4. Check initial conditions and preservation 5. Identify loop variant 6. Check final conditions
Exercise 2 int partition (int A[], int p, int r) { x = A[r]; i = p-1; for (j=p ; j<r ; j++) if (A[j] <= x) { i++; swap(a, i, j); } swap(a, i+1, r); return i+1; }
Análise de Correcção Invariante No início de cada iteração do ciclo for tem-se para qualquer posição k do vector: 1. Se p k i então A[k] x; 2. Se i + 1 k j 1 então A[k] > x; 3. Se k = r então A[k] = x. p i j r x }{{} x }{{} >x } {{ }?? Verificar as propriedades de inicialização (j = p, i = p 1), preservação, e terminação (j = r) o que fazem as duas últimas instruções? Algorithms slide
Specification Description of final state of array (postcond.) Safety Conditions (C implementation): proper index values and memory allocation The difficult part: relation of input to output in this case: output sequence is a permutation of input sequence
A first attempt k : p k r : ( l : p l r : A[k] = B[l] A[l] = B[k] ) What s wrong with it?
Second attempt k : p k r : ( l : p l r : A[k] = B[l] ) k : p k r : ( l : p l r : B[k] = A[l] ) What s wrong with it?
Third attempt Use a logical theory for multi-sets and a function mset that abstracts an array into the multiset of its elements mset (A) = mset (B) This requires a prover with support for theories like sets, multisets, sequences or else user-defined theories
Program Annotation and Verification
Program Verification - One Possible Definition: an exhaustive, correct and complete form of static checking w.r.t. to a full specification - Provides a global certification that the program behaves as it is specified to behave
Caduceus - Program Verification Tool for C language, developed at LRI - Multi-prover; initially meant for the Coq proof assistant - Annotation language is very close to JML subset - Constructed with a generic Verification Condition Generator tool called Why
Caduceus with Coq Annotated Program Caduceus Why files + makefile Compiled Hoare Logic proof (.vo file) Coq Proof Script: Oblig. + default tactics (.v file) make... coq
Caduceus with Simplify Annotated Program Caduceus Why files + makefile Hoare Logic simplify Proof Obligations make... simplify