|
|
|
@ -0,0 +1,290 @@
|
|
|
|
|
(*
|
|
|
|
|
* Copyright (c) 2018-present, Facebook, Inc.
|
|
|
|
|
*
|
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
|
*)
|
|
|
|
|
|
|
|
|
|
(** Symbolic Execution *)
|
|
|
|
|
|
|
|
|
|
(** generic command: ∀xs.{foot}-{post} *)
|
|
|
|
|
type spec = {xs: Var.Set.t; foot: Sh.t; post: Sh.t}
|
|
|
|
|
|
|
|
|
|
type xseg = {us: Var.Set.t; xs: Var.Set.t; seg: Sh.seg}
|
|
|
|
|
|
|
|
|
|
let fresh_var nam us xs =
|
|
|
|
|
let var, us = Var.fresh nam ~wrt:us in
|
|
|
|
|
(Exp.var var, us, Set.add xs var)
|
|
|
|
|
|
|
|
|
|
let fresh_seg ~loc ?bas ?len ?siz ?arr us =
|
|
|
|
|
let freshen exp nam us xs =
|
|
|
|
|
match exp with Some exp -> (exp, us, xs) | None -> fresh_var nam us xs
|
|
|
|
|
in
|
|
|
|
|
let xs = Var.Set.empty in
|
|
|
|
|
let bas, us, xs = freshen bas "b" us xs in
|
|
|
|
|
let len, us, xs = freshen len "m" us xs in
|
|
|
|
|
let siz, us, xs = freshen siz "n" us xs in
|
|
|
|
|
let arr, us, xs = freshen arr "a" us xs in
|
|
|
|
|
{us; xs; seg= {loc; bas; len; siz; arr}}
|
|
|
|
|
|
|
|
|
|
let null_eq ptr = Sh.pure (Exp.eq Exp.null ptr)
|
|
|
|
|
|
|
|
|
|
let assume cnd pre =
|
|
|
|
|
let post = Sh.and_ cnd pre in
|
|
|
|
|
if Sh.is_false post then None else Some post
|
|
|
|
|
|
|
|
|
|
(* { emp }
|
|
|
|
|
* alloc r [n × l]
|
|
|
|
|
* { ∃α'. r-[r;n×l)->⟨n×l,α'⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let alloc_spec us reg num len =
|
|
|
|
|
let loc = Exp.var reg in
|
|
|
|
|
let siz = Exp.mul num len in
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc ~bas:loc ~len:siz ~siz us in
|
|
|
|
|
let post = Sh.seg seg in
|
|
|
|
|
let foot = Sh.extend_us xs Sh.emp in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
(* { p=0 ∨ p-[p;m)->⟨m,α⟩ }
|
|
|
|
|
* free p
|
|
|
|
|
* { emp }
|
|
|
|
|
*)
|
|
|
|
|
let free_spec us ptr =
|
|
|
|
|
let len, us = Var.fresh "m" ~wrt:us in
|
|
|
|
|
let siz = Exp.var len in
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc:ptr ~bas:ptr ~len:siz ~siz us in
|
|
|
|
|
let xs = Set.add xs len in
|
|
|
|
|
let foot = Sh.or_ (null_eq ptr) (Sh.seg seg) in
|
|
|
|
|
let post = Sh.emp in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
(* { p-[b;m)->⟨l,α⟩ }
|
|
|
|
|
* load l r p
|
|
|
|
|
* { r=α * p-[b;m)->⟨l,α⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let load_spec us reg ptr len =
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc:ptr ~siz:len us in
|
|
|
|
|
let foot = Sh.seg seg in
|
|
|
|
|
let post = Sh.and_ (Exp.eq (Exp.var reg) seg.arr) foot in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
(* { p-[b;m)->⟨l,α⟩ }
|
|
|
|
|
* store l p e
|
|
|
|
|
* { p-[b;m)->⟨l,e⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let store_spec us ptr exp len =
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc:ptr ~siz:len us in
|
|
|
|
|
let foot = Sh.seg seg in
|
|
|
|
|
let post = Sh.seg {seg with arr= exp} in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
(* { d-[b;m)->⟨l,α⟩ }
|
|
|
|
|
* memset l d b
|
|
|
|
|
* { d-[b;m)->⟨l,b^l⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let memset_spec us dst byt len =
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc:dst ~siz:len us in
|
|
|
|
|
let foot = Sh.seg seg in
|
|
|
|
|
let post = Sh.seg {seg with arr= Exp.splat ~byt ~siz:len} in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
(* { d=s * l=0 * d-[b;m)->⟨l,α⟩ }
|
|
|
|
|
* memcpy l d s
|
|
|
|
|
* { d-[b;m)->⟨l,α⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let memcpy_eq_spec us dst src len =
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc:dst ~len us in
|
|
|
|
|
let dst_heap = Sh.seg seg in
|
|
|
|
|
let foot =
|
|
|
|
|
Sh.and_ (Exp.eq dst src)
|
|
|
|
|
(Sh.and_ (Exp.eq len (Exp.integer Z.zero)) dst_heap)
|
|
|
|
|
in
|
|
|
|
|
let post = dst_heap in
|
|
|
|
|
{xs; foot; 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 us dst src len =
|
|
|
|
|
let {us; xs= dst_xs; seg= dst_seg} = fresh_seg ~loc:dst ~siz:len us in
|
|
|
|
|
let dst_heap = Sh.seg dst_seg in
|
|
|
|
|
let {us; xs= src_xs; seg= src_seg} = fresh_seg ~loc:src ~siz:len us in
|
|
|
|
|
let src_heap = Sh.seg src_seg in
|
|
|
|
|
let {seg= dst_seg'} =
|
|
|
|
|
fresh_seg ~loc:dst ~bas:dst_seg.bas ~len:dst_seg.len ~siz:dst_seg.siz
|
|
|
|
|
~arr:src_seg.arr us
|
|
|
|
|
in
|
|
|
|
|
let dst_heap' = Sh.seg dst_seg' in
|
|
|
|
|
let xs = Set.union dst_xs src_xs in
|
|
|
|
|
let foot = Sh.star dst_heap src_heap in
|
|
|
|
|
let post = Sh.star dst_heap' src_heap in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
let memcpy_specs us dst src len =
|
|
|
|
|
[memcpy_eq_spec us dst src len; memcpy_dj_spec us dst src len]
|
|
|
|
|
|
|
|
|
|
(* { d=s * d-[b;m)->⟨l,α⟩ }
|
|
|
|
|
* memmov l d s
|
|
|
|
|
* { d-[b;m)->⟨l,α⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let memmov_eq_spec us dst src len =
|
|
|
|
|
let {xs; seg= dst_seg} = fresh_seg ~loc:dst ~len us in
|
|
|
|
|
let dst_heap = Sh.seg dst_seg in
|
|
|
|
|
let foot = Sh.and_ (Exp.eq dst src) dst_heap in
|
|
|
|
|
let post = dst_heap in
|
|
|
|
|
{xs; foot; 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 us dst src len =
|
|
|
|
|
let xs = Var.Set.empty in
|
|
|
|
|
let bas, us, xs = fresh_var "b" us xs in
|
|
|
|
|
let siz, us, xs = fresh_var "m" us xs in
|
|
|
|
|
let arr_dst, us, xs = fresh_var "a" us xs in
|
|
|
|
|
let arr_mid, us, xs = fresh_var "a" us xs in
|
|
|
|
|
let arr_src, us, xs = fresh_var "a" us xs in
|
|
|
|
|
let src_dst = Exp.sub src dst in
|
|
|
|
|
let mem_dst = Exp.memory ~siz:src_dst ~arr:arr_dst in
|
|
|
|
|
let siz_mid = Exp.sub len src_dst in
|
|
|
|
|
let mem_mid = Exp.memory ~siz:siz_mid ~arr:arr_mid in
|
|
|
|
|
let mem_src = Exp.memory ~siz:src_dst ~arr:arr_src in
|
|
|
|
|
let mem_mid_src = Exp.concat mem_mid mem_src in
|
|
|
|
|
let mem_dst_mid_src = Exp.concat mem_dst mem_mid_src in
|
|
|
|
|
let siz_dst_mid_src, us, xs = fresh_var "m" us xs in
|
|
|
|
|
let arr_dst_mid_src, _, xs = fresh_var "a" us xs in
|
|
|
|
|
let eq_mem_dst_mid_src =
|
|
|
|
|
Exp.eq mem_dst_mid_src
|
|
|
|
|
(Exp.memory ~siz:siz_dst_mid_src ~arr:arr_dst_mid_src)
|
|
|
|
|
in
|
|
|
|
|
let seg =
|
|
|
|
|
Sh.seg
|
|
|
|
|
{loc= dst; bas; len= siz; siz= siz_dst_mid_src; arr= arr_dst_mid_src}
|
|
|
|
|
in
|
|
|
|
|
let foot =
|
|
|
|
|
Sh.and_ eq_mem_dst_mid_src
|
|
|
|
|
(Sh.and_ (Exp.lt dst src) (Sh.and_ (Exp.lt src (Exp.add dst len)) seg))
|
|
|
|
|
in
|
|
|
|
|
(xs, 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 us dst src len =
|
|
|
|
|
let xs, bas, siz, _, mem_mid, mem_src, foot =
|
|
|
|
|
memmov_foot us dst src len
|
|
|
|
|
in
|
|
|
|
|
let mem_mid_src_src = Exp.concat (Exp.concat mem_mid mem_src) mem_src in
|
|
|
|
|
let siz_mid_src_src, us, xs = fresh_var "m" us xs in
|
|
|
|
|
let arr_mid_src_src, _, xs = fresh_var "a" us xs in
|
|
|
|
|
let eq_mem_mid_src_src =
|
|
|
|
|
Exp.eq mem_mid_src_src
|
|
|
|
|
(Exp.memory ~siz:siz_mid_src_src ~arr:arr_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
|
|
|
|
|
; arr= arr_mid_src_src })
|
|
|
|
|
in
|
|
|
|
|
{xs; foot; 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 us dst src len =
|
|
|
|
|
let xs, bas, siz, mem_src, mem_mid, _, foot =
|
|
|
|
|
memmov_foot us src dst len
|
|
|
|
|
in
|
|
|
|
|
let mem_src_src_mid = Exp.concat mem_src (Exp.concat mem_src mem_mid) in
|
|
|
|
|
let siz_src_src_mid, us, xs = fresh_var "m" us xs in
|
|
|
|
|
let arr_src_src_mid, _, xs = fresh_var "a" us xs in
|
|
|
|
|
let eq_mem_src_src_mid =
|
|
|
|
|
Exp.eq mem_src_src_mid
|
|
|
|
|
(Exp.memory ~siz:siz_src_src_mid ~arr:arr_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
|
|
|
|
|
; arr= arr_src_src_mid })
|
|
|
|
|
in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
let memmov_specs us dst src len =
|
|
|
|
|
[ memmov_eq_spec us dst src len
|
|
|
|
|
; memmov_dj_spec us dst src len
|
|
|
|
|
; memmov_dn_spec us dst src len
|
|
|
|
|
; memmov_up_spec us dst src len ]
|
|
|
|
|
|
|
|
|
|
(* { p-[b;m)->⟨l,α⟩ }
|
|
|
|
|
* r = strlen p
|
|
|
|
|
* { r=b+m-p-1 * p-[b;m)->⟨l,α⟩ }
|
|
|
|
|
*)
|
|
|
|
|
let strlen_spec us reg ptr =
|
|
|
|
|
let {xs; seg} = fresh_seg ~loc:ptr us in
|
|
|
|
|
let foot = Sh.seg seg in
|
|
|
|
|
let {Sh.loc= p; bas= b; len= m} = seg in
|
|
|
|
|
let ret = Exp.sub (Exp.add (Exp.sub b p) m) (Exp.integer Z.one) in
|
|
|
|
|
let post = Sh.and_ (Exp.eq (Exp.var reg) ret) foot in
|
|
|
|
|
{xs; foot; post}
|
|
|
|
|
|
|
|
|
|
(* execute a command with given spec from pre *)
|
|
|
|
|
let exec_spec pre {xs; foot; post} =
|
|
|
|
|
[%Trace.call fun {pf} ->
|
|
|
|
|
pf "@[%a@]@ @[<2>%a@,@[{%a}@;<0 -1>-{%a}@]@]" Sh.pp pre Sh.pp_us xs
|
|
|
|
|
Sh.pp foot Sh.pp post ;
|
|
|
|
|
assert (
|
|
|
|
|
let vs = Set.diff (Set.diff foot.Sh.us xs) pre.Sh.us in
|
|
|
|
|
Set.is_empty vs || Trace.report "unbound foot: {%a}" Var.Set.pp vs ) ;
|
|
|
|
|
assert (
|
|
|
|
|
let vs = Set.diff (Set.diff post.Sh.us xs) pre.Sh.us in
|
|
|
|
|
Set.is_empty vs || Trace.report "unbound post: {%a}" Var.Set.pp vs )]
|
|
|
|
|
;
|
|
|
|
|
let zs, pre = Sh.bind_exists pre ~wrt:xs in
|
|
|
|
|
( match Solver.infer_frame pre xs foot with
|
|
|
|
|
| Some frame -> Ok (Sh.exists (Set.union zs xs) (Sh.star frame post))
|
|
|
|
|
| None -> Error () )
|
|
|
|
|
|>
|
|
|
|
|
[%Trace.retn fun {pf} r -> pf "%a" (Result.pp "%a" Sh.pp) r]
|
|
|
|
|
|
|
|
|
|
(* execute a multiple-spec command, where the disjunction of the specs
|
|
|
|
|
preconditions are known to be tautologous *)
|
|
|
|
|
let rec exec_specs pre = function
|
|
|
|
|
| ({xs; foot} as spec) :: specs ->
|
|
|
|
|
let open Result.Monad_infix in
|
|
|
|
|
let pre_pure = Sh.star (Sh.exists xs (Sh.pure_approx foot)) pre in
|
|
|
|
|
exec_spec pre_pure spec
|
|
|
|
|
>>= fun post ->
|
|
|
|
|
exec_specs pre specs >>| fun posts -> Sh.or_ post posts
|
|
|
|
|
| [] -> Ok (Sh.false_ pre.us)
|
|
|
|
|
|
|
|
|
|
let inst : Sh.t -> Llair.inst -> (Sh.t, _) result =
|
|
|
|
|
fun pre inst ->
|
|
|
|
|
[%Trace.info "@[<2>exec inst %a from@ %a@]" Llair.Inst.pp inst Sh.pp pre] ;
|
|
|
|
|
assert (Set.disjoint (Sh.fv pre) (Llair.Inst.locals inst)) ;
|
|
|
|
|
let us = pre.us in
|
|
|
|
|
( match inst with
|
|
|
|
|
| Nondet _ -> Ok pre
|
|
|
|
|
| Alloc {reg; num; len} -> exec_spec pre (alloc_spec us reg num len)
|
|
|
|
|
| Free {ptr} -> exec_spec pre (free_spec us ptr)
|
|
|
|
|
| Load {reg; ptr; len} -> exec_spec pre (load_spec us reg ptr len)
|
|
|
|
|
| Store {ptr; exp; len} -> exec_spec pre (store_spec us ptr exp len)
|
|
|
|
|
| Memset {dst; byt; len} -> exec_spec pre (memset_spec us dst byt len)
|
|
|
|
|
| Memcpy {dst; src; len} -> exec_specs pre (memcpy_specs us dst src len)
|
|
|
|
|
| Memmov {dst; src; len} -> exec_specs pre (memmov_specs us dst src len)
|
|
|
|
|
| Strlen {reg; ptr} -> exec_spec pre (strlen_spec us reg ptr) )
|
|
|
|
|
|> Result.map_error ~f:(fun () -> (pre, inst))
|