Proving a Concurrent Program Correct by Demonstrating It Does Nothing Bernhard Kragl IST Austria doi: 10.1007/978-3-319-96145-3_5 https://github.com/boogie-org/boogie Shaz Qadeer Microsoft https://www.rise4fun.com/civl
Credits Cormac Flanagan Stephen N. Freund Serdar Tasiran Tayfun Elmas Chris Hawblitzel
Program verification Program π¬ Verifier Is π¬ safe? Error report Proof
Program verification Invariants Program Templates π¬ Proof structure β¦ Verifier Is π¬ safe? Error report Proof
Reasoning about transition systems β’ Transition system πππ , π½πππ’, πππ¦π’, ππππ πππ (Variables) π½πππ’ (Initial state predicate over πππ ) πππ¦π’ (Transition predicate over πππ βͺ πππ β² ) ππππ (Safety predicate over πππ ) β’ Inductive invariant π½ππ€ π½πππ’ β π½ππ€ (Initialization) π½ππ€ β§ πππ¦π’ β π½ππ€ β² (Preservation) π½ππ€ β ππππ (Safety)
Structured program vs. Transition relation Init: ππ = ππ 1 = ππ 2 = π Next: ππ = π β§ ππ β² = ππ 1 β² = ππ 2 β² = π β§ π¦ β² = 0 β§ ππ π, π’ 1 , π’ 2 a: x := 0 β² = π β§ Β¬π β§ π β² β§ ππ ππ, ππ 2 , π¦, π’ 1 , π’ 2 ππ 1 = π β§ ππ 1 b: acquire(l) acquire(l) β² = π β§ π’ 1 β² = π¦ β§ ππ ππ, ππ 2 , π, π¦, π’ 2 ππ 1 = π β§ ππ 1 c: t1 := x t2 := x β² = π β§ π’ 1 β² = π’ 1 + 1 β§ ππ ππ, ππ 2 , π, π¦, π’ 2 ππ 1 = π β§ ππ 1 d: t1 := t1+1 t2 := t2+1 β² = π β§ π¦ β² = π’ 1 β§ ππ ππ, ππ 2 , π, π’ 1 , π’ 2 ππ 1 = π β§ ππ 1 e: x := t1 x := t2 β² = π β§ Β¬π β² β§ ππ(ππ, ππ 2 , π¦, π’ 1 , π’ 2 ) ππ 1 = π β§ ππ 1 f: release(l) release(l) β² = π β§ Β¬π β§ π β² β§ ππ ππ, ππ 1 , π¦, π’ 1 , π’ 2 ππ 2 = π β§ ππ 2 β² = π β§ π’ 2 β² = π¦ β§ ππ ππ, ππ 1 , π, π¦, π’ 1 g: assert x = 2 ππ 2 = π β§ ππ 2 β² = π β§ π’ 2 β² = π’ 2 + 1 β§ ππ ππ, ππ 1 , π, π¦, π’ 1 ππ 2 = π β§ ππ 2 β² = π β§ π¦ β² = π’ 2 β§ ππ ππ, ππ 1 , π, π’ 1 , π’ 2 ππ 2 = π β§ ππ 2 β² = π β§ Β¬π β² β§ ππ(ππ, ππ 1 , π¦, π’ 1 , π’ 2 ) ππ 2 = π β§ ππ 2 Procedures and dynamic thread creation ππ 1 = ππ 2 = π β§ ππβ² = π β§ ππ(ππ 1 , ππ 2 , π, π¦, π’ 1 , π’ 2 ) complicate transition relation further! Safe: ππ = π β π¦ = 2
Interference freedom and Owicki-Gries Ξ¨ 1 : π 1 π· 1 {π 1 } Ξ¨ 2 : π 2 π· 2 π 2 Ξ¨ 1 , Ξ¨ 2 interference free π 1 β§ π 2 π· 1 β₯ π· 2 π 1 β§ π 2 β’ Example: π¦ = 0 π¦ β π¦ + 1 β₯ π¦ β π¦ + 2 π¦ = 3 π¦ = 0 π¦ = 0 π¦ = 0 Interference freedom: π 1 : {π¦ = 0 β¨ π¦ = 2} π 2 : {π¦ = 0 β¨ π¦ = 1} π 1 β§ π 2 π¦ β π¦ + 2 π π 2 β§ π 1 π¦ β π¦ + 1 π 2 1 π¦ β π¦ + 1 π¦ β π¦ + 2 π 1 β§ π 2 π¦ β π¦ + 2 π 1 π 2 β§ π 1 π¦ β π¦ + 1 π 2 π 1 : π¦ = 1 β¨ π¦ = 3 π 2 : π¦ = 2 β¨ π¦ = 3 π 1 β§ π 2 π¦ = 3
Ghost variables Need to refer to other threadβs state β’ local variables β’ program counter β’ Example: π¦ = 0 π¦ β π¦ + 1 β₯ π¦ β π¦ + 1 π¦ = 2 π¦ = 0 ππππ 1 β ππππ‘π; ππππ 2 β ππππ‘π π 1 : Β¬ππππ 1 β§ Β¬ππππ 2 β π¦ = 0 β§ ππππ 2 β π¦ = 1 π 2 : Β¬ππππ 2 β§ Β¬ππππ 1 β π¦ = 0 β§ ππππ 1 β π¦ = 1 π¦ β π¦ + 1; ππππ 1 β π’π π£π π¦ β π¦ + 1; ππππ 2 β π’π π£π π 1 : ππππ 1 β§ Β¬ππππ 2 β π¦ = 1 β§ ππππ 2 β π¦ = 2 π 2 : ππππ 2 β§ Β¬ππππ 1 β π¦ = 1 β§ ππππ 1 β π¦ = 2 π¦ = 2
Rely/Guarantee Rely/Guarantee specifications π· β¨ (π, π, π», π ) for individual threads and composition rule allow for modular proofs of loosely-coupled systems. π¦ β₯ 0 π = π» = π¦ β² β₯ π¦ π¦ β π¦ + 1 β₯ π¦ β π¦ + 1 π = π = π¦ β₯ 0 π¦ β₯ 0
Multi-layered refinement proofs [skip] || π 1 βΌ π 2 βΌ β― βΌ π πβ1 βΌ π π π is safe π π 1 is safe Advantages of structured proofs: Better for humans: easier to construct and maintain Better for computers: localized/small checks ο easier to automate Programs that do nothing cannot go wrong
Refinement is well-studied β’ Logic β’ π π¦, π¦ β² β π (π¦, π¦ β² ) β’ Labeled transition systems β’ Language containment β’ Simulation (forward, backward, upward, downward, diagonal, sideways, β¦) β’ Bisimulation (vanilla, mint, lavender, barbed, triangulated, complicated, β¦) β’ β¦
Refinement is difficult for programs β’ Programs are complicated β’ Complex control and data β’ Gap between program syntax and abstractions β’ β¦ especially for concurrent programs β’ β¦ especially for interactive proof construction
CIVL: Construct correct concurrent programs layer by layer procedure P(β¦) { S } β’ Operates on program syntax S1; S2 β’ Organizes proof as a sequence of program if (e) S1 else S2 layers with increasingly coarse-grained while (e) S atomic actions call P β’ All layers and supporting invariants async call P expressed together in one textual unit call P1 || P2 β’ Automatically-generated verification call A conditions
Gated atomic actions [Elmas, Q, Tasiran 2009] (Gate, Transition) single-state predicate two-state predicate Command Gate Transition Lock specification π¦ β² = π¦ + π§ β§ π§ β² = π§ π’π π£π x := x+y var lock : ThreadID βͺ {nil} π§ β² = π§ π’π π£π havoc x Acquire(): [assume lock == nil; lock := tid] π¦ β² = π¦ β§ π§ β² = π§ π¦ < π§ assert x<y Release(): [assert lock == tid; lock := nil] π¦ < π§ β§ π¦ β² = π¦ β§ π§ β² = π§ π’π π£π assume x<y β’ Unifies precondition and postcondition β’ Primitive for modeling a (concrete or abstract) concurrent program
Operational semantics β, π‘ β β’ Program configuration π, π β π° β’ Transition relation β between configurations (and failure configuration β₯ ) β β β₯ β’ Safety: Β¬βπβ: π, β, ππππ β β β₯ β’ π»πππ π = π Β¬ββ: π, β, ππππ β β π β² , β π, π β² β’ ππ πππ‘ π = ββ: π, β, ππππ β’ π 1 βΌ π 2 : (1) π»πππ π 2 β π»πππ π (2) π»πππ(π 2 ) β ππ πππ‘ π 1 β ππ πππ‘(π 2 ) 1 π 2 preserves failures π 2 preserves final states
var x; IncrBy2() = call IncrBy2() [ x := x + 2 ] var x; Incr() = Op() { call Op() || Op() [ x := x + 1 ] call Incr() } var lock; Acquire() = Release() = Op() { call Op() || Op() var x; [ assume lock == nil; [ assert lock == tid; var t; lock := tid; ] lock := nil; ] call Acquire(); t := x; x := t + 1; call Release(); } var b; Acquire() { Release() { Op() { call Op() || Op() var x; while (true) b := 0; var t; if (CAS(b, 0, 1)) break; } call Acquire(); } t := x; x := t + 1; call Release(); }
const c >= 0; var x; call Main(); Main() { // Create c threads // each executing Incr ο¨ [assert x β₯ 0] } Incr() { acquire(); assert x β₯ 0; x := x + 1; release(); }
const c >= 0; var x; call Main(); Main() { x := 0; // Create c threads ο¨ [ ] // each executing Incr } Incr() { acquire(); assert x β₯ 0; x := x + 1; release(); }
Programs constructed with CIVL β’ Concurrent garbage collector [Hawblitzel, Petrank, Q, Tasiran 2015] β’ FastTrack2 race-detection algorithm [Flanagan, Freund, Wilcox 2018] β’ Lock-protected memory atop TSO [Hawblitzel] β’ Thread-local heap atop shared heap [Hawblitzel, Q] β’ Two-phase commit [K, Q, Henzinger 2018] β’ Work-stealing queue, Treiber stack, Ticket, β¦
Program layers in CIVL β’ A CIVL program denotes a sequence of concurrent programs (layers) β’ chained together by a refinement-preserving transformation β’ Transformation between program layers combines β’ Atomization: Transform statement S into atomic block [S] β’ Summarization: Transform atomic block [S] into atomic action A β’ Abstraction: Replace atomic action A with atomic action B
Right and left movers [Lipton 1975] Integer a βSemaphoreβ βwaitβ βsignalβ P(a) = [assume a > 0; a := a - 1] V(a) = [a := a + 1] right mover (R) left mover (L) Y V(a) P(a) X S 1 S 2 S 3 S 1 S 2 S 3 V(a) Y X P(a) S 1 T 2 S 3 S 1 T 2 S 3 Sequence R*;(N+ ο₯ ); L* is atomic . . . . R* X N Y L* S 0 S 5 . . . . R* X N L* Y S 0 S 5
Recommend
More recommend