Recalling a Witness

type array (a:Type) (n:N) = mref (repr a n) remains_init where repr a n = s:seq (option a){len s == n} and remains_init #a #n (s0:repr a n) (s1:repr a n) = ∀(i:N{i < n}). Some? s0.(i) =⇒ Some? s1.(i) Notice the interplay between refinements types and monotonic references. The refinement type s:seq (option a){len s == n} passed to mref constrains the stored sequence to be of the appropriate length in every state. Though concise and powerful, refinements of this form can only enforce invariants of each reachable program state taken in isolation. In order to constrain how the array contents can evolve, the preorder remains_init states that the sequence underlying an array can evolve from s0 to s1 only if every initialized index in s0 remains initialized in s1. Given this representation of array, the rest of the code is mostly determined. For instance, its create function takes a length n but no initial value for the array contents. abstract let create (a:Type) (n:N) : ST (array a n) (ensures (λ h0 x h1 → fresh x h0 h1 ∧ modifies { } h0 h1)) = alloc (Seq.create n None) remains_initlet create (a:Type) (n:N) : ST (array a n) (ensures (λ h0 x h1 → fresh x h0 h1 ∧ modifies { } h0 h1)) = alloc (Seq.create n None) remains_init The pure function as_seq h x enables reasoning about an array x in state h as a sequence of optional values. Below we use it to define which parts of an array are initialized, i.e., those indices at which the array contains Some value. let index #a #n (x:array a n) = i:N{i < n} let initialized #a #n (x:array a n) (i:index x) (h:heap) = Some? (as_seq h x).(i) The main use of monotonicity in our library is to observe that initialized is stable with respect to remains_init, the preorder associated with an array. As such, we can define a state-independent proposition (x `init_at` i) using the logical witnessed capabilities. Proceedings of the ACM on Programming Languages, Vol. 2, No. POPL, Article 65. Publication date: January 2018. 65:12 D. Ahman, C. Fournet, C. Hriţcu, K. Maillard, A. Rastogi, and N. Swamy let init_at #a #n (x:array a n) (i:index x) = witnessed (initialized x i) We can now prove that writing to an array at index i ensures that it becomes initialized at i, which is a necessary precondition to read from i. Notice the use of witness when writing, to record the fact that the index is initialized and will remain so; and the use of recall when reading, to recover that the array is initialized at i and so the underlying sequence contains Some v at i. abstract let write (#a:Type) (#n:N) (x:array a n) (i:index x) (v:a) : ST unit (ensures (λ h0 _ h1 →modifies {x} h0 h1 ∧ (as_seq h1 x).(i) == Some v ∧ x `init_at` i)) = x := Seq.upd !x i (Some v); witness (initialized x i)let write (#a:Type) (#n:N) (x:array a n) (i:index x) (v:a) : ST unit (ensures (λ h0 _ h1 →modifies {x} h0 h1 ∧ (as_seq h1 x).(i) == Some v ∧ x `init_at` i)) = x := Seq.upd !x i (Some v); witness (initialized x i) abstract let read (#a:Type) (#n:N) (x:array a n) (i:index x{x `init_at` i}) : ST a (ensures (λ h0 r h1 →modifies { } h0 h1 ∧ Some r == (as_seq h0 x).(i))) = recall (initialized x i); match !r.(i) with Some v→ vlet read (#a:Type) (#n:N) (x:array a n) (i:index x{x `init_at` i}) : ST a (ensures (λ h0 r h1 →modifies { } h0 h1 ∧ Some r == (as_seq h0 x).(i))) = recall (initialized x i); match !r.(i) with Some v→ v It is worth noting that in the absence of monotonicity, the init_at predicate defined above would need to be state-dependent, and thus carried through the subsequent stateful functions as a stateful invariant, causing unnecessary additional proof obligations and bloated specifications.

[1]  Wolfram Schulte,et al.  Local Verification of Global Invariants in Concurrent Programs , 2010, CAV.

[2]  François Pottier,et al.  The essence of monotonic state , 2011, TLDI '11.

[3]  K. Rustan M. Leino,et al.  Using History Invariants to Verify Observers , 2007, ESOP.

[4]  Andrew D. Gordon,et al.  Refinement Types for Secure Implementations , 2008, 2008 21st IEEE Computer Security Foundations Symposium.

[5]  Ioannis T. Kassios Dynamic Frames: Support for Framing, Dependencies and Sharing Without Restrictions , 2006, FM.

[6]  Shin-ya Katsumata,et al.  Parametric effect monads and semantics of effect systems , 2014, POPL.

[7]  Pierre-Yves Strub,et al.  Dependent types and multi-monadic effects in F* , 2016, POPL.

[8]  Viktor Vafeiadis,et al.  Concurrent Abstract Predicates , 2010, ECOOP.

[9]  Lars Birkedal,et al.  Fictional Separation Logic , 2012, ESOP.

[10]  Nikhil Swamy,et al.  Verified low-level programming embedded in F* , 2017, Proc. ACM Program. Lang..

[11]  Robert E. Strom,et al.  Typestate: A programming language concept for enhancing software reliability , 1986, IEEE Transactions on Software Engineering.

[12]  Adam Chlipala,et al.  Certifying a file system using crash hoare logic , 2017, Commun. ACM.

[13]  Nikhil Swamy,et al.  Implementing and Proving the TLS 1.3 Record Layer , 2017, 2017 IEEE Symposium on Security and Privacy (SP).

[14]  Leslie Lamport,et al.  Distributed snapshots: determining global states of distributed systems , 1985, TOCS.

[15]  Juan Chen,et al.  Secure distributed programming with value-dependent types , 2013, J. Funct. Program..

[16]  Nikhil Swamy,et al.  Everest: Towards a Verified, Drop-in Replacement of HTTPS , 2017, SNAPL.

[17]  Arthur Charguéraud,et al.  Characteristic formulae for the verification of imperative programs , 2011, ICFP.

[18]  Ian Stark,et al.  Reducibility and TT-Lifting for Computation Types , 2005, TLCA.

[19]  Andrew D. Gordon,et al.  Modular verification of security protocol code by typing , 2010, POPL '10.

[20]  Michael Barnett,et al.  Friends Need a Bit More: Maintaining Invariants Over Shared State , 2004, MPC.

[21]  Frank Piessens,et al.  Ariadne: A Minimal Approach to State Continuity , 2016, USENIX Security Symposium.