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.
499 lines
20 KiB
499 lines
20 KiB
(*
|
|
* Copyright (c) 2016-present, Programming Research Laboratory (ROPAS)
|
|
* Seoul National University, Korea
|
|
* 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.
|
|
*)
|
|
|
|
open! IStd
|
|
open AbsLoc
|
|
open! AbstractDomain.Types
|
|
module BoUtils = BufferOverrunUtils
|
|
module Dom = BufferOverrunDomain
|
|
module F = Format
|
|
module L = Logging
|
|
module Models = BufferOverrunModels
|
|
module OndemandEnv = BufferOverrunOndemandEnv
|
|
module Sem = BufferOverrunSemantics
|
|
module Trace = BufferOverrunTrace
|
|
|
|
module Payload = SummaryPayload.Make (struct
|
|
type t = BufferOverrunAnalysisSummary.t
|
|
|
|
let field = Payloads.Fields.buffer_overrun_analysis
|
|
end)
|
|
|
|
type extras =
|
|
{ get_summary: BufferOverrunAnalysisSummary.get_summary
|
|
; get_formals: BoUtils.get_formals
|
|
; oenv: OndemandEnv.t }
|
|
|
|
module CFG = ProcCfg.NormalOneInstrPerNode
|
|
|
|
module Init = struct
|
|
let initial_state {ProcData.summary; tenv; extras= {get_summary; oenv}} start_node =
|
|
let try_decl_local =
|
|
let pname = Summary.get_proc_name summary in
|
|
let model_env =
|
|
let node_hash = CFG.Node.hash start_node in
|
|
let location = CFG.Node.loc start_node in
|
|
let integer_type_widths = oenv.OndemandEnv.integer_type_widths in
|
|
BoUtils.ModelEnv.mk_model_env pname ~node_hash location tenv integer_type_widths
|
|
in
|
|
fun (mem, inst_num) {ProcAttributes.name; typ} ->
|
|
let loc = Loc.of_pvar (Pvar.mk name pname) in
|
|
BoUtils.Exec.decl_local model_env (mem, inst_num) (loc, typ)
|
|
in
|
|
let mem = Dom.Mem.init get_summary oenv in
|
|
let mem, _ =
|
|
List.fold ~f:try_decl_local ~init:(mem, 1)
|
|
(Procdesc.get_locals (Summary.get_proc_desc summary))
|
|
in
|
|
mem
|
|
end
|
|
|
|
module TransferFunctions = struct
|
|
module CFG = CFG
|
|
module Domain = Dom.Mem
|
|
|
|
type nonrec extras = extras
|
|
|
|
let instantiate_latest_prune ~ret_id ~callee_exit_mem eval_sym_trace location mem =
|
|
match
|
|
Dom.Mem.get_latest_prune callee_exit_mem
|
|
|> Dom.LatestPrune.subst ~ret_id eval_sym_trace location
|
|
with
|
|
| Ok latest_prune' ->
|
|
(* Note that we are losing some precisions here, e.g., the best results should be "and" of
|
|
caller's and callee's pruned conditions. For now, we defer the implementation of the
|
|
"and" since we haven't seen a case where "and" would help yet. *)
|
|
if Dom.LatestPrune.is_top latest_prune' then mem
|
|
else Dom.Mem.set_latest_prune latest_prune' mem
|
|
| Error `SubstBottom ->
|
|
Dom.Mem.unreachable
|
|
| Error `SubstFail ->
|
|
mem
|
|
|
|
|
|
let symbolic_pname_value pname params typ location mem =
|
|
let obj_path =
|
|
match params with
|
|
| (param, _) :: _ ->
|
|
PowLoc.min_elt_opt (Sem.eval_locs param mem) |> Option.bind ~f:Loc.get_path
|
|
| _ ->
|
|
None
|
|
in
|
|
let path = Symb.SymbolPath.of_callsite ?obj_path ~ret_typ:typ (CallSite.make pname location) in
|
|
Dom.Mem.find (Loc.of_allocsite (Allocsite.make_symbol path)) mem
|
|
|
|
|
|
let instantiate_mem_reachable (ret_id, ret_typ) callee_formals callee_pname params
|
|
~callee_exit_mem ({Dom.eval_locpath} as eval_sym_trace) mem location =
|
|
let formal_locs =
|
|
List.fold callee_formals ~init:PowLoc.bot ~f:(fun acc (formal, _) ->
|
|
PowLoc.add (Loc.of_pvar formal) acc )
|
|
in
|
|
let copy_reachable_locs_from locs mem =
|
|
let copy loc acc =
|
|
Option.value_map (Dom.Mem.find_opt loc callee_exit_mem) ~default:acc ~f:(fun v ->
|
|
let locs = PowLoc.subst_loc loc eval_locpath in
|
|
let v = Dom.Val.subst v eval_sym_trace location in
|
|
PowLoc.fold (fun loc acc -> Dom.Mem.add_heap loc v acc) locs acc )
|
|
in
|
|
let reachable_locs = Dom.Mem.get_reachable_locs_from callee_formals locs callee_exit_mem in
|
|
PowLoc.fold copy (PowLoc.diff reachable_locs formal_locs) mem
|
|
in
|
|
let instantiate_ret_alias mem =
|
|
let subst_loc l =
|
|
Option.find_map (Loc.get_path l) ~f:(fun partial ->
|
|
try
|
|
let locs = eval_locpath partial in
|
|
match PowLoc.is_singleton_or_more locs with
|
|
| IContainer.Singleton loc ->
|
|
Some loc
|
|
| _ ->
|
|
None
|
|
with Not_found_s _ | Caml.Not_found -> None )
|
|
in
|
|
match Dom.Mem.find_ret_alias callee_exit_mem with
|
|
| Bottom ->
|
|
mem
|
|
| NonBottom tgts ->
|
|
let ret_alias = Dom.AliasTargets.subst tgts ~subst_loc in
|
|
Dom.AliasTargets.fold (Dom.Mem.load_alias ret_id) ret_alias mem
|
|
in
|
|
let ret_var = Loc.of_var (Var.of_id ret_id) in
|
|
let ret_val =
|
|
match Procdesc.load callee_pname with
|
|
| Some callee_pdesc when Procdesc.has_added_return_param callee_pdesc ->
|
|
Dom.Val.of_loc (Loc.of_pvar (Pvar.get_ret_param_pvar callee_pname))
|
|
| _ ->
|
|
if Language.curr_language_is Java && Dom.Mem.is_exc_raised callee_exit_mem then
|
|
symbolic_pname_value callee_pname params ret_typ location mem
|
|
else Dom.Mem.find (Loc.of_pvar (Pvar.get_ret_pvar callee_pname)) callee_exit_mem
|
|
in
|
|
Dom.Mem.add_stack ret_var (Dom.Val.subst ret_val eval_sym_trace location) mem
|
|
|> instantiate_ret_alias
|
|
|> copy_reachable_locs_from (PowLoc.join formal_locs (Dom.Val.get_all_locs ret_val))
|
|
|> instantiate_latest_prune ~ret_id ~callee_exit_mem eval_sym_trace location
|
|
|
|
|
|
let instantiate_mem :
|
|
is_params_ref:bool
|
|
-> Typ.IntegerWidths.t
|
|
-> Ident.t * Typ.t
|
|
-> (Pvar.t * Typ.t) list
|
|
-> Procname.t
|
|
-> (Exp.t * Typ.t) list
|
|
-> Dom.Mem.t
|
|
-> BufferOverrunAnalysisSummary.t
|
|
-> Location.t
|
|
-> Dom.Mem.t =
|
|
fun ~is_params_ref integer_type_widths ret callee_formals callee_pname params caller_mem
|
|
callee_exit_mem location ->
|
|
let eval_sym_trace =
|
|
Sem.mk_eval_sym_trace ~is_params_ref integer_type_widths callee_formals params caller_mem
|
|
~mode:Sem.EvalNormal
|
|
in
|
|
let mem =
|
|
instantiate_mem_reachable ret callee_formals callee_pname params ~callee_exit_mem
|
|
eval_sym_trace caller_mem location
|
|
in
|
|
if Language.curr_language_is Java then
|
|
Dom.Mem.incr_iterator_simple_alias_on_call eval_sym_trace ~callee_exit_mem mem
|
|
else mem
|
|
|
|
|
|
let rec is_array_access_exp = function
|
|
| Exp.BinOp ((PlusPI | MinusPI), _, _) | Exp.Lindex _ ->
|
|
true
|
|
| Exp.Cast (_, x) ->
|
|
is_array_access_exp x
|
|
| _ ->
|
|
false
|
|
|
|
|
|
let is_java_enum_values tenv callee_pname =
|
|
Option.exists (Procname.get_class_type_name callee_pname) ~f:(fun callee_class_name ->
|
|
PatternMatch.is_java_enum tenv callee_class_name
|
|
&& String.equal (Procname.get_method callee_pname) "values" )
|
|
|
|
|
|
let assign_java_enum_values get_summary id ~caller_pname ~callee_pname mem =
|
|
let caller_class_name = Procname.get_class_type_name caller_pname in
|
|
let callee_class_name = Procname.get_class_type_name callee_pname in
|
|
let is_caller_class_initializer =
|
|
IOption.exists2 caller_class_name callee_class_name
|
|
~f:(fun caller_class_name callee_class_name ->
|
|
Procname.is_java_class_initializer caller_pname
|
|
&& Typ.equal_name caller_class_name callee_class_name )
|
|
in
|
|
match callee_class_name with
|
|
| Some (JavaClass class_name as typename) ->
|
|
let clinit_mem =
|
|
if is_caller_class_initializer then Some (Dom.Mem.unset_oenv mem)
|
|
else get_summary (Procname.Java (Procname.Java.get_class_initializer typename))
|
|
in
|
|
Option.value_map clinit_mem ~default:mem ~f:(fun clinit_mem ->
|
|
let loc =
|
|
let class_var =
|
|
let class_mangled = Mangled.from_string (JavaClassName.to_string class_name) in
|
|
Loc.of_var (Var.of_pvar (Pvar.mk_global class_mangled))
|
|
in
|
|
let fn = Fieldname.make typename "$VALUES" in
|
|
Loc.append_field class_var ~fn
|
|
in
|
|
let v = Dom.Mem.find loc clinit_mem in
|
|
let mem = Dom.Mem.add_stack (Loc.of_id id) v mem in
|
|
if is_caller_class_initializer then mem
|
|
else
|
|
let arr_locs = Dom.Val.get_all_locs v in
|
|
let arr_v = Dom.Mem.find_set arr_locs clinit_mem in
|
|
Dom.Mem.add_heap_set arr_locs arr_v mem )
|
|
| _ ->
|
|
assert false
|
|
|
|
|
|
let join_java_static_final =
|
|
let known_java_static_fields = String.Set.of_list [".EMPTY"] in
|
|
let is_known_java_static_field fn =
|
|
let fieldname = Fieldname.to_string fn in
|
|
String.Set.exists known_java_static_fields ~f:(fun suffix ->
|
|
String.is_suffix fieldname ~suffix )
|
|
in
|
|
let copy_reachable_locs_from loc ~from_mem ~to_mem =
|
|
let copy loc acc =
|
|
Option.value_map (Dom.Mem.find_opt loc from_mem) ~default:acc ~f:(fun v ->
|
|
Dom.Mem.add_heap loc v acc )
|
|
in
|
|
let reachable_locs = Dom.Mem.get_reachable_locs_from [] (PowLoc.singleton loc) from_mem in
|
|
PowLoc.fold copy reachable_locs to_mem
|
|
in
|
|
fun tenv get_summary exp mem ->
|
|
Option.value_map (Exp.get_java_class_initializer tenv exp) ~default:mem
|
|
~f:(fun (clinit_pname, pvar, fn, field_typ) ->
|
|
let copy_from_class_init () =
|
|
Option.value_map (get_summary clinit_pname) ~default:mem ~f:(fun clinit_mem ->
|
|
let field_loc = Loc.append_field ~typ:field_typ (Loc.of_pvar pvar) ~fn in
|
|
copy_reachable_locs_from field_loc ~from_mem:clinit_mem ~to_mem:mem )
|
|
in
|
|
match field_typ.Typ.desc with
|
|
| Typ.Tptr ({desc= Tstruct _}, _) when is_known_java_static_field fn ->
|
|
(* It copies all of the reachable values when the contents of the field are commonly
|
|
used as immutable, e.g., values of enum. *)
|
|
copy_from_class_init ()
|
|
| Typ.Tptr ({desc= Tarray _}, _) ->
|
|
copy_from_class_init ()
|
|
| _ ->
|
|
mem )
|
|
|
|
|
|
let modeled_load_of_empty_collection_opt =
|
|
let known_empty_collections = String.Set.of_list ["EMPTY_LIST"; "EMPTY_SET"; "EMPTY_MAP"] in
|
|
fun exp model_env ret mem ->
|
|
match exp with
|
|
| Exp.Lfield (_, fieldname, typ)
|
|
when String.Set.mem known_empty_collections (Fieldname.get_field_name fieldname)
|
|
&& String.equal "java.util.Collections" (Typ.to_string typ) ->
|
|
Models.Collection.create_collection model_env ~ret mem ~length:Itv.zero |> Option.some
|
|
| _ ->
|
|
None
|
|
|
|
|
|
let modeled_range_of_exp location exp mem =
|
|
match exp with
|
|
| Exp.Lindex (arr_exp, _) ->
|
|
let length =
|
|
Sem.eval_array_locs_length (Sem.eval_locs arr_exp mem) mem |> Dom.Val.get_itv
|
|
in
|
|
Option.map (Itv.get_const length)
|
|
~f:(Dom.ModeledRange.of_big_int ~trace:(Bounds.BoundTrace.of_loop location))
|
|
| _ ->
|
|
None
|
|
|
|
|
|
let load_global_constant get_summary ((id, _) as ret) pvar location mem ~find_from_initializer =
|
|
match Pvar.get_initializer_pname pvar with
|
|
| Some callee_pname -> (
|
|
match get_summary callee_pname with
|
|
| Some callee_mem ->
|
|
let v = find_from_initializer callee_mem in
|
|
Dom.Mem.add_stack (Loc.of_id id) v mem
|
|
| None ->
|
|
L.d_printfln_escaped "/!\\ Unknown initializer of global constant %a" (Pvar.pp Pp.text)
|
|
pvar ;
|
|
Dom.Mem.add_unknown_from ret ~callee_pname ~location mem )
|
|
| None ->
|
|
L.d_printfln_escaped "/!\\ Failed to get initializer name of global constant %a"
|
|
(Pvar.pp Pp.text) pvar ;
|
|
Dom.Mem.add_unknown ret ~location mem
|
|
|
|
|
|
let exec_instr : Dom.Mem.t -> extras ProcData.t -> CFG.Node.t -> Sil.instr -> Dom.Mem.t =
|
|
fun mem {summary; tenv; extras= {get_summary; get_formals; oenv= {integer_type_widths}}} node
|
|
instr ->
|
|
match instr with
|
|
| Load {id} when Ident.is_none id ->
|
|
mem
|
|
| Load {id; e= Exp.Lvar pvar; typ; loc= location}
|
|
when Pvar.is_compile_constant pvar || Pvar.is_ice pvar ->
|
|
load_global_constant get_summary (id, typ) pvar location mem
|
|
~find_from_initializer:(fun callee_mem -> Dom.Mem.find (Loc.of_pvar pvar) callee_mem)
|
|
| Load {id; e= Exp.Lindex (Exp.Lvar pvar, _); typ; loc= location}
|
|
when Pvar.is_compile_constant pvar || Pvar.is_ice pvar ->
|
|
load_global_constant get_summary (id, typ) pvar location mem
|
|
~find_from_initializer:(fun callee_mem ->
|
|
let locs = Dom.Mem.find (Loc.of_pvar pvar) callee_mem |> Dom.Val.get_all_locs in
|
|
Dom.Mem.find_set locs callee_mem )
|
|
| Load {id; e= exp; typ; loc= location} -> (
|
|
let model_env =
|
|
let pname = Summary.get_proc_name summary in
|
|
let node_hash = CFG.Node.hash node in
|
|
BoUtils.ModelEnv.mk_model_env pname ~node_hash location tenv integer_type_widths
|
|
in
|
|
match modeled_load_of_empty_collection_opt exp model_env (id, typ) mem with
|
|
| Some mem' ->
|
|
mem'
|
|
| None ->
|
|
let mem =
|
|
if Language.curr_language_is Java then join_java_static_final tenv get_summary exp mem
|
|
else mem
|
|
in
|
|
let represents_multiple_values = is_array_access_exp exp in
|
|
let modeled_range = modeled_range_of_exp location exp mem in
|
|
BoUtils.Exec.load_locs ~represents_multiple_values ~modeled_range id typ
|
|
(Sem.eval_locs exp mem) mem )
|
|
| Store {e2= Exn _} when Language.curr_language_is Java ->
|
|
Dom.Mem.exc_raised
|
|
| Store {e1= tgt_exp; e2= Const (Const.Cstr _) as src; loc= location}
|
|
when Language.curr_language_is Java ->
|
|
let pname = Summary.get_proc_name summary in
|
|
let node_hash = CFG.Node.hash node in
|
|
let model_env =
|
|
BoUtils.ModelEnv.mk_model_env pname ~node_hash location tenv integer_type_widths
|
|
in
|
|
let tgt_locs = Sem.eval_locs tgt_exp mem in
|
|
let tgt_deref =
|
|
let allocsite =
|
|
Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None
|
|
~represents_multiple_values:false
|
|
in
|
|
PowLoc.singleton (Loc.of_allocsite allocsite)
|
|
in
|
|
Dom.Mem.update_mem tgt_locs (Dom.Val.of_pow_loc ~traces:Trace.Set.bottom tgt_deref) mem
|
|
|> Models.JavaString.constructor_from_char_ptr model_env tgt_deref src
|
|
| Store {e1= exp1; e2= Const (Const.Cstr s); loc= location} ->
|
|
let locs = Sem.eval_locs exp1 mem in
|
|
let model_env =
|
|
let pname = Summary.get_proc_name summary in
|
|
let node_hash = CFG.Node.hash node in
|
|
BoUtils.ModelEnv.mk_model_env pname ~node_hash location tenv integer_type_widths
|
|
in
|
|
let do_alloc = not (Sem.is_stack_exp exp1 mem) in
|
|
BoUtils.Exec.decl_string model_env ~do_alloc locs s mem
|
|
| Store {e1= exp1; typ; e2= exp2; loc= location} ->
|
|
let locs = Sem.eval_locs exp1 mem in
|
|
let v =
|
|
Sem.eval integer_type_widths exp2 mem |> Dom.Val.add_assign_trace_elem location locs
|
|
in
|
|
let mem = Dom.Mem.update_mem locs v mem in
|
|
let mem =
|
|
if Language.curr_language_is Clang && Typ.is_char typ then
|
|
BoUtils.Exec.set_c_strlen ~tgt:(Sem.eval integer_type_widths exp1 mem) ~src:v mem
|
|
else mem
|
|
in
|
|
let mem =
|
|
match PowLoc.is_singleton_or_more locs with
|
|
| IContainer.Singleton loc_v ->
|
|
Dom.Mem.store_simple_alias loc_v exp2 mem
|
|
| _ ->
|
|
mem
|
|
in
|
|
let mem = Dom.Mem.update_latest_prune ~updated_locs:locs exp1 exp2 mem in
|
|
mem
|
|
| Prune (exp, _, _, _) ->
|
|
Sem.Prune.prune integer_type_widths exp mem
|
|
| Call ((id, _), Const (Cfun callee_pname), _, _, _) when is_java_enum_values tenv callee_pname
|
|
->
|
|
let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in
|
|
assign_java_enum_values get_summary id ~caller_pname:(Summary.get_proc_name summary)
|
|
~callee_pname mem
|
|
| Call (((id, _) as ret), Const (Cfun callee_pname), params, location, _) -> (
|
|
let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in
|
|
let fun_arg_list =
|
|
List.map params ~f:(fun (exp, typ) ->
|
|
ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} )
|
|
in
|
|
match Models.Call.dispatch tenv callee_pname fun_arg_list with
|
|
| Some {Models.exec} ->
|
|
let model_env =
|
|
let node_hash = CFG.Node.hash node in
|
|
BoUtils.ModelEnv.mk_model_env callee_pname ~node_hash location tenv
|
|
integer_type_widths
|
|
in
|
|
exec model_env ~ret mem
|
|
| None -> (
|
|
let {BoUtils.ReplaceCallee.pname= callee_pname; params; is_params_ref} =
|
|
BoUtils.ReplaceCallee.replace_make_shared tenv get_formals callee_pname params
|
|
in
|
|
match (get_summary callee_pname, get_formals callee_pname) with
|
|
| Some callee_exit_mem, Some callee_formals ->
|
|
instantiate_mem ~is_params_ref integer_type_widths ret callee_formals callee_pname
|
|
params mem callee_exit_mem location
|
|
| _, _ ->
|
|
(* This may happen for procedures with a biabduction model too. *)
|
|
L.d_printfln_escaped "/!\\ Unknown call to %a" Procname.pp callee_pname ;
|
|
Dom.Mem.add_unknown_from ret ~callee_pname ~location mem ) )
|
|
| Call (((id, _) as ret), fun_exp, _, location, _) ->
|
|
let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in
|
|
L.d_printfln_escaped "/!\\ Call to non-const function %a" Exp.pp fun_exp ;
|
|
Dom.Mem.add_unknown ret ~location mem
|
|
| Metadata (VariableLifetimeBegins (pvar, typ, location)) when Pvar.is_global pvar ->
|
|
let model_env =
|
|
let pname = Summary.get_proc_name summary in
|
|
let node_hash = CFG.Node.hash node in
|
|
BoUtils.ModelEnv.mk_model_env pname ~node_hash location tenv integer_type_widths
|
|
in
|
|
let mem, _ = BoUtils.Exec.decl_local model_env (mem, 1) (Loc.of_pvar pvar, typ) in
|
|
mem
|
|
| Metadata (ExitScope (dead_vars, _)) ->
|
|
Dom.Mem.remove_temps (List.filter_map dead_vars ~f:Var.get_ident) mem
|
|
| Metadata (Abstract _ | Nullify _ | Skip | VariableLifetimeBegins _) ->
|
|
mem
|
|
|
|
|
|
let pp_session_name node fmt = F.fprintf fmt "bufferoverrun %a" CFG.Node.pp_id (CFG.Node.id node)
|
|
end
|
|
|
|
module Analyzer = AbstractInterpreter.MakeWTO (TransferFunctions)
|
|
|
|
type invariant_map = Analyzer.invariant_map
|
|
|
|
type memory_summary = BufferOverrunAnalysisSummary.t
|
|
|
|
let extract_pre = Analyzer.extract_pre
|
|
|
|
let extract_post = Analyzer.extract_post
|
|
|
|
let extract_state = Analyzer.extract_state
|
|
|
|
let compute_invariant_map :
|
|
Summary.t
|
|
-> Tenv.t
|
|
-> Typ.IntegerWidths.t
|
|
-> BufferOverrunAnalysisSummary.get_summary
|
|
-> BoUtils.get_formals
|
|
-> invariant_map =
|
|
fun summary tenv integer_type_widths get_summary get_formals ->
|
|
let pdesc = Summary.get_proc_desc summary in
|
|
let cfg = CFG.from_pdesc pdesc in
|
|
let pdata =
|
|
let oenv = OndemandEnv.mk pdesc tenv integer_type_widths in
|
|
ProcData.make summary tenv {get_summary; get_formals; oenv}
|
|
in
|
|
let initial = Init.initial_state pdata (CFG.start_node cfg) in
|
|
Analyzer.exec_pdesc ~do_narrowing:true ~initial pdata
|
|
|
|
|
|
let cached_compute_invariant_map =
|
|
let cache_get, cache_set = Procname.UnitCache.create () in
|
|
fun summary tenv integer_type_widths ->
|
|
let pname = Summary.get_proc_name summary in
|
|
match cache_get pname with
|
|
| Some inv_map ->
|
|
inv_map
|
|
| None ->
|
|
let get_summary callee_pname = Payload.read ~caller_summary:summary ~callee_pname in
|
|
let get_formals callee_pname =
|
|
Ondemand.get_proc_desc callee_pname |> Option.map ~f:Procdesc.get_pvar_formals
|
|
in
|
|
let inv_map =
|
|
compute_invariant_map summary tenv integer_type_widths get_summary get_formals
|
|
in
|
|
cache_set pname inv_map ; inv_map
|
|
|
|
|
|
let compute_summary : (Pvar.t * Typ.t) list -> CFG.t -> invariant_map -> memory_summary =
|
|
fun formals cfg inv_map ->
|
|
let exit_node_id = CFG.exit_node cfg |> CFG.Node.id in
|
|
match extract_post exit_node_id inv_map with
|
|
| Some exit_mem ->
|
|
exit_mem |> Dom.Mem.forget_unreachable_locs ~formals |> Dom.Mem.unset_oenv
|
|
| None ->
|
|
Unreachable
|
|
|
|
|
|
let do_analysis : Callbacks.proc_callback_args -> Summary.t =
|
|
fun {exe_env; summary} ->
|
|
let proc_desc = Summary.get_proc_desc summary in
|
|
let proc_name = Summary.get_proc_name summary in
|
|
let tenv = Exe_env.get_tenv exe_env proc_name in
|
|
let integer_type_widths = Exe_env.get_integer_type_widths exe_env proc_name in
|
|
let inv_map = cached_compute_invariant_map summary tenv integer_type_widths in
|
|
let formals = Procdesc.get_pvar_formals proc_desc in
|
|
let cfg = CFG.from_pdesc proc_desc in
|
|
let payload = compute_summary formals cfg inv_map in
|
|
Payload.update_summary payload summary
|