1 / 38

Program verification with Hoare Type Theory

Program verification with Hoare Type Theory. Aleks Nanevski Microsoft Research, Cambridge Joint with Greg Morrisett, Lars Birkedal, Amal Ahmed, Rasmus Petersen Dagstuhl, February 2008. How to design a programming language from scratch with verification in mind?.

karly-dale
Download Presentation

Program verification with Hoare Type Theory

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Program verification with Hoare Type Theory Aleks Nanevski Microsoft Research, Cambridge Joint with Greg Morrisett, Lars Birkedal, Amal Ahmed, Rasmus Petersen Dagstuhl, February 2008

  2. How to design a programming language from scratch with verification in mind? • Simply-typed languages have been remarkably successful in preventing a class of programming errors. • But still resist the attempts at specification and verification of deeper semantic properties. • index-out-of-bounds • division-by-zero • invariants on mutable state • or almost anything involving effects. • What are the properties that make types work? • Can we harness these properties to help with specification and verification?

  3. Two foundational approaches to program specification and verification • Hoare Logic • targets imperative, usually untyped, first-order languages • recent extensions to simply-typed functional languages [Honda’05],[Krishnaswami’06],[Birkedal’05] • Dependent type theory • targetspure higher-order lambda calculus • types may capture deep semantic properties of data • integer is even, list has 5 elements, etc. • I want to argue that we essentially want a combination of both.

  4. Starting point: type-and-effect systems • Refine types with effect annotation. • Usually, annotations drawn from some finite set of labels.

  5. But let’s be more semantic… • Draw effect annotations from logic. • y > 0 is a precondition that must be proved before running div x y. • We will also require postconditions, like in Hoare logic • And proofs!

  6. Which logic to use for the annotations? • We want to specify many things: • practical data structures (e.g., hash-tables). • higher-order functions, polymorphism. • dynamically-generated pointers, aliasing, state ownership • recursion, jumps, IO, concurrency. • Thus, the logic better be very expressive. • we won’t exactly be proving Fermat’s Last Theorem, but we will need a lot of math. • Type theory (like Coq) seems perfect. • higher-order logic with proofs. • higher-orderness will come very handy for data abstraction and information hiding. • But need to reconcile Coq with effects.

  7. Hoare Type Theory (HTT) • We introduce into Coq a type corresponding to specs in Hoare Logic. • so that pre/post-conditions can appear in negative positions. • Specifications-as-types principle. • Hoare type stands for • stateful programs (possibly diverging) with • precondition P • postcondition Q • result type A

  8. Hoare Type Theory (cont’d) • Benefits from an interplay of important PL ideas: • verification by wp’s and sp’s • Curry-Howard isomorphism • monads as in Haskell • Separation Logic • Provably modular: • components can be specified and checked in isolation. • Prototype under construction as extension of Coq.

  9. Dependent types and effects

  10. Type theories are unsound if effects are added naively • Propositions like (10 < 0) are types. • Effectful programs can often be given any type: • divergence via infinite recursion • exceptions • mutable state • IO • concurrency • An effectful program can prove that (10 < 0)! • Hence, leading to inconsistency awkward squad from Haskell

  11. A solution: Monads • Like in Haskell, distinguish purity with types • pure fragment – the underlying type theory • e : nat • e is an integer value • e : ST nat • e is delayed effectful computation. • when executed, it may change the state and diverge. • but since it is delayed, it is actually considered pure. • hence, can safely appear in types, predicates, proofs. • e : ST (10 < 0) • a computation which must diverge when executed.

  12. Refine the monad to capture effectful behavior • Hoare type is a “dependent” monad • parametrized by pre- and post-condition • Formation rule • ST{P}x:A{Q} : Type if • P : heap  Prop • A : Type • x:A |- Q : heap  heap  Prop, where heap = loc  option( a:Type. a), and loc = nat. • Note: postcondition is binary relation on heaps. Variant of VDM notation (goes back to I.J. Good).

  13. Example: specify function that increments location contents and returns old value • where is true if x points to v:A in h. • Note: before running inc x, must prove that x stores a nat. • because x may store a value of some other type. • because x may be a dangling pointer.

  14. Implementation of inc in Haskell-style do-notation. • HTT implementation typechecks inc as follows: • Compute P,Q=weakest pre/strongest post for the do-block • Then emit obligation to prove the consequence:

  15. Primitive commands: lookup and update • Memory read • (Strong) Memory update

  16. Primitive commands: allocation and deallocation • Memory allocation • Memory deallocation

  17. Primitive commands: fixpoints • Giving p and q in type of fix corresponds to giving loop invariants in Hoare Logic

  18. Monadic primitives: unit • Roughly, corresponds to Hoare Logic rule for assigning to variable r.

  19. Monadic primitives: bind • Rule of sequential composition (but higher-order) • Note: quantifications over pre/posts and heaps is essential for obtaining tightest specs.

  20. Monadic primitives: Haskell-style do • Rule of consequence • Interesting fact: “do” is not ordinary coercion • it is an introduction form for Hoare type • bind is corresponding elimination • No need for other structural rules of Hoare logic • no rule for auxiliary variables, or conjunction rule

  21. Example: counter • Allocate a private location x • Export function that increments x • Executing fcounter; x0f; x1f; x2f will bind 0,1,2 to x0,x1,x2, respectively. • What is the spec for counter?

  22. A specification with nested Hoare types • Problem: x is out of scope in return type.

  23. Hide private state by existential abstraction • Introduce invariant into code to hide how count is kept. • Another problem: • fst(f) 0 h states (x0) h, but we lost connection with i • We will need Separation Logic to handle this.

  24. Separation logic adds two new things: • Separating conjunction (easily definable in HTT): (P * Q) holds of heap h iff P and Q hold of disjoint parts of h • Frame rule of inference: If then • Can we add Frame rule to HTT? How to prove it is sound?

  25. Employ a type-theoretic idea to expedite… • Impose that well-typed programs must satisfy Frame! • Define new monad STsep, over ST: • Then re-type the stateful commands, using rule of consequence.

  26. Programs remain the same, but specs become much simpler • Example: allocation • empty subheap is consumed and replaced by rv • Hence r must be fresh

  27. STsep monad correctly handles private state • Now (fst f) 0 replaces empty from the precondition. • Meaning: initial heap is extended with x0

  28. Proving program correctness in HTT

  29. Weakest pre and strongest post precisely capture the semantics of a program. • Problem: these may not be easy to read! • Remember the example 3-line program:

  30. Here is the computed tightest spec for inc, in Coq syntax. inc : forall x : loc, ST (fun i : heap => (fun i0 : heap => exists v : nat, ptsto x v i0) i /\ (forall (x0 : nat) (m : heap), (fun (y : nat) (i0 m0 : heap) => m0 = i0 /\ ptsto x y i0) x0 i m -> (fun (xv : nat) (i0 : heap) => (fun i1 : heap => exists B : Type, exists w : B, ptsto x w i1) i0 /\ (forall (x1 : unit) (m0 : heap), (fun (_ : unit) (i1 m1 : heap) => m1 = update x (xv + 1) i1) x1 i0 m0 -> (fun (_ : unit) (_ : heap) => True) x1 m0)) x0 m)) (fun (y : nat) (i m : heap) => exists x0 : nat, exists h : heap, (fun (y0 : nat) (i0 m0 : heap) => m0 = i0 /\ ptsto x y0 i0) x0 i h /\ (fun (xv y0 : nat) (i0 m0 : heap) => exists x1 : unit, exists h0 : heap, (fun (_ : unit) (i1 m1 : heap) => m1 = update x (xv + 1) i1) x1 i0 h0 /\ (fun (_ : unit) (r : nat) (i1 f : heap) => r = xv /\ f = i1) x1 y0 h0 m0) x0 y h m)

  31. Luckily, the spec has a lot of structure! • It literally represents the program as a predicate. • We apply the proving strategy from Hoare Logic: • symbolically evaluate the program, one step at a time. • at each step, discharge the verification condition that enables the next evaluation step. • With a twist: Evaluation/VC-generation can be implemented as a set of lemmas. • proving the lemmas verifies the VC-gen implementation.

  32. Example lemma for symbolic evaluation (in Coq syntax) • If program starts with a read from location x: • first prove that x is initialized (ptsto x v i) • then proceed to prove the spec of the continuation. • Other lemmas similar (evals_bind_write, evals_bind_new…) • Applicable lemma can be determined by a tactic. Lemma evals_bind_read : forall (A B : Type) (x : loc) (v : A) (p2 : A -> heap -> Prop) (q2 : A -> B -> heap -> heap -> Prop)          (i : heap) (q : B -> heap -> Prop),      ptsto x v i ->    (p2 v i /\ forall y m, q2 v y i m -> q y m) ->     (bind_pre (read_pre A x) (read_post A x) p2 i /\        forall y m, (bind_post (read_pre A x) (read_post A x) p2 q2 y i m -> q y m.

  33. Meta-theoretic properties:soundness, compositionality, equations

  34. Verification reduces to typechecking • Theorem: If e:ST{P}r:A{Q}, then e evaluates as expected. • Proved via Preservation and Progress lemmas. • but much more demanding! • Preservation: evaluation preserves types, normal forms, and postconditions. • e.g: if e:ST{T}r:int{r = 55} then e does produce 55. • Progress demands soundness of assertion logic • requires a denotational model for HTT [Petersen,Birkedal’08].

  35. Verification is syntax directed • Program properties independent of context. • No need for whole program reasoning. • Hence, a program is a proof of its spec: • in the pure case, by Curry-Howard. • in the impure case, by weakest pre/strongest post. • Formal statements of compositionality • In the pure case, substitution principles. • In the impure case, Hoare’s rule of composition.

  36. Related work, summary

  37. Summary • HTT types = specifications in the style of Hoare and Separation logic. • generalizes monadic type-and-effect systems, but effect annotations are logical predicates over heaps. • HTT programs = proofs of their own correctness. • leading to compositional verification. • we are currently building libraries for basic data structures. • Verification methodology is itself implemented and verified inside the theory as a set of lemmas.

  38. Related work • Extended static checking: • ESC/Java, JML, Spec#, SPlint, Cyclone, Sage • Hoare-like annotations verified during typechecking. • Restrictive strategies for dealing with undecidability • Dependent types and effects • [Augustson’98],[Mandelbaum’03],[Zhu,Xi’05],[Shao’05], [Sheard’05],[Westbrook’06],[Taha’07],[Condit’07]. • Programs and specs cannot share pure code (phase separation) • Hoare Logics for higher-order functions: • [Schoeder’02],[Honda’05],[Krishnaswami’06],[Birkedal’04] • Simply-typed underlying languages (with effects) • Hoare triples do not integrate into types.

More Related