300 likes | 419 Views
Automated Software Verification with a Permission-Based Logic. Malte Schwerhoff , ETH Zürich. 20 th June 2014, Zürich. Outline. Motivation Permissions Viper Demo. Automated Software Verification. We have Mutable state (heap locations) Method calls, loops Concurrency We want:
E N D
Automated Software Verification with aPermission-Based Logic Malte Schwerhoff, ETH Zürich 20th June 2014, Zürich
Outline Motivation Permissions Viper Demo
Automated Software Verification • We have • Mutable state (heap locations) • Method calls, loops • Concurrency • We want: • Automated static verification • Modularity
Example class Cell { varv: int methodadd(c: Cell) { v := v + c.v } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assertc1.v == 3 assertc2.v == 2 }
Modularity class Cell { varv: int methodadd(c: Cell) { v := v + c.v } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assert c1.v == 3 assertc2.v == 2 } ?
Specifications class Cell { varv: int methodadd(c: Cell) requires c != nullensures v == old(v) + old(c.v) { v := v + c.v } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assert c1.v == 3 assertc2.v == 2 }
Reasoning with Specifications class Cell { varv: int methodadd(c: Cell) requires c != nullensures v == old(v) + old(c.v) { v := v + c.v } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assert c1.v == 3 assertc2.v == 2 } ?
An Incorrect Implementation class Cell { varv: int methodadd(c: Cell) requires c != nullensures v == old(v) + old(c.v) { v := v + c.v c.v := 0 } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assert c1.v == 3 assertc2.v == 2 }
Strengthening Specifications class Cell { varv: int methodadd(c: Cell) requires c != nullensures v == old(v) + old(c.v) ensures c.v == old(c.v) { v := v + c.v c.v := 0 } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assert c1.v == 3 assertc2.v == 2 }
Strengthening Specifications class Cell { varv: int methodadd(c: Cell) requires c != nullensures v == old(v) + old(c.v) ensures c.v == old(c.v) { v := v + c.v } } methodclient() { varc1 := newCell c1.v := 1 var c2 := newCell c2.v := 2 c1.add(c2) assert c1.v == 3 assertc2.v == 2 } ?
Aliasing class Cell { varv: int methodadd(c: Cell) requires c != nullensures v == old(v) + old(c.v) ensures c.v == old(c.v) { v := v + c.v } } methodclient() { varc1 := newCell c1.v := 1 varc2 := new Cell c2.v := 2 c1.add(c1)// ensures c1.v == 1 + 1 // ensures c1.v == 1 assert c1.v == 3 assertc2.v == 2 }
Challenges Reason about Shared State & Control Aliasing
Permission-Based Verification • Use permissions to control access to shared state • Permissions only exist conceptually, not at run-time • Permissions • Per field x.f • Exclusive write permissions (allows reading as well) • Non-exclusive read permissions
Permission-Based Verification • Permissions to a location x.f can be • Splitinto multiple read permissions • Transferred between methods (or threads) • Recombinedagain
Fractional Permissions client(x) add(x)
Splitting & Transferring Fractional Permissions client(x) add(x) ? ?
Merging Fractional Permissions client(x) add(x) ?
Permission-Based Verification • Assumptions such as x.f == 0can only be made if permissions to x.f are available • If all permissions to x.f are lost, assumptions about x.f must be havoced (forgotten)
Syntax, Separating Conjunction • Permissions can be split: acc(x.f, 1) ⇔ acc(x.f, 1/2) && acc(x.f, 1/2) • Write permissions are exclusive: acc(x.f, 1) && acc(y.f, 1)⇒x ≠ y • Write permissions are “maximal”: acc(x.f, 1) && acc(y.f, 1/100000) ⇒ x ≠ y
Return of the Example method add(c: Cell) requiresacc(v) && acc(c.v, 1/2) ensuresacc(v) && acc(c.v, 1/2) ensures v == old(v) + c.v methodclient() { varc1 := newCell // acc(c1.v) c1.v := 1 //c1.v == 1 var c2 := new Cell // acc(c2.v) c2.v := 2 //c2.v == 2 c1.add(c2) assertc1.v == 3 && c2.v == 2 } ? 20
Return of the Example method add(c: Cell) requiresacc(v) && acc(c.v, 1/2) ensuresacc(v) && acc(c.v, 1/2) ensures v == old(v) + c.v methodclient() { varc1 := newCell // acc(c1.v, 1) c1.v := 1 //c1.v == 1 var c2 := new Cell // acc(c2.v, 1) c2.v := 2 //c2.v == 2 c1.add(c2) assertc1.v == 3 && c2.v == 2 } Reason about call byexhaling precondition followed byinhalingpostcondition 21
Return of the Example method add(c: Cell) requiresacc(v) && acc(c.v, 1/2) ensuresacc(v) && acc(c.v, 1/2) ensures v == old(v) + c.v methodclient() { ... // c1.add(c2) // acc(c1.v, 1) && c1.v == 1 && acc(c2.v, 1) && c2.v == 2 exhale acc(c1.v) && acc(c2.v, 1/2) // && acc(c2.v, 1/2) && c2.v == 2 inhale acc(c1.v) && acc(c.v, 1/2) // acc(c1.v, 1) && acc(c2.v, 1) && c2.v == 2 inhale c1.v == old(c1.v) + c2.v // acc(c1.v, 1) && c1.v == 3 && acc(c2.v, 1) && c2.v == 2 assert c1.v == 3 && c2.v == 2 } 22
Viper Silver verified by Carbon Silicon queries Boogie(Microsoft) Z3(Microsoft) VerificationConditionGeneration Symbolic Execution
Silver • Silver is an intermediate verification language • Encode source languages (with specs) in Silver • Use Silver verifier to verify encoding • Simple: • Objects, fields (heap), methods, loops, if-then-else • Rudimentary type system (primitives + Ref) • Verification features such as specifications • No concurrency primitives • Silver programs can be used for • Verification • Specification inference
Symbolic Execution with Silicon maintains verifies Verifier Program Symbolic State σ queries uses Prover - Symbolically each method (method-modular) - At each statement:- querystate (and prover) to decide if statement is executable - update state by exhaling precondition and inhaling postcondition - Branch over conditionals σ1 σ2 σ3 σ4 σ5
Symbolic Execution with Silicon Symbolic state σ comprises • γ:Store mapping local variables to symbolic values (terms) c1 ↦ tc1, c2 ↦ tc2 • h:Heap recording permissions to and values of fields in the form of heap chunks tc1.v ↦ tv1 # p1, tc2 ↦ tv2 # p2 • π:Path conditions with assumptions about values tc1 ≠ null, tv1 > 0
Slide of the Undead Example methodclient() { ... // γ: c1 ↦ tc1, c2 ↦ tc2 // h: tc1.v ↦ tv1 # 1, tc2.v ↦ tv2 # 1 // π: tv1 == 1, tv2 == 2 exhale acc(c1.v) && acc(c2.v, 1/2) // h: tc2.v ↦ tv2 # 1/2 // π: tv1 == 1, tv2 == 2 inhale acc(c1.v) && acc(c.v, 1/2) // h: tc1.v ↦ tv1 # 1, tc2.v ↦ tv3 # 1 // π: tv1 == 1, tv2 == 2 inhale c1.v == old(c1.v) + c2.v // π: tv1 == 1, tv2 == 2, tv3 == tv1 + tv2 assert c1.v == 3 && c2.v == 2 // π ⊢ tv3 == 3 && tv2 == 2 } 28
Outlook • Information hiding and abstraction • Recursive data structures • Opposite of permissions: obligations • Translation of high-level features • Immutable data (vs. permissions) • Lazy evaluation (vs. permissions) • Closures • Actor-based concurrency • Specification inference