You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

833 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
(** Symbolic Execution *)
open Fol
[@@@warning "+9"]
module Fresh : sig
(** Monad to manage generation of fresh variables. A value of type ['a t]
is a value of type ['a] which may contain as-yet-unnamed variables. *)
include Monad.S
val gen : wrt:Var.Set.t -> 'a t -> Var.Set.t * 'a
(** [gen ~wrt a] generates names that are fresh with respect to [wrt] for
all unnamed variables in [a], and yields the set of generated
variables together with [a] expressed in terms of those variables. *)
val var : string -> Term.t t
(** Fresh variable whose name is based on the given string. *)
val seg :
?bas:Term.t
-> ?len:Term.t
-> ?siz:Term.t
-> ?cnt:Term.t
-> Term.t
-> Sh.seg t
(** Segment with fresh variables for omitted arguments. *)
val assign : ws:Var.Set.t -> rs:Var.Set.t -> (Var.Subst.t * Var.Set.t) t
(** Renaming of fresh ghosts for overwritten variables, and remaining
modified but not read variables, given written variables [ws] and read
(or appearing in the precondition) variables [rs]. *)
end = struct
type ctx = {wrt: Var.Set.t; xs: Var.Set.t}
include Monad.State (struct
type t = ctx
end)
open Import
let gen ~wrt m =
let a, {xs; wrt= _} = run m {wrt; xs= Var.Set.empty} in
(xs, a)
let var nam {wrt; xs} =
let x, wrt = Var.fresh nam ~wrt in
let xs = Var.Set.add x xs in
return (Term.var x) {wrt; xs}
let seg ?bas ?len ?siz ?cnt loc =
let freshen term nam =
match term with Some term -> return term | None -> var nam
in
let* bas = freshen bas "b" in
let* len = freshen len "m" in
let* siz = freshen siz "n" in
let* cnt = freshen cnt "a" in
return Sh.{loc; bas; len; siz; cnt}
let assign ~ws ~rs {wrt; xs} =
let ovs = Var.Set.inter ws rs in
let Var.Subst.{sub; dom; rng}, wrt = Var.Subst.freshen ovs ~wrt in
let ms = Var.Set.diff ws dom in
let xs = Var.Set.union xs rng in
return (sub, ms) {wrt; xs}
end
(** generic command: [{foot ∧ sub} ms := - {post}] *)
type spec = {foot: Sh.t; sub: Var.Subst.t; ms: Var.Set.t; post: Sh.t}
let gen_spec us specm =
let xs, spec = Fresh.gen ~wrt:us specm in
let us = Var.Set.union xs (Var.Set.union spec.foot.us spec.post.us) in
let foot = Sh.extend_us us spec.foot in
let post = Sh.extend_us us spec.post in
(xs, {spec with foot; post})
(*
* Instruction small axioms
*)
let null_eq ptr = Sh.pure (Formula.eq0 ptr)
let eq_concat (siz, cnt) ms =
Formula.eq
(Term.sized ~siz ~seq:cnt)
(Term.concat
(Array.map ~f:(fun (siz, cnt) -> Term.sized ~siz ~seq:cnt) ms))
open Fresh.Import
(* { emp }
* rs := es
* { *ᵢ rᵢ=eᵢΘ }
*)
let move_spec reg_exps =
let foot = Sh.emp in
let ws, rs =
IArray.fold reg_exps (Var.Set.empty, Var.Set.empty)
~f:(fun (reg, exp) (ws, rs) ->
(Var.Set.add reg ws, Var.Set.union rs (Term.fv exp)) )
in
let+ sub, ms = Fresh.assign ~ws ~rs in
let post =
IArray.fold reg_exps Sh.emp ~f:(fun (reg, exp) post ->
Sh.and_ (Formula.eq (Term.var reg) (Term.rename sub exp)) post )
in
{foot; sub; ms; post}
(* { p-[b;m)->⟨l,α⟩ }
* load l r p
* { r=αΘ * (p-[b;m)->⟨l,α⟩)Θ }
*)
let load_spec reg ptr len =
let* seg = Fresh.seg ptr ~siz:len in
let foot = Sh.seg seg in
let+ sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:foot.us in
let post =
Sh.and_
(Formula.eq (Term.var reg) (Term.rename sub seg.cnt))
(Sh.rename sub foot)
in
{foot; sub; ms; post}
(* { p-[b;m)->⟨l,α⟩ }
* store l p e
* { p-[b;m)->⟨l,e⟩ }
*)
let store_spec ptr exp len =
let+ seg = Fresh.seg ptr ~siz:len in
let foot = Sh.seg seg in
let post = Sh.seg {seg with cnt= exp} in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { d-[b;m)->⟨l,α⟩ }
* memset l d b
* { d-[b;m)->⟨l,b^⟩ }
*)
let memset_spec dst byt len =
let+ seg = Fresh.seg dst ~siz:len in
let foot = Sh.seg seg in
let post = Sh.seg {seg with cnt= Term.splat byt} in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { d=s * l=0 * d-[b;m)->⟨l,α⟩ }
* memcpy l d s
* { d-[b;m)->⟨l,α⟩ }
*)
let memcpy_eq_spec dst src len =
let+ seg = Fresh.seg dst ~len in
let dst_heap = Sh.seg seg in
let foot =
Sh.and_ (Formula.eq dst src) (Sh.and_ (Formula.eq0 len) dst_heap)
in
let post = dst_heap in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { d-[b;m)->⟨l,α⟩ * s-[b';m')->⟨l,α'⟩ }
* memcpy l d s
* { d-[b;m)->⟨l,α'⟩ * s-[b';m')->⟨l,α'⟩ }
*)
let memcpy_dj_spec dst src len =
let* dst_seg = Fresh.seg dst ~siz:len in
let dst_heap = Sh.seg dst_seg in
let+ src_seg = Fresh.seg src ~siz:len in
let src_heap = Sh.seg src_seg in
let dst_seg' = {dst_seg with cnt= src_seg.cnt} in
let dst_heap' = Sh.seg dst_seg' in
let foot = Sh.star dst_heap src_heap in
let post = Sh.star dst_heap' src_heap in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
let memcpy_specs dst src len =
[memcpy_eq_spec dst src len; memcpy_dj_spec dst src len]
(* { d=s * d-[b;m)->⟨l,α⟩ }
* memmov l d s
* { d-[b;m)->⟨l,α⟩ }
*)
let memmov_eq_spec dst src len =
let+ dst_seg = Fresh.seg dst ~len in
let dst_heap = Sh.seg dst_seg in
let foot = Sh.and_ (Formula.eq dst src) dst_heap in
let post = dst_heap in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { d-[b;m)->⟨l,α⟩ * s-[b';m')->⟨l,α'⟩ }
* memmov l d s
* { d-[b;m)->⟨l,α'⟩ * s-[b';m')->⟨l,α'⟩ }
*)
let memmov_dj_spec = memcpy_dj_spec
(* memmov footprint for dst < src case *)
let memmov_foot dst src len =
let* bas = Fresh.var "b" in
let* siz = Fresh.var "m" in
let* cnt_dst = Fresh.var "a" in
let* cnt_mid = Fresh.var "a" in
let* cnt_src = Fresh.var "a" in
let src_dst = Term.sub src dst in
let mem_dst = (src_dst, cnt_dst) in
let siz_mid = Term.sub len src_dst in
let mem_mid = (siz_mid, cnt_mid) in
let mem_src = (src_dst, cnt_src) in
let mem_dst_mid_src = [|mem_dst; mem_mid; mem_src|] in
let* siz_dst_mid_src = Fresh.var "m" in
let+ cnt_dst_mid_src = Fresh.var "a" in
let eq_mem_dst_mid_src =
eq_concat (siz_dst_mid_src, cnt_dst_mid_src) mem_dst_mid_src
in
let seg =
Sh.seg
{loc= dst; bas; len= siz; siz= siz_dst_mid_src; cnt= cnt_dst_mid_src}
in
let foot =
Sh.and_ eq_mem_dst_mid_src
(Sh.and_ (Formula.lt dst src)
(Sh.and_ (Formula.lt src (Term.add dst len)) seg))
in
(bas, siz, mem_dst, mem_mid, mem_src, foot)
(* { d<s * s<d+l * d-[b;m)->⟨s-d,α⟩^⟨l-(s-d),β⟩^⟨s-d,γ⟩ }
* memmov l d s
* { d-[b;m)->⟨l-(s-d),β⟩^⟨s-d,γ⟩^⟨s-d,γ⟩ }
*)
let memmov_dn_spec dst src len =
let* bas, siz, _, mem_mid, mem_src, foot = memmov_foot dst src len in
let mem_mid_src_src = [|mem_mid; mem_src; mem_src|] in
let* siz_mid_src_src = Fresh.var "m" in
let+ cnt_mid_src_src = Fresh.var "a" in
let eq_mem_mid_src_src =
eq_concat (siz_mid_src_src, cnt_mid_src_src) mem_mid_src_src
in
let post =
Sh.and_ eq_mem_mid_src_src
(Sh.seg
{ loc= dst
; bas
; len= siz
; siz= siz_mid_src_src
; cnt= cnt_mid_src_src })
in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { s<d * d<s+l * s-[b;m)->⟨d-s,α⟩^⟨l-(d-s),β⟩^⟨d-s,γ⟩ }
* memmov l d s
* { s-[b;m)->⟨d-s,α⟩^⟨d-s,α⟩^⟨l-(d-s),β⟩ }
*)
let memmov_up_spec dst src len =
let* bas, siz, mem_src, mem_mid, _, foot = memmov_foot src dst len in
let mem_src_src_mid = [|mem_src; mem_src; mem_mid|] in
let* siz_src_src_mid = Fresh.var "m" in
let+ cnt_src_src_mid = Fresh.var "a" in
let eq_mem_src_src_mid =
eq_concat (siz_src_src_mid, cnt_src_src_mid) mem_src_src_mid
in
let post =
Sh.and_ eq_mem_src_src_mid
(Sh.seg
{ loc= src
; bas
; len= siz
; siz= siz_src_src_mid
; cnt= cnt_src_src_mid })
in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
let memmov_specs dst src len =
[ memmov_eq_spec dst src len
; memmov_dj_spec dst src len
; memmov_dn_spec dst src len
; memmov_up_spec dst src len ]
(* { emp }
* alloc r [n × l]
* { ∃α'. r-[r;(n×l)Θ)->⟨(n×l)Θ,α'⟩ }
*)
let alloc_spec reg num len =
let foot = Sh.emp in
let siz = Term.mulq (Q.of_int len) num in
let* sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:(Term.fv siz) in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let+ seg = Fresh.seg loc ~bas:loc ~len:siz ~siz in
let post = Sh.seg seg in
{foot; sub; ms; post}
(*
* Memory management - see e.g. http://jemalloc.net/jemalloc.3.html
*)
(* { p=0 p-[p;m)->⟨m,α⟩ }
* free p
* { emp }
*)
let free_spec ptr =
let* len = Fresh.var "m" in
let+ seg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let foot = Sh.or_ (null_eq ptr) (Sh.seg seg) in
let post = Sh.emp in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { p-[p;m)->⟨m,α⟩ }
* dallocx p
* { emp }
*)
let dallocx_spec ptr =
let* len = Fresh.var "m" in
let+ seg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let foot = Sh.seg seg in
let post = Sh.emp in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { emp }
* malloc r s
* { r=0 ∃α'. r-[r;sΘ)->⟨sΘ,α'⟩ }
*)
let malloc_spec reg siz =
let foot = Sh.emp in
let* sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:(Term.fv siz) in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let+ seg = Fresh.seg loc ~bas:loc ~len:siz ~siz in
let post = Sh.or_ (null_eq (Term.var reg)) (Sh.seg seg) in
{foot; sub; ms; post}
(* { s≠0 }
* mallocx r s
* { r=0 ∃α'. r-[r;sΘ)->⟨sΘ,α'⟩ }
*)
let mallocx_spec reg siz =
let foot = Sh.pure (Formula.dq0 siz) in
let* sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:(Term.fv siz) in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let+ seg = Fresh.seg loc ~bas:loc ~len:siz ~siz in
let post = Sh.or_ (null_eq (Term.var reg)) (Sh.seg seg) in
{foot; sub; ms; post}
(* { emp }
* calloc r [n × l]
* { r=0 r-[r;(n×l)Θ)->⟨(n×l)Θ,0^⟩ }
*)
let calloc_spec reg num len =
let foot = Sh.emp in
let siz = Term.mul num len in
let* sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:(Term.fv siz) in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let cnt = Term.splat Term.zero in
let+ seg = Fresh.seg loc ~bas:loc ~len:siz ~siz ~cnt in
let post = Sh.or_ (null_eq (Term.var reg)) (Sh.seg seg) in
{foot; sub; ms; post}
let size_of_ptr = Term.integer (Z.of_int Llair.Typ.(size_of ptr))
let size_of_siz = Term.integer (Z.of_int Llair.Typ.(size_of siz))
(* { p-[_;_)->⟨W,_⟩ }
* posix_memalign r p s
* { r=ENOMEM * (p-[_;_)->⟨W,_⟩)Θ
* ∃α',q. r=0 * (p-[_;_)->⟨W,q⟩ * q-[q;s)->⟨s,α'⟩)Θ }
* where W = sizeof void*
*)
let posix_memalign_spec reg ptr siz =
let* pseg = Fresh.seg ptr ~siz:size_of_ptr in
let foot = Sh.seg pseg in
let* sub, ms =
Fresh.assign ~ws:(Var.Set.of_ reg)
~rs:(Var.Set.union foot.us (Term.fv siz))
in
let* q = Fresh.var "q" in
let pseg' = {pseg with cnt= q} in
let+ qseg = Fresh.seg q ~bas:q ~len:siz ~siz in
let eok = Term.zero in
let enomem = Term.integer (Z.of_int 12) in
let post =
Sh.or_
(Sh.and_ (Formula.eq (Term.var reg) enomem) (Sh.rename sub foot))
(Sh.and_
(Formula.eq (Term.var reg) eok)
(Sh.rename sub (Sh.star (Sh.seg pseg') (Sh.seg qseg))))
in
{foot; sub; ms; post}
(* { p=0 p-[p;m)->⟨m,α⟩ }
* realloc r p s
* { (r=0 * (pΘ=0 pΘ-[pΘ;m)->⟨m,α⟩))
* ∃α',α'' . r-[r;sΘ)->⟨sΘ,α'⟩
* * (m≤sΘ ? ⟨sΘ,α'⟩=⟨m,α⟩^⟨sΘ-m,α''⟩ : ⟨m,α⟩=⟨sΘ,α'⟩^⟨m-sΘ,α''⟩) }
*)
let realloc_spec reg ptr siz =
let* len = Fresh.var "m" in
let* pseg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let foot = Sh.or_ (null_eq ptr) (Sh.seg pseg) in
let* sub, ms =
Fresh.assign ~ws:(Var.Set.of_ reg)
~rs:(Var.Set.union foot.us (Term.fv siz))
in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let* rseg = Fresh.seg loc ~bas:loc ~len:siz ~siz in
let a0 = pseg.cnt in
let a1 = rseg.cnt in
let+ a2 = Fresh.var "a" in
let post =
Sh.or_
(Sh.and_ (Formula.eq0 loc) (Sh.rename sub foot))
(Sh.and_
(Formula.cond ~cnd:(Formula.le len siz)
~pos:(eq_concat (siz, a1) [|(len, a0); (Term.sub siz len, a2)|])
~neg:(eq_concat (len, a0) [|(siz, a1); (Term.sub len siz, a2)|]))
(Sh.seg rseg))
in
{foot; sub; ms; post}
(* { s≠0 * p-[p;m)->⟨m,α⟩ }
* rallocx r p s
* { (r=0 * pΘ-[pΘ;m)->⟨m,α⟩)
* ∃α',α'' . r-[r;sΘ)->⟨sΘ,α'⟩
* * (m≤sΘ ? ⟨sΘ,α'⟩=⟨m,α⟩^⟨sΘ-m,α''⟩ : ⟨m,α⟩=⟨sΘ,α'⟩^⟨m-sΘ,α''⟩) }
*)
let rallocx_spec reg ptr siz =
let* len = Fresh.var "m" in
let* pseg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let pheap = Sh.seg pseg in
let foot = Sh.and_ (Formula.dq0 siz) pheap in
let* sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:foot.us in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let* rseg = Fresh.seg loc ~bas:loc ~len:siz ~siz in
let a0 = pseg.cnt in
let a1 = rseg.cnt in
let+ a2 = Fresh.var "a" in
let post =
Sh.or_
(Sh.and_ (Formula.eq0 loc) (Sh.rename sub pheap))
(Sh.and_
(Formula.cond ~cnd:(Formula.le len siz)
~pos:(eq_concat (siz, a1) [|(len, a0); (Term.sub siz len, a2)|])
~neg:(eq_concat (len, a0) [|(siz, a1); (Term.sub len siz, a2)|]))
(Sh.seg rseg))
in
{foot; sub; ms; post}
(* { s≠0 * p-[p;m)->⟨m,α⟩ }
* xallocx r p s e
* { ∃α',α'' . sΘ≤r≤(s+e)Θ * pΘ-[pΘ;r)->⟨r,α'⟩
* * (m≤r ? ⟨r,α'⟩=⟨m,α⟩^⟨r-m,α''⟩ : ⟨m,α⟩=⟨r,α'⟩^⟨m-r,α''⟩) }
*)
let xallocx_spec reg ptr siz ext =
let* len = Fresh.var "m" in
let* seg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let foot = Sh.and_ (Formula.dq0 siz) (Sh.seg seg) in
let* sub, ms =
Fresh.assign ~ws:(Var.Set.of_ reg)
~rs:Var.Set.(union foot.us (union (Term.fv siz) (Term.fv ext)))
in
let reg = Term.var reg in
let ptr = Term.rename sub ptr in
let siz = Term.rename sub siz in
let ext = Term.rename sub ext in
let* seg' = Fresh.seg ptr ~bas:ptr ~len:reg ~siz:reg in
let a0 = seg.cnt in
let a1 = seg'.cnt in
let+ a2 = Fresh.var "a" in
let post =
Sh.and_
(Formula.and_
(Formula.cond ~cnd:(Formula.le len siz)
~pos:(eq_concat (siz, a1) [|(len, a0); (Term.sub siz len, a2)|])
~neg:(eq_concat (len, a0) [|(siz, a1); (Term.sub len siz, a2)|]))
(Formula.and_ (Formula.le siz reg)
(Formula.le reg (Term.add siz ext))))
(Sh.seg seg')
in
{foot; sub; ms; post}
(* { p-[p;m)->⟨m,α⟩ }
* sallocx r p
* { r=m * (p-[p;m)->⟨m,α⟩)Θ }
*)
let sallocx_spec reg ptr =
let* len = Fresh.var "m" in
let* seg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let foot = Sh.seg seg in
let+ sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:foot.us in
let post = Sh.and_ (Formula.eq (Term.var reg) len) (Sh.rename sub foot) in
{foot; sub; ms; post}
(* { p-[p;m)->⟨m,α⟩ }
* malloc_usable_size r p
* { m≤r * (p-[p;m)->⟨m,α⟩)Θ }
*)
let malloc_usable_size_spec reg ptr =
let* len = Fresh.var "m" in
let* seg = Fresh.seg ptr ~bas:ptr ~len ~siz:len in
let foot = Sh.seg seg in
let+ sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:foot.us in
let post = Sh.and_ (Formula.le len (Term.var reg)) (Sh.rename sub foot) in
{foot; sub; ms; post}
(* { s≠0 }
* r = nallocx s
* { r=0 r=sΘ }
*)
let nallocx_spec reg siz =
let foot = Sh.pure (Formula.dq0 siz) in
let+ sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:foot.us in
let loc = Term.var reg in
let siz = Term.rename sub siz in
let post = Sh.or_ (null_eq loc) (Sh.pure (Formula.eq loc siz)) in
{foot; sub; ms; post}
let size_of_int_mul = Term.mulq (Q.of_int Llair.Typ.(size_of siz))
(* { r-[_;_)->⟨m,_⟩ * i-[_;_)->⟨W,m⟩ * w=0 * n=0 }
* mallctl (_, r, i, w, n)
* { ∃α'. r-[_;_)->⟨m,α'⟩ * i-[_;_)->⟨W,m⟩ }
* where W = sizeof size_t
*)
let mallctl_read_spec r i w n =
let* iseg = Fresh.seg i ~siz:size_of_siz in
let* rseg = Fresh.seg r ~siz:iseg.cnt in
let+ a = Fresh.var "a" in
let foot =
Sh.and_ (Formula.eq0 w)
(Sh.and_ (Formula.eq0 n) (Sh.star (Sh.seg iseg) (Sh.seg rseg)))
in
let rseg' = {rseg with cnt= a} in
let post = Sh.star (Sh.seg rseg') (Sh.seg iseg) in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { p-[_;_)->⟨W×l,_⟩ * r-[_;_)->⟨m,_⟩ * i-[_;_)->⟨_,m⟩ * w=0 * n=0 }
* mallctlbymib p l r i w n
* { ∃α'. p-[_;_)->⟨W×l,_⟩ * r-[_;_)->⟨m,α'⟩ * i-[_;_)->⟨_,m⟩ }
* where W = sizeof int
*)
let mallctlbymib_read_spec p l r i w n =
let wl = size_of_int_mul l in
let* pseg = Fresh.seg p ~siz:wl in
let* iseg = Fresh.seg i in
let m = iseg.cnt in
let* rseg = Fresh.seg r ~siz:m in
let const = Sh.star (Sh.seg pseg) (Sh.seg iseg) in
let+ a = Fresh.var "a" in
let foot =
Sh.and_ (Formula.eq0 w)
(Sh.and_ (Formula.eq0 n) (Sh.star const (Sh.seg rseg)))
in
let rseg' = {rseg with cnt= a} in
let post = Sh.star (Sh.seg rseg') const in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { r=0 * i=0 * w-[_;_)->⟨n,_⟩ }
* mallctl (_, r, i, w, n)
* { w-[_;_)->⟨n,_⟩ }
*)
let mallctl_write_spec r i w n =
let+ seg = Fresh.seg w ~siz:n in
let post = Sh.seg seg in
let foot = Sh.and_ (Formula.eq0 r) (Sh.and_ (Formula.eq0 i) post) in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(* { p-[_;_)->⟨W×l,_⟩ * r=0 * i=0 * w-[_;_)->⟨n,_⟩ }
* mallctl (_, r, i, w, n)
* { p-[_;_)->⟨W×l,_⟩ * w-[_;_)->⟨n,_⟩ }
* where W = sizeof int
*)
let mallctlbymib_write_spec p l r i w n =
let wl = size_of_int_mul l in
let* pseg = Fresh.seg p ~siz:wl in
let+ wseg = Fresh.seg w ~siz:n in
let post = Sh.star (Sh.seg pseg) (Sh.seg wseg) in
let foot = Sh.and_ (Formula.eq0 r) (Sh.and_ (Formula.eq0 i) post) in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
let mallctl_specs r i w n =
[mallctl_read_spec r i w n; mallctl_write_spec r i w n]
let mallctlbymib_specs p j r i w n =
[mallctlbymib_read_spec p j r i w n; mallctlbymib_write_spec p j r i w n]
(* { p-[_;_)->⟨W×n,α⟩ * o-[_;_)->⟨_,n⟩ }
* mallctlnametomib p o
* { ∃α'.
* p-[_;_)->⟨W×n,α'⟩ * o-[_;_)->⟨_,n⟩ }
* where W = sizeof int
*
* Note: post is too strong, more accurate would be:
* { ∃α',α²,α³,n'. ⟨W×n,α⟩=⟨W×n',α³⟩^⟨W×(n-n'),α²⟩ *
* p-[_;_)->⟨W×n',α'⟩ * p+W×n'-[_;_)->⟨W×(n-n'),α²⟩ * o-[_;_)->⟨_,n'⟩ }
*)
let mallctlnametomib_spec p o =
let* oseg = Fresh.seg o in
let n = oseg.cnt in
let wn = size_of_int_mul n in
let* pseg = Fresh.seg p ~siz:wn in
let+ a = Fresh.var "a" in
let foot = Sh.star (Sh.seg oseg) (Sh.seg pseg) in
let pseg' = {pseg with cnt= a} in
let post = Sh.star (Sh.seg pseg') (Sh.seg oseg) in
{foot; sub= Var.Subst.empty; ms= Var.Set.empty; post}
(*
* cstring - see e.g. http://www.cplusplus.com/reference/cstring/
*)
(* { p-[b;m)->⟨l,α⟩ }
* r = strlen p
* { r=(b+m-p-1)Θ * (p-[b;m)->⟨l,α⟩)Θ }
*)
let strlen_spec reg ptr =
let* seg = Fresh.seg ptr in
let foot = Sh.seg seg in
let+ sub, ms = Fresh.assign ~ws:(Var.Set.of_ reg) ~rs:foot.us in
let {Sh.loc= p; bas= b; len= m; _} = seg in
let ret = Term.sub (Term.sub (Term.add b m) p) Term.one in
let post =
Sh.and_
(Formula.eq (Term.var reg) (Term.rename sub ret))
(Sh.rename sub foot)
in
{foot; sub; ms; post}
(*
* Symbolic Execution
*)
open Option.Import
let check_preserve_us (q0 : Sh.t) (q1 : Sh.t) =
let gain_us = Var.Set.diff q1.us q0.us in
let lose_us = Var.Set.diff q0.us q1.us in
(Var.Set.is_empty gain_us || fail "gain us: %a" Var.Set.pp gain_us ())
&& (Var.Set.is_empty lose_us || fail "lose us: %a" Var.Set.pp lose_us ())
(* execute a command with given explicitly-quantified spec from
explicitly-quantified pre *)
let exec_spec_ (xs, pre) (gs, {foot; sub; ms; post}) =
([%Trace.call fun {pf} ->
pf "@[%a@]@ @[<2>%a@,@[<hv>{%a %a}@;<1 -1>%a--@ {%a }@]@]" Sh.pp pre
(Sh.pp_us ~pre:"@<2>∀ " ())
gs Sh.pp foot
(fun fs sub ->
if not (Var.Subst.is_empty sub) then
Format.fprintf fs "∧ %a" Var.Subst.pp sub )
sub
(fun fs ms ->
if not (Var.Set.is_empty ms) then
Format.fprintf fs "%a := " Var.Set.pp ms )
ms Sh.pp post ;
(* gs contains all vars in spec not in pre.us *)
assert (
let vs = Var.Set.(diff (diff foot.us gs) pre.us) in
Var.Set.is_empty vs || fail "unbound foot: {%a}" Var.Set.pp vs () ) ;
assert (
let vs = Var.Set.(diff (diff ms gs) pre.us) in
Var.Set.is_empty vs || fail "unbound modif: {%a}" Var.Set.pp vs () ) ;
assert (
let vs = Var.Set.(diff (diff (Var.Subst.domain sub) gs) pre.us) in
Var.Set.is_empty vs || fail "unbound write: {%a}" Var.Set.pp vs () ) ;
assert (
let vs = Var.Set.(diff (diff (Var.Subst.range sub) gs) pre.us) in
Var.Set.is_empty vs || fail "unbound ghost: {%a}" Var.Set.pp vs () ) ;
assert (
let vs = Var.Set.(diff (diff post.us gs) pre.us) in
Var.Set.is_empty vs || fail "unbound post: {%a}" Var.Set.pp vs () )]
;
let+ frame = Solver.infer_frame pre gs foot in
Sh.exists (Var.Set.union xs gs)
(Sh.star post (Sh.exists ms (Sh.rename sub frame))))
|>
[%Trace.retn fun {pf} r ->
pf "%a" (Option.pp "%a" Sh.pp) r ;
assert (Option.for_all ~f:(check_preserve_us (Sh.exists xs pre)) r)]
(* execute a command with given spec from pre *)
let exec_spec pre specm =
let xs, pre = Sh.bind_exists pre ~wrt:Var.Set.empty in
exec_spec_ (xs, pre) (gen_spec pre.us specm)
(* execute a multiple-spec command, where the disjunction of the specs
preconditions are known to be tautologous *)
let exec_specs pre =
let xs, pre = Sh.bind_exists pre ~wrt:Var.Set.empty in
let rec exec_specs_ (xs, pre) = function
| specm :: specs ->
let gs, spec = gen_spec pre.Sh.us specm in
let pure = Sh.pure (Sh.pure_approx spec.foot) in
let pre_pure =
Sh.star (Sh.exists (Var.Set.inter gs pure.us) pure) pre
in
let* post = exec_spec_ (xs, pre_pure) (gs, spec) in
let+ posts = exec_specs_ (xs, pre) specs in
Sh.or_ post posts
| [] -> Some (Sh.false_ Var.Set.empty)
in
exec_specs_ (xs, pre)
let exec_specs pre specs =
[%Trace.call fun _ -> ()]
;
exec_specs pre specs
|>
[%Trace.retn fun _ r ->
assert (Option.for_all ~f:(check_preserve_us pre) r)]
(*
* Exposed interface
*)
let assume pre cnd =
let post = Sh.and_ cnd pre in
if Sh.is_false post then None else Some post
let kill pre reg =
let ms = Var.Set.of_ reg in
Sh.extend_us ms (Sh.exists ms pre)
let move pre reg_exps =
exec_spec pre (move_spec reg_exps)
|> function Some post -> post | _ -> fail "Exec.move failed" ()
let load pre ~reg ~ptr ~len = exec_spec pre (load_spec reg ptr len)
let store pre ~ptr ~exp ~len = exec_spec pre (store_spec ptr exp len)
let alloc pre ~reg ~num ~len = exec_spec pre (alloc_spec reg num len)
let free pre ~ptr = exec_spec pre (free_spec ptr)
let nondet pre = function Some reg -> kill pre reg | None -> pre
let abort _ = None
let intrinsic :
Sh.t
-> Var.t option
-> Llair.Intrinsic.t
-> Term.t iarray
-> Sh.t option =
fun pre areturn intrinsic actuals ->
match (areturn, intrinsic, IArray.to_array actuals) with
(*
* llvm intrinsics
*)
| None, `memset, [|dst; byt; len; _isvolatile|] ->
exec_spec pre (memset_spec dst byt len)
| None, `memcpy, [|dst; src; len; _isvolatile|] ->
exec_specs pre (memcpy_specs dst src len)
| None, `memmove, [|dst; src; len; _isvolatile|] ->
exec_specs pre (memmov_specs dst src len)
(*
* cstdlib - memory management
*)
(* void* malloc(size_t size) *)
| Some reg, `malloc, [|size|]
(* void* aligned_alloc(size_t alignment, size_t size) *)
|Some reg, `aligned_alloc, [|_; size|] ->
exec_spec pre (malloc_spec reg size)
(* void* calloc(size_t number, size_t size) *)
| Some reg, `calloc, [|number; size|] ->
exec_spec pre (calloc_spec reg number size)
(* int posix_memalign(void** ptr, size_t alignment, size_t size) *)
| Some reg, `posix_memalign, [|ptr; _; size|] ->
exec_spec pre (posix_memalign_spec reg ptr size)
(* void* realloc(void* ptr, size_t size) *)
| Some reg, `realloc, [|ptr; size|] ->
exec_spec pre (realloc_spec reg ptr size)
(*
* jemalloc - non-standard API
*)
(* void* mallocx(size_t size, int flags) *)
| Some reg, `mallocx, [|size; _|] -> exec_spec pre (mallocx_spec reg size)
(* void* rallocx(void* ptr, size_t size, int flags) *)
| Some reg, `rallocx, [|ptr; size; _|] ->
exec_spec pre (rallocx_spec reg ptr size)
(* size_t xallocx(void* ptr, size_t size, size_t extra, int flags) *)
| Some reg, `xallocx, [|ptr; size; extra; _|] ->
exec_spec pre (xallocx_spec reg ptr size extra)
(* size_t sallocx(void* ptr, int flags) *)
| Some reg, `sallocx, [|ptr; _|] -> exec_spec pre (sallocx_spec reg ptr)
(* void dallocx(void* ptr, int flags) *)
| None, `dallocx, [|ptr; _|]
(* void sdallocx(void* ptr, size_t size, int flags) *)
|None, `sdallocx, [|ptr; _; _|] ->
exec_spec pre (dallocx_spec ptr)
(* size_t nallocx(size_t size, int flags) *)
| Some reg, `nallocx, [|size; _|] -> exec_spec pre (nallocx_spec reg size)
(* size_t malloc_usable_size(void* ptr) *)
| Some reg, `malloc_usable_size, [|ptr|] ->
exec_spec pre (malloc_usable_size_spec reg ptr)
(* int mallctl(const char* name, void* oldp, size_t* oldlenp, void* newp,
size_t newlen) *)
| Some _, `mallctl, [|_; oldp; oldlenp; newp; newlen|] ->
exec_specs pre (mallctl_specs oldp oldlenp newp newlen)
(* int mallctlnametomib(const char* name, size_t* mibp, size_t* miblenp) *)
| Some _, `mallctlnametomib, [|_; mibp; miblenp|] ->
exec_spec pre (mallctlnametomib_spec mibp miblenp)
(* int mallctlbymib(const size_t* mib, size_t miblen, void* oldp, size_t*
oldlenp, void* newp, size_t newlen); *)
| Some _, `mallctlbymib, [|mib; miblen; oldp; oldlenp; newp; newlen|] ->
exec_specs pre
(mallctlbymib_specs mib miblen oldp oldlenp newp newlen)
| _, `malloc_stats_print, _ -> Some pre
(*
* cstring
*)
(* size_t strlen (const char* ptr) *)
| Some reg, `strlen, [|ptr|] -> exec_spec pre (strlen_spec reg ptr)
(*
* folly
*)
(* bool folly::usingJEMalloc() *)
| Some _, `_ZN5folly13usingJEMallocEv, [||] -> Some pre
(*
* signature mismatch
*)
| ( _
, ( `memset | `memcpy | `memmove | `malloc | `aligned_alloc | `calloc
| `posix_memalign | `realloc | `mallocx | `rallocx | `xallocx
| `sallocx | `dallocx | `sdallocx | `nallocx | `malloc_usable_size
| `mallctl | `mallctlnametomib | `mallctlbymib | `strlen
| `_ZN5folly13usingJEMallocEv )
, _ ) ->
fail "%aintrinsic %a%a;"
(Option.pp "%a := " Var.pp)
areturn Llair.Intrinsic.pp intrinsic (IArray.pp "@ " Term.pp)
actuals ()