Program Verification with F*

fstar-logo

Cătălin Hriţcu, Inria Paris

MPRI 2-30, Paris, Friday, 13 January 2017

Today: Program Verification with F*

  • Verifying purely functional programs

    • intrinsic proof style (introduced last week)
    • extrinsic proof style
  • More interactive than last time!

Verifying purely functional programs

Verifying list-manipulating programs

type list (a:Type) =
  | Nil  : list a
  | Cons : hd:a -> tl:list a -> list a

Using refinement types for stacks

module RefinedStack

  abstract type stack = list int
  
  abstract val is_empty : stack -> Tot bool
  let is_empty = Nil?

  abstract val empty : s:stack{is_empty s}
  let empty = []
  
  abstract val push : int -> stack -> Tot (s:stack{~(is_empty s)})
  let push x xs = Cons x xs

  abstract val pop : s:stack{~(is_empty s)} -> Tot stack
  let pop = Cons?.tl
  
  abstract val top : s:stack{~(is_empty s)} -> Tot int
  let top = Cons?.hd
 

Client for RefinedStack

module RefinedStackClient

open RefinedStack

let main() : Tot stack =
  let s = push 1 (push 2 (push 3 empty)) in
  let t = top s in
  let s' = pop s in s'
  (* pop s' -- Subtyping check failed;
       expected type (s:stack{~(is_empty s)}); got type stack *)
  • Exercise: redesign RefinedStack interface so that this code works

Intrinsic proofs (Hoare-style specs)

module AppIntrinsic

open FStar.List.Tot

let rec append (#a:Type) (xs : list a) (ys : list a) : Tot (list a) =
  match xs with
  | [] -> ys
  | x :: xs' -> x :: append xs' ys

let rec append' (#a:Type) (xs : list a) (ys : list a) : Pure (list a)
    (requires True) (ensures (fun zs -> length zs = length xs + length ys)) =
  match xs with
  | [] -> ys
  | x :: xs' -> x :: append' xs' ys

Extrinsic proofs (Lemmas)

module AppExtrinsic

open FStar.List.Tot

let rec append (#a:Type) (xs : list a) (ys : list a) : Tot (list a) =
  match xs with
  | [] -> ys
  | x :: xs' -> x :: append xs' ys

let rec append_length (#a:Type) (xs : list a) (ys : list a) : Pure unit
    (requires True)
    (ensures (fun _ -> length (append xs ys) = length xs + length ys)) =
  match xs with
  | [] -> ()
  | x :: xs' -> append_length xs' ys

let rec append_length_lemma (#a:Type) (xs : list a) (ys : list a) : Lemma
    (ensures (length (append xs ys) = length xs + length ys)) =
  match xs with
  | [] -> ()
  | x :: xs' -> append_length xs' ys

Often one really needs lemmas

module Rev

open AppExtrinsic

let snoc l h = append l [h]

val reverse: #a:Type -> list a -> Tot (list a)
let rec reverse (#a:Type) l =
  match l with
  | [] -> []
  | hd::tl -> snoc (reverse tl) hd

val snoc_cons: #a:Type -> l:list a -> h:a ->
  Lemma (reverse (snoc l h) == h::reverse l)
let rec snoc_cons (#a:Type) l h =
  match l with
  | [] -> ()
  | hd::tl -> snoc_cons tl h

val rev_involutive: #a:Type -> l:list a -> Lemma (reverse (reverse l) == l)
let rec rev_involutive (#a:Type) l =
  match l with
  | [] -> ()
  | hd::tl -> rev_involutive tl; snoc_cons (reverse tl) hd

Insertion Sort

Next steps

  • in 1 week: Verified Low-Level Programming Embedded in F* (Jonathan Protzenko)

  • in 2 weeks: From “Hoare Logic” to “Dijkstra Monads for Free”

  • in 3-4 weeks: Verifying Crypto Implementations in F* (Karthik)