Demand-driven inference of loop invariants in a theorem prover K. Rustan M. Leino Microsoft Research, Redmond, WA, USA joint work with Francesco Logozzo École Polytechnique, Paris, France Spec# joint work with Mike Barnett, Robert DeLine, Manuel Fähndrich, Wolfram Schulte, and Herman Venter 3 April 2005 Invited talk, AVIS 2005 Edinburgh, Scotland, UK
Software engineering problem Building and maintaining large systems that are correct
Approach Specifications record design decisions bridge intent and code Tools amplify human effort manage details find inconsistencies ensure quality
Spec# Experimental mix of contracts and tool support Aimed at experienced developers who know the high cost of testing and maintenance Superset of C# non-null types pre- and postconditions object invariants Tool support more type checking compiler-emitted run-time checks static program verification contracts everywhere C# into the future type checking run-time checks static verification degree of checking, effort
Spec# demo
Basic architecture of a static verifier program with specifications verification condition generator verification condition theorem prover “correct” or list of errors
Spec# static verifier architecture Spec# compiler MSIL (“bytecode”) Spec# static program verifier translator inference engine Boogie PL computes invariants over-approximates V.C. generator verification condition high precision needs invariants automatic theorem prover “correct” or list of errors
Predicate abstraction and refinement e.g.: Graf & Saïdi, SLAM, BLAST, … correct model checker boolean program abstract trace predicate abstraction concrete trace C program predicates feasible? no yes error message predicate refinement
Lemmas-by-demand theorem proving e.g.: Verifun, de Moura & Rueß, CVC Lite, Zap, … unsatisfiable SAT solver propositional formula monome conjunction of input literals input formula lemmas consistent with theories? no yes conflict-clause generation satisfiable
Static program verification valid theorem prover verification condition counterexample verification condition generation program trace loop invariants program error message
Loop invariants on demand valid theorem prover verification condition counterexample verification condition generation program trace loop invariants program give up? no yes more precise (stronger or context sensitive) inference error message
Generating VC once valid theorem prover formula counterexample give up? no yes program more precise inference verification condition (VC) properties about loop invariants program trace error message
source language intermediate language passive command verification condition S,T ::= x := E | assert E | S ; T | if E then S else T end | while E do S end
source language intermediate language passive command verification condition C,D ::= x := E | assert E | assume E | C ; D | C [] D | while * do S end
Tr[ while E do S end ] = while * do assume E ; Tr[ S ] end ; assume ¬E source language intermediate language passive command verification condition Tr[ x := E ] = x := E Tr[ assert E ] = assert E Tr[ S;T ] = Tr[ S ] ; Tr[ T ] Tr[ if E then S else T end ] = ( assume E ; Tr[ S ] [] assume ¬E ; Tr[ T ] ) Tr[ while E do S end ] = while * do assume E ; Tr[ S ] end ; assume ¬E
Tr[ x := E ] = x := E Tr[ assert E ] = assert E source language intermediate language passive command verification condition Tr[ x := E ] = x := E Tr[ assert E ] = assert E Tr[ S;T ] = Tr[ S ] ; Tr[ T ] Tr[ if E then S else T end ] = ( assume E ; Tr[ S ] [] assume ¬E ; Tr[ T ] ) Tr[ while E do S end ] = while * do assume E ; Tr[ S ] end ; ( assume E ; Tr[ S ] ; assume false [] assume ¬E ) x := * ; assume J
variation on Single Static Assignment (SSA) form Examples: source language intermediate language passive command verification condition variation on Single Static Assignment (SSA) form Examples: if name of x before assert E is x0, then translate assert E into: assert E[x0 / x] if name of x before x := E is x0, then make up a new name x1 and translate x := E into: assume x1 = E[x0 / x]
source language intermediate language passive command verification condition if name of x is x0 after S and x1 after T , then make up a new name x2 and translate S [] T into: S’ ; assume x2 = x0 [] T’ ; assume x2 = x1 if name of x is x0 before while * do S end , then make up a new name x1 and translate the loop into: assume J(x0, x1) where J is an uninterpreted predicate symbol
wp( S;T, Q ) = wp( S, wp( T, Q )) source language intermediate language passive command verification condition wp( assert E, Q ) = E Q wp( assume E, Q ) = E Q wp( S;T, Q ) = wp( S, wp( T, Q )) wp( S [] T, Q ) = wp( S, Q ) wp( T, Q )
Example finding index of minimum element in an array m := 0; x := 0; while x < N do if * then m := x end; x := x + 1 end; if N > 0 then assert 0 ≤ m < N end
Example: passive command assume m0 = 0; assume x0 = 0; assume J(m0, m1, x0, x1); ( assume x1 < N ; ( assume m2 = m1 [] assume m2 = x1 ); assume x2 = x1 + 1; assume false [] assume ¬ (x1 < N) ); ( assume N > 0 ; assert 0 ≤ m1 < N [] assume ¬ (N > 0) )
Example: from monome to lemma M : m0 = 0 x0 = 0 J(m0, m1, x0, x1) ¬ (x1 < N) N > 0 ¬(0 ≤ m1 < N) On entry to the loop, the names m0, m1, N are in scope, about which M says: m0 = 0 x0 = 0 N > 0 Thus, assuming the condition: m0 = 0 x0 = 0 N > 0 m0 = m x0 = x on entry to the loop, an abstract interpreter may infer the following loop invariant: 0 = m0 ≤ m < N 0 = x0 ≤ x ≤ N Thus, the abstract interpreter produces the following lemma about J: m0 = 0 x0 = 0 N > 0 J(m0, m1, x0, x1) 0 = m0 ≤ m1 < N 0 = x0 ≤ x1 ≤ N
Summary and conclusions Spec# is a programming system that includes a static program verifier Trend: abstraction refinement on demand Inference of invariants can be done this way, inside theorem prover! Inference can be context sensitive VCs can be generated once Extensions to procedure summaries Watch for preliminary release of Spec# next week http://research.microsoft.com/~leino http://research.microsoft.com/projects/specsharp