Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PhD Dissertation Defense

PhD Dissertation Defense

Slides from defending my PhD.

Some minor formatting / layout errors were introduced by exporting a Windows-created Powerpoint presentation to PDF using Keynote.

Colin S Gordon

July 02, 2014
Tweet

More Decks by Colin S Gordon

Other Decks in Research

Transcript

  1. Verifying Concurrent Programs by 
 Controlling Alias Interference Colin S.

    Gordon University of Washington [email protected] " Dissertation Defense July 2, 2014
  2. Modular Reasoning for Programs • Decompose program into components •

    Data types + operations • Specifications giving expected input-output behavior • Localize informal reasoning • Devs treat dependencies as black boxes • Localize faults when testing (e.g., unit tests + dependency injection) • Goal Formally reason component X in isolation • ∀ o:operation in X, ensure o’s results meet o’s specification • Assume correctness of X’s dependencies 2
  3. Concurrent Programs *x = 3;! " assert(*x > 0); ...!

    *x = 0;! ... 3 Thread 1 Thread 2
  4. Sequential Programs *x = 3;! f(x) ! ! ! !

    ! !! assert(*x > 0); f(int* y) {! *y = 0;! } 4 Caller Callee
  5. Verification Challenge: Aliasing +Mutation • Aliasing: multiple names for shared

    memory • 2+ incompatible points of view • *x > 0 vs. *x >= 0 • One view may invalidate another’s assumptions • Decades spent on flexible ways to manage this problem • My research: A new approach based on describing interference between aliases • Local, function, and thread interference is via aliases 5
  6. Thesis Statement " " Interference between individual aliases is
 a

    useful and powerful approach to verifying programs, and
 reduces the distance between verifying 
 sequential and concurrent programs. 6
  7. Thesis Statement: Implications Alias interference can be exploited for: •

    Proving data race freedom • Proving (sequential) data structure invariants • Proving concurrent (lock-free) data structure invariants • Proving functional correctness of data structure operations • Proving data structure invariants for actual Haskell programs 7
  8. My Goals
 for Program Verification • Simple conceptual model •

    Only a few component ideas, all familiar • Flexible precision and rigor • Support serial and concurrent programs • From weak (e.g., readonly) to strong (e.g., functional correctness) properties • From informal (whiteboard) to formal (proof) • Handle gradual verification • Verify pieces of a program in context, not all-or- nothing • Support iterative refinement of program properties 8
  9. Talk
 Outline Program Verification Background [Prior work] Reference Immutability Rely-Guarantee

    References Concurrent Rgrefs Serial Concurrent Non-Interference (mutation yes/no) Logical & Temporal Invariants (x > 0, x only increases) Rgref Trace Refinement Future Work 9 Functional Correctness RGHaskell Practical
  10. The Design Space of
 Verification Techniques Verification Style • Global

    Reasoning • Hoare Logic, Owicki-Gries • Isolation • Separation logic, uniqueness • Serialized Sharing • Locks, invariants • Read Sharing • Reference immutability, DPJ • Interference Summaries • Rely-guarantee Limitations • Non-compositional " • Constrained aliasing " • Coarse-grained interaction " • Coarse guarantees " • Requires summaries
  11. • Epitomized by Rely-Guarantee reasoning: " " " • An

    assertion P is stable w.r.t relation R if: – P is preserved by any action in R – σ⊨P ∧ (σ,σ’)∈R ⇒ σ’⊨P – x=0⊨x≥0 ∧(x=0,x=1)∈inc(x) ⇒ x=1⊨x≥0 • Stable assertions will not be invalidated by other threads! Interference Summaries Union
  12. Interference in Sequential Programs • Nearly unexplored for sequential applications!

    • Wickerson et al. verify UNIXv7 malloc (ESOP’10) • This dissertation (portions in PLDI’13) • Recently: Militao et al., Rely-Guarantee Protocols (to appear at ECOOP’14) • Cause: Historical view that sequential and concurrent verification are inherently different
  13. My Approach:
 Reference Interference Summaries • References are capabilities •

    Holding a reference grants certain abilities • Different references: possibly different capabilities! • Successful mental model • Already used; next JS spec adds support (proxies) • Simple cases already work • Reference immutability, ownership types, object capability systems… 13
  14. Talk
 Outline • Background • Reference Immutability for Data Race

    Freedom • Rely-Guarantee References • Lock-free Rely-Guarantee References • Functional Correctness via Rely-Guarantee References • Rely-Guarantee References for Haskell • Future Work / Conclusions 14
  15. Reference Immutability
 for Data Race Freedom • Collaboration with Microsoft

    • My work primarily formalizing and proving • Use Reference Immutability • Per-reference permissions allow or prohibit mutation • Permissions on aliases must be compatible: e.g., no writable alias to immutable data • Exploit mutation and aliasing bounds for race freedom • Results • Data race free language (+ generics, borrowing, & more) • Sequential tool ! concurrency (2 simple type rules) • Concurrent proof structure ! sequential concepts! 15
  16. Talk
 Outline • Background • Reference Immutability for Data Race

    Freedom • Rely-Guarantee References • Lock-free Rely-Guarantee References • Functional Correctness via Rely-Guarantee References • Rely-Guarantee References for Haskell • Future Work / Conclusions 16
  17. Rely-Guarantee References • Permit some mutation, not just all-or none

    • Want to reason about invariants • Use rely-guarantee reasoning for aliases • Results • Knowing interference between aliases is useful • Again: concurrent proof ! sequential programs • Formalized subtle developer reasoning 17
  18. Monotonic Counters:
 A Verification Challenge Problem • Posed by Pilkiewicz

    and Pottier (TLDI’11) • Simple to explain • Represents larger challenges: • Enforce state usage over time • Restrict how state changes, not verify the increment function • Infinite state • Little prior work handles this • Pretty complicated • We can specify and enforce this • Simple specification, no abstraction required 18
  19. Manual Monotonic Counters let x : ref int = alloc

    1 in! // x is an increasing counter > 0! assert (!x > 0);! let x0 = !x in! m(x);! assert (!x >= x0);! assert (!x > 0) 19 m(y : ref int){! y := 0 //bad!! } > 0, ++ ≥ 0, ≥ 0
  20. Rely-Guarantee References • Binary Relations specify pre/post state pairs •

    Rely R: summary of alias behavior • Guarantee G: permitted local behavior • Unary Relations specify invariants • Predicate P: refinement of data • P preserved by (stable w.r.t.) R • Global alias invariant: For any aliases x and y, x.G => y.R and vice versa • Enforced with every new alias creation! • ref T ≡ ref{T|any}{havoc, havoc} • Can encode reference immutability, typestate, related systems 20 ref{τ|P}[R,G] standard reference (≈ T* in C) Rely (e.g. ==) Guarantee (e.g.≤) Predicate (e.g. >0)
  21. RGrefs for Increments let x : ref{int|>0}[≤,≤] = alloc 1

    in //Statically, 1 > 0 ! !x > 0! m(x); //By rely/guarantee: old(!x) ≤ new(!x) //By stable predicate, value at x > 0 x := !x + 1 // Statically: !x ≤ !x + 1 21 m(y : ref{int|>0}[≤,≤]){…}
  22. : ref{ListNode|isSorted} [updateOK, updateOK] Challenge: Nested References Rely, Guarantee, and

    Predicate can talk about heaps… • Nested rely relations? • Interior pointers can’t surprise “root” pointers • Nested guarantees for restricted aliases? • Propagate restrictions at dereference • How much heap can we restrict? • What’s reachable from referent 22 x : ref{ListNode | isSorted } [ updateOK , readonly ] next val … G allows insert … X
  23. Experimenting with RGrefs" • Implemented as a shallow DSL embedded

    into Coq • Verifications: • Monotonic counter • Prepend-only lists • Append-only lists • Reference Immutability as a library • Race-free rely-guarantee references • Function memoization • https://github.com/csgordon/rgref/ 23
  24. Evaluating Verification • Expressiveness • Accepts interesting, useful programs •

    So far: small programs • Later: lock-free data structures, Haskell • Correctness (Soundness) • Accepts only correct programs • Requires a proof: • Always, the refinement on each reference is true in the current heap 24
  25. Lessons from 
 Rely-Guarantee References • Reference-based mutation control is

    useful • Verifies interesting examples • Interference is the right model for aliasing • Applied concurrency verification ideas to aliasing • Current developer reasoning is subtle • RGrefs roughly formalize what devs already think about • Informal reasoning can be made precise • Informal pitfalls highlighted 25
  26. Talk
 Outline • Background • Reference Immutability for Data Race

    Freedom • Rely-Guarantee References • Lock-free Rely-Guarantee References • Functional Correctness via Rely-Guarantee References • Rely-Guarantee References for Haskell • Future Work / Conclusions 26
  27. RGrefs for Concurrency • Rely-Guarantee originally for threads • Hoare

    logic with rely and guarantee for thread actions:
 R,G⊢{P} C {Q} • To make RGrefs useful for concurrency: 1. Fix atomicity assumptions in first revision 2. Add concurrency primitives • Results • Concurrent reasoning is incremental over sequential • RGrefs strong enough for new general results • Proof structure simplified by considering interference 27
  28. Lock-Free Monotonic Counter atomic_inc(c : ref{ int | >0 }[

    ≤ , ≤ ]) {! do {! let x = !c! } while (not CAS(c, x, x + 1))! } Instead of proving: !c ≤ !c + 1 Prove:
 h[c] = x ! h[c] ≤ x + 1 28 bool CAS(r:ref{T|P}[R,G], old:T, new:T) { atomic { if (!r = old) then { [r]:=new; return true } else { return false } } }
  29. Verified Lock Free Data Structures • Treiber Stack • Updates

    push/pop 1 element • Representation is acyclic • Michael-Scott Queue • Simplified for exposition • Enqueue / dequeue 1 element • Representation is acyclic • Lock-free Union Find • Parent chains acyclic, parent rank decreases • All ops are union or path compression • Elements in the same set remain in same set 29
  30. Lessons from
 Concurrent RGrefs • Concurrent reasoning can be an

    incremental refinement of sequential case • RGrefs strong enough for new developments • Verifying lock-free union find invariants • Proof by embedding into Views Framework • As in reference immutability work • Synthesize thread interference from alias interference 30
  31. Talk
 Outline • Background • Reference Immutability for Data Race

    Freedom • Rely-Guarantee References • Lock-free Rely-Guarantee References • Functional Correctness via Rely-Guarantee References • Rely-Guarante References for Haskell • Future Work / Conclusions 31
  32. Functional Correctness via Tracing • Generate abstract trace from typing

    • Interleave local actions and interference • Loop(R!read)!R!increment!R • Simplify abstract trace: R is a rely from a reference! • Loop(R!read)!R!increment!R • Loop(R)!R!increment!R • R!R!increment!R • R!increment!R • This is a spec! • Examples: • Counter • Treiber stack 32
  33. Talk
 Outline • Background • Reference Immutability for Data Race

    Freedom • Rely-Guarantee References • Lock-free Rely-Guarantee References • Functional Correctness via Rely-Guarantee References • Rely-Guarantee References for Haskell • Future Work / Conclusions 33
  34. Rely-Guarantee Haskell • Embed (a variant of) RGRefs into Liquid

    Haskell • Haskell + Refinements + SMT + Rely-Guarantee • Results • RGRef implementation for a real language • Experience with real-world code • Experience with inference • Experience with automation (SMT) • RGRefs nicely complement general refinement types 34
  35. Embedding RGRefs into LH • Reduce higher-order logical statements to

    first-order (stability, reflexivity)
 stable p r = 
 x:a<p> -> y:a<r x> -> {z:a<p> | z = y}! " • No dereference expression (Haskell state is fully monadic) • Haskell code often uses modifyIORef • Observations imply stable refinements • Alternative: Embed Hoare Logic in LH (Hard) 35
  36. Core RGRefs in Liquid Haskell 36 module RG where! {-@

    data RGRef a <p :: a -> Prop, r :: a -> a -> Prop > = Wrap (rgref_ref :: IORef a<p>) @-}! RGRef a = Wrap (IORef a) ! " {-@ newRGRef :: forall <p :: a -> Prop, r :: a -> a -> Prop >.! e:a<p> -> ! e2:a<r e> -> ! f:(x:a<p> -> y:a<r x> -> {v:a<p> | (v = y)}) -> IO (RGRef <p, r> a) @-} ! newRGRef :: a -> a -> (a -> a -> a) -> IO (RGRef a)! newRGRef e e2 stabilityPf = do { r <- newIORef e; return (Wrap r) } ! " {-@ assume readRGRef :: forall <p :: a -> Prop, r :: a -> a -> Prop >. ! x:RGRef<p, r> a -> IO (a<p>) @-} ! readRGRef (Wrap x) = readIORef x ! " {-@ assume writeRGRef :: forall <p :: a -> Prop, r :: a -> a -> Prop>. ! x:(RGRef<p,r> a) -> old:a -> new:a<r old> -> IO () @-}! writeRGRef :: RGRef a -> a -> a -> IO () ! writeRGRef (Wrap x) old new = writeIORef x new! " {-@ modifyRGRef :: forall <p :: a -> Prop, r :: a -> a -> Prop >.! rf:(RGRef<p, r> a) -> ! f:(x:a<p> -> a<r x>) -> ! pf:(x:a<p> -> y:a<r x> -> {v:a<p> | (v = y)}) -> IO () @-}! modifyRGRef :: RGRef a -> (a -> a) -> (a -> a -> a) -> IO () ! modifyRGRef (Wrap x) f pf = modifyIORef x (\ v -> pf v (f v)) !
  37. MonotonicCounter.hs -- Stability Proof: Requires explicit type annotation {-@ stable_monocount

    :: x:{v:Int | v > 0 } -> ! y:{v:Int | x <= v } -> {v:Int | ((v = y) && (v > 0)) } @-} stable_monocount :: Int -> Int -> Int! stable_monocount x y = y -- Monotonically increasing counter! {-@ alloc_counter :: ! () -> IO (RGRef<{\x -> x > 0}, {\x y -> x <= y}> Int) @-} alloc_counter :: () -> IO (RGRef Int) alloc_counter _ = newRGRef 1 3 stable_monocount! {-@ inc_counter :: ! RGRef<{\x -> x > 0}, {\x y -> x <= y}> Int -> IO () @-} inc_counter :: RGRef Int -> IO () inc_counter r = modifyRGRef r (\x -> x + 1) stable_monocount! -- the stability proof is used as a cast! 37
  38. Lock Free Monotonic Counter {-@ atomic_inc :: ! RGRef<{\x ->

    x > 0}, {\x y -> x <= y}> Int -> IO () @-}! atomic_inc :: RGRef Int -> IO ()! atomic_inc r = ! ! atomicModifyRGRef r (\x -> x + 1) stable_monocount 38
  39. Other Liquid Haskell Verification • STM Undo log • Sequential

    fallback for STM Haskell (slightly modified) • Verified undo log is only appended to (on the right end) • Lock-free Linked List • GHC test case • Append, delete, lookup modify correctly • Logical and physical deletion separated, preserve link structure • Observed refinement: A logically deleted node is immutable 39
  40. Lessons from Liquid RGRefs • RGRefs can be simplified while

    retaining utility • First order logic • Only local heap access + exploiting frozen cells • RGRefs are amenable to inference • RGRefs are useful for actual Haskell code 40
  41. Talk
 Outline • Background • Reference Immutability for Data Race

    Freedom • Rely-Guarantee References • Lock-free Rely-Guarantee References • Functional Correctness via Rely-Guarantee References • Rely-Guarantee References for Haskell • Future Work / Conclusions 41
  42. Future Work • RGRefs for Inductive Data Types • Subtlety

    of nested pointers • RGRefs + … • Hoare/Separation Logic • Tried in Liquid Haskell, but the tool isn’t ready • Ghost State • Linear Capabilities • Temporal Logic • Simpler assertions (reference immutability < L < FOL) • Flexible choice of heap fragment size 42
  43. Conclusions • Interference between individual aliases is
 a useful and

    powerful approach to verifying programs; • 4 prototypes, verified sophisticated examples • It reduces the distance between verifying 
 sequential and concurrent programs • Sequential and concurrent versions nearly the same, for metatheory, code, and proofs 43