340 likes | 383 Views
Specifying and verifying software. K. Rustan M. Leino Microsoft Research, Redmond, WA, USA. 8 Nov 2007 Invited talk, ASE 2007 Atlanta, GA. Collaborators most relevant to this talk. Mike Barnett Nikolaj Bjørner Leonardo de Moura Manuel F ä hndrich Bart Jacobs Ronald Middelkoop
E N D
Specifying and verifying software K. Rustan M. Leino Microsoft Research, Redmond, WA, USA 8 Nov 2007Invited talk, ASE 2007Atlanta, GA
Collaborators most relevant to this talk • Mike Barnett • Nikolaj Bjørner • Leonardo de Moura • Manuel Fähndrich • Bart Jacobs • Ronald Middelkoop • Michał Moskal • Peter Müller • Ralf Sasse • Wolfram Schulte • Jan Smans • Herman Venter • Angela Wallenburg
Software engineering problem • Problem • Building and maintaining programs that are correct • Approach • Specifications record design decisions • bridge intent and code • Tools amplify human effort • manage details • find inconsistencies • ensure quality
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 • Superset of C#, adding: • 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
BoogiePL – a verification tool bus Spec# C HAVOC and VerifiedC (vcc) Java bytecode + BML Eiffel …? abstract interpreter predicate abstraction? BoogiePL termination detector? …? Z3 Simplify Zap 2 SMT Lib Fx7 Isabelle/HOL …?
Challenges 0: specifications 1: specifications Spec# C 3: translation 4: translation BoogiePL 2: BoogiePL overview 5:V.C. generation Z3
0: Specifications in Spec# • Multi-variable invariants • Multi-object invariants
Multi-variable invaraints • Demo
Multi-object invariants :Chunker :Chunker :Classroom n: 84 n: 20 invstudentGrades.Count ≤ 20; invdict.Count ≤ n; invdict.Count ≤ n; rep dict: dict: studentGrades: owner :Dictionary Count: 21
1: Specifications in vcc • Based on dynamic frames [YannisKassios, FM 2006] class C {int x; int y; Dictionary d; …frame f { x, y, d, … } d.fr;predicate Valid reads f x ≤ y d.Valid … ;method M()requires Valid;modifies f;ensures Valid;}
2: Overview of BoogiePL • Top-level declarations • Statements
BoogiePL declarations • type T; • const x: T; • function f(A, B) returns (T); • axiom E; • var y: T; • procedure P(a: A, b: B) returns (x: T, y: U); requires pre; modifies w; ensures Q; • implementation P(a: A, b: B) returns (x: T, y: U) { … }
BoogiePL statements • x := E • a[ i ] := E • havoc x • assert E • assume E • ; • call P() • if • while • break • label: • goto A, B
BoogiePL demo Queue frame :Queue head: tail: … :Node :Node :Node :Node next: next: next:
3: Defining OO semantics by translation into BoogiePL 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 source program
// class types constuniqueSystem.Object: name; constuniqueC: name; axiom C <: System.Object; functiontypeof(ref) returns(name); // fields typeField; constuniqueC.x: Field; constuniqueallocated: Field; // the heap var Heap: [ref, Field] int; Example: BoogiePL translation (0) class C : object { int x;
// 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 (1) C() { … } virtualint M(int n) staticvoid Main()
// method implementations implementationC.Main() { var c: ref, y: int; havoc c; assume c != null; assume Heap[c, allocated] == 0; assumetypeof(c) == C; Heap[c, allocated] := 1; call C..ctor(c); assert c != null; Heap[c, C.x] := 12; call y := C.M(c, 5); } Example: BoogiePL translation (2) c.x = 12; C c = new C(); int y = c.M(5);
The type of the heap • type Field;var Heap: [ref, Field] int; • type Field;varHeapInt: [ref, Field] int;varHeapBool: [ref, Field] bool;… • type Field;type Value;var Heap: [ref, Field] Value; and conversion functions between Value and other types • type Field ;var Heap: .[ref, Field ] ;
Translation in more detail: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];
4: Defining C semantics by translation into BoogiePL • A pointer in C can go to any byte location in a segment • A pointer is a (segment, offset) pair • base(ptr) gives the segment • offset(ptr) gives the offset within the segment • Each segment has a fixed length • length(seg)
Modeling memory • Consider the translation of: *p = k; m = *p; • typePtr; type Segment;function base(Ptr) returns (Segment);function offset(Ptr) returns (int); • varMem: [Segment, int] int; • assert 0 ≤ offset(p);assertoffset(p) + 4 ≤ length(base(p));Mem[base(p), offset(p)] := B3(k);Mem[base(p), offset(p)+1] := B2(k);Mem[base(p), offset(p)+2] := B1(k); Mem[base(p), offset(p)+3] := B0(k); • assert 0 ≤ offset(p);assertoffset(p) + 4 ≤ length(base(p));m := Word( Mem[base(p), offset(p)+3],Mem[base(p), offset(p)+2],Mem[base(p), offset(p)+1],Mem[base(p), offset(p)]);
Alternative memory model • Define custom functions to access memory • typeMType; typePtr;typeByteSeq;function rd(MType, Ptr, int) returns (ByteSeq);functionwr(MType, Ptr, int, ByteSeq) returns (Mtype); • axiom ( m: MType, p: Ptr, q: Ptr,plen: int, qlen: int, val: ByteSeq base(p) = base(q) offset(p) = offset(q) 0 < plen plen = qlen rd(wr(m,p,plen,val), q, qlen) = val); • axiom ( m: MType, p: Ptr, q: Ptr,plen: int, qlen: int, val: ByteSeq base(p) base(q) offset(p) + plen ≤ offset(q) offset(q) + qlen ≤ offset(p) rd(wr(m,p,plen,val), q, qlen) = rd(m, q, qlen)); • axiom …
5: Verification-condition generation • Use of quantifiers • Matching triggers, available already in BoogiePL • Reducing redundancy • Paths across conditional statements • But also splitting VCs into smaller ones • wp versus sp
Weakest-precondition versus strongest-postcondition • { P } S { Q } • Started in a state satisfying P, • no execution of S will go wrong, and • every terminating execution of S will end in a state satisfying Q • P wp(S, Q) • or perhaps: sp(P, S) Q ?
wp versus sp • wp(x := E, Q) = Q[E/x] • wp(S;T, Q) =wp(S, wp(T, Q)) • wp(assume E, Q) = E Q • wp(assert E, Q) = E Q • sp(P, x := E) =(y P[y/x] x = E[y/x]) • sp(P, S;T) = sp(sp(P, S), T) • sp(P, assume E) = E P • sp(P, assert E) = E Pbut one would also like to check that E actually holds in P
Further challenges • Prove more programs • Extend structuring methodologies • Improve performance • Raise level of abstraction, e.g.: • Alloy • Meta programming • B method
Summary and conclusions • Verification for Spec#, C, and BoogiePL • To verify, use an intermediate language • Separates concerns • Promotes sharing in verification community • Front ends for multiple languages • Multiple theorem provers • Shared benchmarks • Build your own verifier using Boogie • Hardest part in designing VCs: programming methodology that • Fits common programming idioms and • Can be handled well by automatic prover • Education • Teach using Spec# • Teach verification using BoogiePL • http://research.microsoft.com/specsharp DownloadSpec# and Boogiefrom here