300 likes | 413 Views
Verifying object-oriented software: Lessons and challenges. K. Rustan M. Leino Microsoft Research, Redmond. Invited talk, TACAS 2007 ETAPS, Braga, Portugal 28 March 2007. Collaborators. Rosemary Monahan Michaŀ Moskal Peter Müller Madan Musuvathi Dave Naumann Simon Ou
E N D
Verifying object-oriented software:Lessons and challenges K. Rustan M. Leino Microsoft Research, Redmond Invited talk, TACAS 2007ETAPS, Braga, Portugal28 March 2007
Collaborators • Rosemary Monahan • MichaŀMoskal • Peter Müller • MadanMusuvathi • Dave Naumann • Simon Ou • ArndPoetzsch-Heffter • Wolfram Schulte • Herman Venter • Angela Wallenburg • … • Dave Detlefs • Cormac Flanagan • Rajeev Joshi • Gary Leavens • Mark Lillibridge • Todd Millstein • Greg Nelson • Jim Saxe • RaymieStata • Mike Barnett • Nikolaj Bjørner • Evan Chang • Ádám Darvas • Rob DeLine • Leo de Moura • Manuel Fähndrich • Diego Garbervetsky • Bart Jacobs • Francesco Logozzo
Grand Challenge ofVerified Software • Hoare, Joshi, Leavens, Misra, Naumann, Shankar, Woodcock, et al. • “We envision a world in which computer programs are always the most reliable component of any system or device that contains them” [Hoare & Misra]
Spec# programming system • Spec# language • Object-oriented .NET language • More types • Specifications (pre- and postconditions, etc.) • Usage rules (methodology) • Checking: • Static type checking • Run-time checking • Static verification (optional)
Static verification • sound modular verification • focus on automation, not full functional correctness specifications • no termination verification • no verification of temporal properties
Spec# verifier architecture Spec# Spec# compiler MSIL (“bytecode”) translator BoogiePL Inference engine static verifier (Boogie) V.C. generator verification condition SMT solver “correct” or list of errors
Lessons and challenges • Language design • Specifications • VC generation • Inference and decision procedures • Error messages
Language design for verification • non-null types • control flow • if, loops, exceptions, gotos • by default, put base-constructor call last • include pre- and postconditions constructs • à la Gypsy, Eiffel • superset of full language is challenging
Comparing against null publicvoid M( T? x ) { if (x == null) { … } else {int y = ((T!)x).f; … }}
Comparing against null publicvoid M( T? x ) { if (x == null) { … } else {int y = ((!)x).f; … }}
Comparing against null publicvoid M( T? x ) { if (x == null) { … } else {int y = x.f; … }} Spec# performs a data-flow analysis to allow this (similar to definite assignment)
Non-null instance fields No! Is this code type safe? class C : B { T ! x;public C(T ! y) {base();this.x = y;this.P();} publicoverrideint M() { returnx.f; } } abstractclass B {public B() { this.M(); } publicabstractint M(); } null dereference
Non-null instance fields Spec# allows x to beassigned before baseconstructor is called. class C : B { T ! x;public C(T ! y) {this.x = y;base(); this.P();} publicoverrideint M() { returnx.f; } }
Specifications • specifying consistency of data (invariants) • organizing the heap (ownership, …) • abstraction and information hiding (model fields, pure methods, …) • frame conditions (modifies clauses) • support for useful programming idioms • Spec# has a numerous attributes—what is a small, usable set?
x.M(); The heap x exposed mutable consistent committed valid
Frame conditions modifiesp.x; modifies Heap; ensures (forall o: ref, f: field :: • Heap[o,f] == old(Heap)[o,f] • (o == p f == x) • old(Heap)[o,allocated] • old(Heap)[o,committed] • );
VC generation • use an intermediate language • BoogiePL, Why [Filliâtre et al.] • a common verification toolbus • challenge: how to take advantage of special features/theories of the underlying provers? • loop invariants • simple inference helps • applying method frame conditions to loops • functions and quantifiers encode theories • extensible, convenient
BoogiePL Spec# C Java+JML Eiffel … abstract interpreter BoogiePL predicate abstraction termination detector … Simplify Z3 CVC 3 SMT Lib Coq …
Example: source program class C : object{int x; C() { … } virtualint M(int n) { … } staticvoid Main() { C c = new C();c.x = 12;int y = c.M(5); }}
Example: BoogiePL translation (0) • class C • : object { • int x; // class types constuniqueSystem.Object: name; constuniqueC: name; axiom C <: System.Object; functiontypeof(o: ref) returns (t: name); // fields typefield; constuniqueC.x: <int>field; constuniqueallocated: <bool>field; // the heap var Heap: [ref, <α>field]α;
Example: BoogiePL translation (1) C() { … } virtualint M(int n) staticvoid Main() // method declarations procedure C..ctor(this: ref); requires this != null&&typeof(this) <: C; modifies Heap; procedure C.M(this: ref, n: int)returns(result: int); requires this != null&&typeof(this) <: C; modifies Heap; procedureC.Main(); modifies Heap;
Example: BoogiePL translation (2) // method implementations implementationC.Main() { var c: ref, y: int; start: havoc c; assume c != null; assume ! Heap[c, allocated]; assumetypeof(c) == C; Heap[c, allocated] := true; call C..ctor(c); assert c != null; Heap[c, C.x] := 12; call y := C.M(c, 5); return; } c.x = 12; C c = new C(); int y = c.M(5);
Chunker.NextChunk specification publicstringNextChunk() modifiesthis.*; ensuresresult.Length <= ChunkSize;
Chunker.NextChunk translation procedureChunker.NextChunk(this: refwhere $IsNotNull(this, Chunker)) returns ($result: refwhere $IsNotNull($result, System.String)); // in-parameter: target object freerequires $Heap[this, $allocated]; requires ($Heap[this, $ownerFrame] == $PeerGroupPlaceholder || !($Heap[$Heap[this, $ownerRef], $inv] <: $Heap[this, $ownerFrame]) || $Heap[$Heap[this, $ownerRef], $localinv] == $BaseClass($Heap[this, $ownerFrame])) && (forall $pc: ref :: $pc != null && $Heap[$pc, $allocated] && $Heap[$pc, $ownerRef] == $Heap[this, $ownerRef] && $Heap[$pc, $ownerFrame] == $Heap[this, $ownerFrame] ==> $Heap[$pc, $inv] == $typeof($pc) && $Heap[$pc, $localinv] == $typeof($pc)); // out-parameter: return value freeensures $Heap[$result, $allocated]; ensures ($Heap[$result, $ownerFrame] == $PeerGroupPlaceholder || !($Heap[$Heap[$result, $ownerRef], $inv] <: $Heap[$result, $ownerFrame]) || $Heap[$Heap[$result, $ownerRef], $localinv] == $BaseClass($Heap[$result, $ownerFrame])) && (forall $pc: ref :: $pc != null && $Heap[$pc, $allocated] && $Heap[$pc, $ownerRef] == $Heap[$result, $ownerRef] && $Heap[$pc, $ownerFrame] == $Heap[$result, $ownerFrame] ==> $Heap[$pc, $inv] == $typeof($pc) && $Heap[$pc, $localinv] == $typeof($pc)); // user-declared postconditions ensures $StringLength($result) <= $Heap[this, Chunker.ChunkSize]; // frame condition modifies $Heap; freeensures (forall $o: ref, $f: name :: { $Heap[$o, $f] } $f != $inv && $f != $localinv && $f != $FirstConsistentOwner && (!IsStaticField($f) || !IsDirectlyModifiableField($f)) && $o != null && old($Heap)[$o, $allocated] && (old($Heap)[$o, $ownerFrame] == $PeerGroupPlaceholder || !(old($Heap)[old($Heap)[$o, $ownerRef], $inv] <: old($Heap)[$o, $ownerFrame]) || old($Heap)[old($Heap)[$o, $ownerRef], $localinv] == $BaseClass(old($Heap)[$o, $ownerFrame])) && old($o != this || !(Chunker <: DeclType($f)) || !$IncludedInModifiesStar($f)) && old($o != this || $f != $exposeVersion) ==> old($Heap)[$o, $f] == $Heap[$o, $f]); // boilerplate freerequires $BeingConstructed == null; freeensures (forall $o: ref :: { $Heap[$o, $localinv] } { $Heap[$o, $inv] } $o != null && !old($Heap)[$o, $allocated] && $Heap[$o, $allocated] ==> $Heap[$o, $inv] == $typeof($o) && $Heap[$o, $localinv] == $typeof($o)); freeensures (forall $o: ref :: { $Heap[$o, $FirstConsistentOwner] } old($Heap)[old($Heap)[$o, $FirstConsistentOwner], $exposeVersion] == $Heap[old($Heap)[$o, $FirstConsistentOwner], $exposeVersion] ==> old($Heap)[$o, $FirstConsistentOwner] == $Heap[$o, $FirstConsistentOwner]); freeensures (forall $o: ref :: { $Heap[$o, $localinv] } { $Heap[$o, $inv] } old($Heap)[$o, $allocated] ==> old($Heap)[$o, $inv] == $Heap[$o, $inv] && old($Heap)[$o, $localinv] == $Heap[$o, $localinv]); freeensures (forall $o: ref :: { $Heap[$o, $allocated] } old($Heap)[$o, $allocated] ==> $Heap[$o, $allocated]) && (forall $ot: ref :: { $Heap[$ot, $ownerFrame] } { $Heap[$ot, $ownerRef] } old($Heap)[$ot, $allocated] && old($Heap)[$ot, $ownerFrame] != $PeerGroupPlaceholder ==> old($Heap)[$ot, $ownerRef] == $Heap[$ot, $ownerRef] && old($Heap)[$ot, $ownerFrame] == $Heap[$ot, $ownerFrame]) && old($Heap)[$BeingConstructed, $NonNullFieldsAreInitialized] == $Heap[$BeingConstructed, $NonNullFieldsAreInitialized];
Inference, decision procedures • most useful features in theorem prover: • functions and equality, boolean connectives, quantifiers, arithmetic, … • counterexamples • good performance • benchmarks • very different from, say, TTA (time-triggered architecture) protocols • wish: combine inference and theorem proving
Achieving goodSMT-prover performance • non-chronological backtracking • avoid “exponential splitting” • incremental matching -- Leonardo de Moura, on Z3 for Boogie
Combining inference and theorem proving • Abstraction • Computes fix-points • Arithmetic • Unique variables names • Functions, equality • Heap operations Spec# … BoogiePL Inference engine • Precision • Unique variables names • Functions, equality • Arithmetic • Heap operations • Quantifiers V.C. generator verification condition SMT solver
Generating error messages • error location and execution trace to error can easily be reconstructed from simple/limited prover output • better: concrete values, more explanation • e.g., “an object allocated by method M and is later changed by the call to P might violate ownership invariant”
Conclusions • Spec#, C, functional languages, dynamically typed languages, … • allow correctness features to drive language design • use intermediate language • separation of concern • let us share as a community (abstract interpretation, VC generation, multiple theorem provers, predicate abstraction, …) • theorem prover must be good with quantifiers • exciting prospect: combine abstract interpretation and SMT solving • mine counterexamples for more detailed error messages DownloadSpec# from here http://research.microsoft.com/specsharp