320 likes | 459 Views
This talk focuses on the challenges of building and maintaining correct software systems, emphasizing the importance of specifications in bridging design intent and code. The Spec# programming system, an extension of C#, enhances static and runtime checking, enabling sound modular verification. Key collaborators and contributors, including prominent figures from Microsoft Research, have developed tools and techniques that aid in the verification process, tackling challenges such as specification consistency and type verification. This approach aims to make software the most reliable component of any system.
E N D
Specifying and verifying software K. Rustan M. Leino Microsoft Research, Redmond, WA, USA 15 Nov 2007ChalmersGöteborg, Sweden
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
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 ] ;
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
Galois and wp, wlp, sp • f(x) ≤ y ≡ x ≤ g(y) • sp(S, P) Q ≡ P wlp(S, Q) • ? ≡ P wp(S, Q)
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