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.
477 lines
17 KiB
477 lines
17 KiB
6 years ago
|
(*
|
||
6 years ago
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
6 years ago
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*)
|
||
|
open! IStd
|
||
|
module F = Format
|
||
6 years ago
|
module L = Logging
|
||
5 years ago
|
open PulseBasicInterface
|
||
6 years ago
|
|
||
6 years ago
|
(* {2 Abstract domain description } *)
|
||
|
|
||
6 years ago
|
(* {3 Heap domain } *)
|
||
|
|
||
5 years ago
|
module AddrHistPair = struct
|
||
5 years ago
|
type t = AbstractValue.t * ValueHistory.t [@@deriving compare]
|
||
6 years ago
|
|
||
|
let pp f addr_trace =
|
||
|
if Config.debug_level_analysis >= 3 then
|
||
5 years ago
|
Pp.pair ~fst:AbstractValue.pp ~snd:ValueHistory.pp f addr_trace
|
||
|
else AbstractValue.pp f (fst addr_trace)
|
||
6 years ago
|
end
|
||
|
|
||
6 years ago
|
module Memory : sig
|
||
6 years ago
|
module Access : sig
|
||
5 years ago
|
include PrettyPrintable.PrintableOrderedType with type t = AbstractValue.t HilExp.Access.t
|
||
6 years ago
|
|
||
|
val equal : t -> t -> bool
|
||
|
end
|
||
6 years ago
|
|
||
|
module Edges : PrettyPrintable.PPMap with type key = Access.t
|
||
6 years ago
|
|
||
5 years ago
|
type edges = AddrHistPair.t Edges.t
|
||
6 years ago
|
|
||
6 years ago
|
val pp_edges : F.formatter -> edges -> unit
|
||
|
|
||
6 years ago
|
type cell = edges * Attributes.t
|
||
|
|
||
6 years ago
|
type t
|
||
6 years ago
|
|
||
|
val empty : t
|
||
|
|
||
5 years ago
|
val filter : (AbstractValue.t -> bool) -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val filter_heap : (AbstractValue.t -> edges -> bool) -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val find_opt : AbstractValue.t -> t -> cell option
|
||
6 years ago
|
|
||
5 years ago
|
val fold_attrs : (AbstractValue.t -> Attributes.t -> 'acc -> 'acc) -> t -> 'acc -> 'acc
|
||
6 years ago
|
|
||
5 years ago
|
val set_attrs : AbstractValue.t -> Attributes.t -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val set_edges : AbstractValue.t -> edges -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val set_cell : AbstractValue.t -> cell -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val find_edges_opt : AbstractValue.t -> t -> edges option
|
||
6 years ago
|
|
||
5 years ago
|
val mem_edges : AbstractValue.t -> t -> bool
|
||
6 years ago
|
|
||
5 years ago
|
val pp_heap : F.formatter -> t -> unit
|
||
|
|
||
|
val pp_attributes : F.formatter -> t -> unit
|
||
6 years ago
|
|
||
5 years ago
|
val register_address : AbstractValue.t -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val add_edge : AbstractValue.t -> Access.t -> AddrHistPair.t -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val find_edge_opt : AbstractValue.t -> Access.t -> t -> AddrHistPair.t option
|
||
6 years ago
|
|
||
5 years ago
|
val add_attribute : AbstractValue.t -> Attribute.t -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val invalidate : AbstractValue.t * ValueHistory.t -> Invalidation.t -> Location.t -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val check_valid : AbstractValue.t -> t -> (unit, Invalidation.t Trace.t) result
|
||
6 years ago
|
|
||
5 years ago
|
val get_closure_proc_name : AbstractValue.t -> t -> Typ.Procname.t option
|
||
6 years ago
|
|
||
5 years ago
|
val get_constant : AbstractValue.t -> t -> Const.t option
|
||
5 years ago
|
|
||
5 years ago
|
val std_vector_reserve : AbstractValue.t -> t -> t
|
||
6 years ago
|
|
||
5 years ago
|
val is_std_vector_reserved : AbstractValue.t -> t -> bool
|
||
6 years ago
|
end = struct
|
||
6 years ago
|
module Access = struct
|
||
5 years ago
|
type t = AbstractValue.t HilExp.Access.t [@@deriving compare]
|
||
6 years ago
|
|
||
6 years ago
|
let equal = [%compare.equal: t]
|
||
|
|
||
5 years ago
|
let pp = HilExp.Access.pp AbstractValue.pp
|
||
6 years ago
|
end
|
||
|
|
||
|
module Edges = PrettyPrintable.MakePPMap (Access)
|
||
6 years ago
|
|
||
5 years ago
|
type edges = AddrHistPair.t Edges.t [@@deriving compare]
|
||
6 years ago
|
|
||
5 years ago
|
let pp_edges = Edges.pp ~pp_value:AddrHistPair.pp
|
||
6 years ago
|
|
||
6 years ago
|
type cell = edges * Attributes.t
|
||
6 years ago
|
|
||
5 years ago
|
module Graph = PrettyPrintable.MakePPMap (AbstractValue)
|
||
6 years ago
|
|
||
6 years ago
|
type t = edges Graph.t * Attributes.t Graph.t
|
||
6 years ago
|
|
||
5 years ago
|
let pp_heap fmt (heap, _) = Graph.pp ~pp_value:pp_edges fmt heap
|
||
|
|
||
|
let pp_attributes fmt (_, attrs) = Graph.pp ~pp_value:Attributes.pp fmt attrs
|
||
6 years ago
|
|
||
6 years ago
|
let register_address addr memory =
|
||
|
if Graph.mem addr (fst memory) then memory
|
||
|
else (Graph.add addr Edges.empty (fst memory), snd memory)
|
||
|
|
||
|
|
||
6 years ago
|
(* {3 Helper functions to traverse the two maps at once } *)
|
||
6 years ago
|
|
||
6 years ago
|
let add_edge addr_src access value memory =
|
||
6 years ago
|
let old_edges = Graph.find_opt addr_src (fst memory) |> Option.value ~default:Edges.empty in
|
||
|
let new_edges = Edges.add access value old_edges in
|
||
|
if phys_equal old_edges new_edges then memory
|
||
|
else (Graph.add addr_src new_edges (fst memory), snd memory)
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
let find_edge_opt addr access memory =
|
||
6 years ago
|
let open Option.Monad_infix in
|
||
6 years ago
|
Graph.find_opt addr (fst memory) >>= Edges.find_opt access
|
||
6 years ago
|
|
||
6 years ago
|
|
||
5 years ago
|
let add_attribute addr attribute memory =
|
||
|
match Graph.find_opt addr (snd memory) with
|
||
|
| None ->
|
||
|
(fst memory, Graph.add addr (Attributes.singleton attribute) (snd memory))
|
||
|
| Some old_attrs ->
|
||
|
let new_attrs = Attributes.add old_attrs attribute in
|
||
6 years ago
|
(fst memory, Graph.add addr new_attrs (snd memory))
|
||
6 years ago
|
|
||
|
|
||
5 years ago
|
let invalidate (address, history) invalid location memory =
|
||
|
let invalidation = Trace.Immediate {imm= invalid; location; history} in
|
||
|
add_attribute address (Attribute.Invalid invalidation) memory
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
let check_valid address memory =
|
||
5 years ago
|
L.d_printfln "Checking validity of %a" AbstractValue.pp address ;
|
||
6 years ago
|
match Graph.find_opt address (snd memory) |> Option.bind ~f:Attributes.get_invalid with
|
||
6 years ago
|
| Some invalidation ->
|
||
|
Error invalidation
|
||
|
| None ->
|
||
|
Ok ()
|
||
6 years ago
|
|
||
6 years ago
|
|
||
6 years ago
|
let get_closure_proc_name address memory =
|
||
|
Graph.find_opt address (snd memory)
|
||
|
|> Option.bind ~f:(fun attributes -> Attributes.get_closure_proc_name attributes)
|
||
|
|
||
|
|
||
5 years ago
|
let get_constant address memory =
|
||
|
Graph.find_opt address (snd memory)
|
||
|
|> Option.bind ~f:(fun attributes -> Attributes.get_constant attributes)
|
||
|
|
||
|
|
||
6 years ago
|
let std_vector_reserve address memory = add_attribute address Attribute.StdVectorReserve memory
|
||
6 years ago
|
|
||
6 years ago
|
let is_std_vector_reserved address memory =
|
||
6 years ago
|
Graph.find_opt address (snd memory)
|
||
6 years ago
|
|> Option.value_map ~default:false ~f:(fun attributes ->
|
||
6 years ago
|
Attributes.is_std_vector_reserved attributes )
|
||
6 years ago
|
|
||
6 years ago
|
|
||
6 years ago
|
(* {3 Monomorphic {!PPMap} interface as needed } *)
|
||
6 years ago
|
|
||
6 years ago
|
let empty = (Graph.empty, Graph.empty)
|
||
|
|
||
6 years ago
|
let find_edges_opt addr memory = Graph.find_opt addr (fst memory)
|
||
|
|
||
|
let find_attrs_opt addr memory = Graph.find_opt addr (snd memory)
|
||
|
|
||
6 years ago
|
let find_opt addr memory =
|
||
6 years ago
|
match (find_edges_opt addr memory, find_attrs_opt addr memory) with
|
||
6 years ago
|
| None, None ->
|
||
|
None
|
||
|
| edges_opt, attrs_opt ->
|
||
|
let edges = Option.value edges_opt ~default:Edges.empty in
|
||
|
let attrs = Option.value attrs_opt ~default:Attributes.empty in
|
||
|
Some (edges, attrs)
|
||
|
|
||
6 years ago
|
|
||
6 years ago
|
let fold_attrs f memory init = Graph.fold f (snd memory) init
|
||
|
|
||
6 years ago
|
let set_attrs addr attrs memory = (fst memory, Graph.add addr attrs (snd memory))
|
||
|
|
||
|
let set_edges addr edges memory = (Graph.add addr edges (fst memory), snd memory)
|
||
|
|
||
6 years ago
|
let set_cell addr (edges, attrs) memory =
|
||
|
(Graph.add addr edges (fst memory), Graph.add addr attrs (snd memory))
|
||
6 years ago
|
|
||
6 years ago
|
|
||
6 years ago
|
let filter f memory =
|
||
|
let heap = Graph.filter (fun address _ -> f address) (fst memory) in
|
||
|
let attrs = Graph.filter (fun address _ -> f address) (snd memory) in
|
||
|
if phys_equal heap (fst memory) && phys_equal attrs (snd memory) then memory else (heap, attrs)
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
let filter_heap f memory =
|
||
|
let heap = Graph.filter f (fst memory) in
|
||
|
if phys_equal heap (fst memory) then memory else (heap, snd memory)
|
||
|
|
||
|
|
||
6 years ago
|
let mem_edges addr memory = Graph.mem addr (fst memory)
|
||
6 years ago
|
end
|
||
6 years ago
|
|
||
6 years ago
|
(** Stacks: map addresses of variables to values and initialisation location. *)
|
||
6 years ago
|
module Stack = struct
|
||
6 years ago
|
module VarAddress = struct
|
||
|
include Var
|
||
|
|
||
|
let pp f var =
|
||
|
let pp_ampersand f = function
|
||
|
| ProgramVar _ ->
|
||
|
F.pp_print_string f "&"
|
||
|
| LogicalVar _ ->
|
||
|
()
|
||
|
in
|
||
|
F.fprintf f "%a%a" pp_ampersand var Var.pp var
|
||
|
end
|
||
|
|
||
5 years ago
|
include PrettyPrintable.MakePPMonoMap (VarAddress) (AddrHistPair)
|
||
6 years ago
|
|
||
|
let pp fmt m =
|
||
|
let pp_item fmt (var_address, v) =
|
||
5 years ago
|
F.fprintf fmt "%a=%a" VarAddress.pp var_address AddrHistPair.pp v
|
||
6 years ago
|
in
|
||
|
PrettyPrintable.pp_collection ~pp_item fmt (bindings m)
|
||
6 years ago
|
|
||
|
|
||
5 years ago
|
let compare = compare AddrHistPair.compare
|
||
6 years ago
|
end
|
||
6 years ago
|
|
||
6 years ago
|
type t = {heap: Memory.t; stack: Stack.t}
|
||
6 years ago
|
|
||
6 years ago
|
let empty =
|
||
6 years ago
|
{ heap=
|
||
|
Memory.empty
|
||
|
(* TODO: we could record that 0 is an invalid address at this point but this makes the
|
||
|
analysis go a bit overboard with the Nullptr reports. *)
|
||
|
; stack= Stack.empty }
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
(** comparison between two elements of the domain to determine the [<=] relation
|
||
|
|
||
6 years ago
|
Given two states [lhs] and [rhs], try to find a bijection [lhs_to_rhs] (with inverse
|
||
|
[rhs_to_lhs]) between the addresses of [lhs] and [rhs] such that [lhs_to_rhs(reachable(lhs)) =
|
||
|
reachable(rhs)] (where addresses are reachable if they are reachable from stack variables). *)
|
||
6 years ago
|
module GraphComparison = struct
|
||
5 years ago
|
module AddressMap = PrettyPrintable.MakePPMap (AbstractValue)
|
||
6 years ago
|
|
||
|
(** translation between the abstract values on the LHS and the ones on the RHS *)
|
||
|
type mapping =
|
||
5 years ago
|
{ rhs_to_lhs: AbstractValue.t AddressMap.t (** map from RHS values to LHS *)
|
||
|
; lhs_to_rhs: AbstractValue.t AddressMap.t (** inverse map from [rhs_to_lhs] *) }
|
||
6 years ago
|
|
||
|
let empty_mapping = {rhs_to_lhs= AddressMap.empty; lhs_to_rhs= AddressMap.empty}
|
||
|
|
||
|
let pp_mapping fmt {rhs_to_lhs; lhs_to_rhs} =
|
||
|
F.fprintf fmt "@[<v>{ rhs_to_lhs=@[<hv2>%a@];@,lhs_to_rhs=@[<hv2>%a@];@,}@]"
|
||
5 years ago
|
(AddressMap.pp ~pp_value:AbstractValue.pp)
|
||
6 years ago
|
rhs_to_lhs
|
||
5 years ago
|
(AddressMap.pp ~pp_value:AbstractValue.pp)
|
||
6 years ago
|
lhs_to_rhs
|
||
|
|
||
|
|
||
|
(** try to add the fact that [addr_lhs] corresponds to [addr_rhs] to the [mapping] *)
|
||
|
let record_equal ~addr_lhs ~addr_rhs mapping =
|
||
|
(* have we seen [addr_lhs] before?.. *)
|
||
|
match AddressMap.find_opt addr_lhs mapping.lhs_to_rhs with
|
||
5 years ago
|
| Some addr_rhs' when not (AbstractValue.equal addr_rhs addr_rhs') ->
|
||
6 years ago
|
(* ...yes, but it was bound to another address *)
|
||
|
L.d_printfln
|
||
|
"Aliasing in LHS not in RHS: LHS address %a in current already bound to %a, not %a@\n\
|
||
|
State=%a"
|
||
5 years ago
|
AbstractValue.pp addr_lhs AbstractValue.pp addr_rhs' AbstractValue.pp addr_rhs pp_mapping
|
||
|
mapping ;
|
||
6 years ago
|
`AliasingLHS
|
||
|
| Some _addr_rhs (* [_addr_rhs = addr_rhs] *) ->
|
||
|
`AlreadyVisited
|
||
|
| None -> (
|
||
|
(* ...and have we seen [addr_rhs] before?.. *)
|
||
|
match AddressMap.find_opt addr_rhs mapping.rhs_to_lhs with
|
||
|
| Some addr_lhs' ->
|
||
|
(* ...yes, but it was bound to another address: [addr_lhs' != addr_lhs] otherwise we would
|
||
|
have found [addr_lhs] in the [lhs_to_rhs] map above *)
|
||
|
L.d_printfln
|
||
|
"Aliasing in RHS not in LHS: RHS address %a in current already bound to %a, not %a@\n\
|
||
|
State=%a"
|
||
5 years ago
|
AbstractValue.pp addr_rhs AbstractValue.pp addr_lhs' AbstractValue.pp addr_lhs
|
||
6 years ago
|
pp_mapping mapping ;
|
||
|
`AliasingRHS
|
||
|
| None ->
|
||
|
(* [addr_rhs] and [addr_lhs] are both new, record that they correspond to each other *)
|
||
|
let mapping' =
|
||
|
{ rhs_to_lhs= AddressMap.add addr_rhs addr_lhs mapping.rhs_to_lhs
|
||
|
; lhs_to_rhs= AddressMap.add addr_lhs addr_rhs mapping.lhs_to_rhs }
|
||
|
in
|
||
|
`NotAlreadyVisited mapping' )
|
||
|
|
||
|
|
||
6 years ago
|
type isograph_relation =
|
||
|
| NotIsomorphic (** no mapping was found that can make LHS the same as the RHS *)
|
||
|
| IsomorphicUpTo of mapping (** [mapping(lhs)] is isomorphic to [rhs] *)
|
||
6 years ago
|
|
||
6 years ago
|
(** can we extend [mapping] so that the subgraph of [lhs] rooted at [addr_lhs] is isomorphic to
|
||
|
the subgraph of [rhs] rooted at [addr_rhs]? *)
|
||
|
let rec isograph_map_from_address ~lhs ~addr_lhs ~rhs ~addr_rhs mapping =
|
||
5 years ago
|
L.d_printfln "%a<->%a@\n" AbstractValue.pp addr_lhs AbstractValue.pp addr_rhs ;
|
||
6 years ago
|
match record_equal mapping ~addr_lhs ~addr_rhs with
|
||
|
| `AlreadyVisited ->
|
||
6 years ago
|
IsomorphicUpTo mapping
|
||
6 years ago
|
| `AliasingRHS | `AliasingLHS ->
|
||
6 years ago
|
NotIsomorphic
|
||
6 years ago
|
| `NotAlreadyVisited mapping -> (
|
||
6 years ago
|
let get_non_empty_cell = function
|
||
|
| None ->
|
||
|
None
|
||
|
| Some (edges, attrs) when Memory.Edges.is_empty edges && Attributes.is_empty attrs ->
|
||
|
(* this can happen because of [register_address] or because we don't care to delete empty
|
||
|
edges when removing edges *)
|
||
|
None
|
||
|
| Some _ as some_cell ->
|
||
|
some_cell
|
||
|
in
|
||
|
let lhs_cell_opt = Memory.find_opt addr_lhs lhs.heap |> get_non_empty_cell in
|
||
|
let rhs_cell_opt = Memory.find_opt addr_rhs rhs.heap |> get_non_empty_cell in
|
||
|
match (lhs_cell_opt, rhs_cell_opt) with
|
||
|
| None, None ->
|
||
|
IsomorphicUpTo mapping
|
||
|
| Some _, None | None, Some _ ->
|
||
|
NotIsomorphic
|
||
|
| Some (edges_rhs, attrs_rhs), Some (edges_lhs, attrs_lhs) ->
|
||
6 years ago
|
(* continue the comparison recursively on all edges and attributes *)
|
||
6 years ago
|
if Attributes.equal attrs_rhs attrs_lhs then
|
||
6 years ago
|
let bindings_lhs = Memory.Edges.bindings edges_lhs in
|
||
|
let bindings_rhs = Memory.Edges.bindings edges_rhs in
|
||
6 years ago
|
isograph_map_edges ~lhs ~edges_lhs:bindings_lhs ~rhs ~edges_rhs:bindings_rhs mapping
|
||
|
else NotIsomorphic )
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
(** check that the isograph relation can be extended for all edges *)
|
||
|
and isograph_map_edges ~lhs ~edges_lhs ~rhs ~edges_rhs mapping =
|
||
6 years ago
|
match (edges_lhs, edges_rhs) with
|
||
6 years ago
|
| [], [] ->
|
||
|
IsomorphicUpTo mapping
|
||
6 years ago
|
| (a_lhs, (addr_lhs, _trace_lhs)) :: edges_lhs, (a_rhs, (addr_rhs, _trace_rhs)) :: edges_rhs
|
||
|
when Memory.Access.equal a_lhs a_rhs -> (
|
||
6 years ago
|
(* check isograph relation from the destination addresses *)
|
||
|
match isograph_map_from_address ~lhs ~addr_lhs ~rhs ~addr_rhs mapping with
|
||
|
| IsomorphicUpTo mapping ->
|
||
6 years ago
|
(* ok: continue with the other edges *)
|
||
6 years ago
|
isograph_map_edges ~lhs ~edges_lhs ~rhs ~edges_rhs mapping
|
||
|
| NotIsomorphic ->
|
||
|
NotIsomorphic )
|
||
|
| _ :: _, _ :: _ | [], _ :: _ | _ :: _, [] ->
|
||
|
NotIsomorphic
|
||
6 years ago
|
|
||
|
|
||
|
(** check that the memory graph induced by the addresses in [lhs] reachable from the variables in
|
||
6 years ago
|
[stack_lhs] is a isograph of the same graph in [rhs] starting from [stack_rhs], up to some
|
||
6 years ago
|
[mapping] *)
|
||
6 years ago
|
let rec isograph_map_from_stack ~lhs ~stack_lhs ~rhs ~stack_rhs mapping =
|
||
6 years ago
|
match (stack_lhs, stack_rhs) with
|
||
6 years ago
|
| [], [] ->
|
||
|
IsomorphicUpTo mapping
|
||
6 years ago
|
| ( (var_lhs, (addr_lhs, _trace_lhs)) :: stack_lhs
|
||
|
, (var_rhs, (addr_rhs, _trace_rhs)) :: stack_rhs )
|
||
|
when Var.equal var_lhs var_rhs -> (
|
||
6 years ago
|
match isograph_map_from_address ~lhs ~addr_lhs ~rhs ~addr_rhs mapping with
|
||
|
| IsomorphicUpTo mapping ->
|
||
|
isograph_map_from_stack ~lhs ~stack_lhs ~rhs ~stack_rhs mapping
|
||
|
| NotIsomorphic ->
|
||
|
NotIsomorphic )
|
||
|
| _ :: _, _ :: _ | [], _ :: _ | _ :: _, [] ->
|
||
|
NotIsomorphic
|
||
|
|
||
|
|
||
|
let isograph_map ~lhs ~rhs mapping =
|
||
6 years ago
|
let stack_lhs = Stack.bindings lhs.stack in
|
||
|
let stack_rhs = Stack.bindings rhs.stack in
|
||
6 years ago
|
isograph_map_from_stack ~lhs ~rhs ~stack_lhs ~stack_rhs mapping
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
let is_isograph ~lhs ~rhs mapping =
|
||
|
match isograph_map ~lhs ~rhs mapping with IsomorphicUpTo _ -> true | NotIsomorphic -> false
|
||
6 years ago
|
end
|
||
6 years ago
|
|
||
6 years ago
|
let ( <= ) ~lhs ~rhs =
|
||
6 years ago
|
phys_equal lhs rhs || GraphComparison.is_isograph ~lhs ~rhs GraphComparison.empty_mapping
|
||
6 years ago
|
|
||
6 years ago
|
|
||
6 years ago
|
let pp fmt {heap; stack} =
|
||
5 years ago
|
F.fprintf fmt "{@[<v1> roots=@[<hv>%a@];@;mem =@[<hv>%a@];@;attrs=@[<hv>%a@];@]}" Stack.pp stack
|
||
|
Memory.pp_heap heap Memory.pp_attributes heap
|
||
6 years ago
|
|
||
|
|
||
6 years ago
|
module GraphVisit : sig
|
||
|
val fold :
|
||
|
var_filter:(Var.t -> bool)
|
||
|
-> t
|
||
|
-> init:'accum
|
||
|
-> f:( 'accum
|
||
5 years ago
|
-> AbstractValue.t
|
||
6 years ago
|
-> Var.t
|
||
|
-> Memory.Access.t list
|
||
|
-> ('accum, 'final) Base.Continue_or_stop.t)
|
||
|
-> finish:('accum -> 'final)
|
||
5 years ago
|
-> AbstractValue.Set.t * 'final
|
||
6 years ago
|
(** Generic graph traversal of the memory starting from each variable in the stack that pass
|
||
|
[var_filter], in order. Returns the result of folding over every address in the graph and the
|
||
|
set of addresses that have been visited before [f] returned [Stop] or all reachable addresses
|
||
|
were seen. [f] is passed each address together with the variable from which the address was
|
||
|
reached and the access path from that variable to the address. *)
|
||
6 years ago
|
end = struct
|
||
6 years ago
|
open Base.Continue_or_stop
|
||
|
|
||
6 years ago
|
let visit address visited =
|
||
5 years ago
|
if AbstractValue.Set.mem address visited then `AlreadyVisited
|
||
6 years ago
|
else
|
||
5 years ago
|
let visited = AbstractValue.Set.add address visited in
|
||
6 years ago
|
`NotAlreadyVisited visited
|
||
|
|
||
|
|
||
6 years ago
|
let rec visit_address orig_var ~f rev_accesses astate address ((visited, accum) as visited_accum)
|
||
|
=
|
||
6 years ago
|
match visit address visited with
|
||
|
| `AlreadyVisited ->
|
||
6 years ago
|
Continue visited_accum
|
||
6 years ago
|
| `NotAlreadyVisited visited -> (
|
||
6 years ago
|
match f accum address orig_var rev_accesses with
|
||
|
| Continue accum -> (
|
||
|
match Memory.find_opt address astate.heap with
|
||
|
| None ->
|
||
|
Continue (visited, accum)
|
||
|
| Some (edges, _) ->
|
||
|
visit_edges orig_var ~f rev_accesses astate ~edges (visited, accum) )
|
||
|
| Stop fin ->
|
||
|
Stop (visited, fin) )
|
||
|
|
||
|
|
||
|
and visit_edges orig_var ~f rev_accesses ~edges astate visited_accum =
|
||
|
let finish visited_accum = Continue visited_accum in
|
||
|
Container.fold_until edges
|
||
|
~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Memory.Edges.fold)
|
||
|
~finish ~init:visited_accum ~f:(fun visited_accum (access, (address, _trace)) ->
|
||
|
match visit_address orig_var ~f (access :: rev_accesses) astate address visited_accum with
|
||
|
| Continue _ as cont ->
|
||
|
cont
|
||
|
| Stop fin ->
|
||
|
Stop (Stop fin) )
|
||
|
|
||
|
|
||
|
let fold ~var_filter astate ~init ~f ~finish =
|
||
|
let finish (visited, accum) = (visited, finish accum) in
|
||
5 years ago
|
let init = (AbstractValue.Set.empty, init) in
|
||
6 years ago
|
Container.fold_until astate.stack
|
||
|
~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Stack.fold) ~init ~finish
|
||
|
~f:(fun visited_accum (var, (address, _loc)) ->
|
||
|
if var_filter var then visit_address var ~f [] astate address visited_accum
|
||
|
else Continue visited_accum )
|
||
6 years ago
|
end
|
||
|
|
||
6 years ago
|
include GraphComparison
|
||
6 years ago
|
|
||
|
let reachable_addresses astate =
|
||
|
GraphVisit.fold astate
|
||
|
~var_filter:(fun _ -> true)
|
||
|
~init:() ~finish:Fn.id
|
||
|
~f:(fun () _ _ _ -> Continue ())
|
||
|
|> fst
|