Introduce method SummaryPayload.read_toplevel_procedure

Summary:
Cluster checkers call `SummaryPayload.read` but set the `caller_summary` to correspond to the same summary as gives the `callee_pname`

This change introduces a new method `read_toplevel_procedure` that does not require a `caller_summary`, to be used by the cluster checkers

Reviewed By: ngorogiannis

Differential Revision: D16131660

fbshipit-source-id: 12caa1000
master
Phoebe Nichols 6 years ago committed by Facebook Github Bot
parent b5b8259ea7
commit d9535f42d5

@ -20,9 +20,12 @@ module type S = sig
val of_summary : Summary.t -> t option val of_summary : Summary.t -> t option
val read_full : Procdesc.t -> Typ.Procname.t -> (Procdesc.t * t) option val read_full :
caller_summary:Summary.t -> callee_pname:Typ.Procname.t -> (Procdesc.t * t) option
val read : Procdesc.t -> Typ.Procname.t -> t option val read : caller_summary:Summary.t -> callee_pname:Typ.Procname.t -> t option
val read_toplevel_procedure : Typ.Procname.t -> t option
end end
module Make (P : Payload) : S with type t = P.t = struct module Make (P : Payload) : S with type t = P.t = struct
@ -38,16 +41,21 @@ module Make (P : Payload) : S with type t = P.t = struct
let of_summary (summary : Summary.t) = of_payloads summary.payloads let of_summary (summary : Summary.t) = of_payloads summary.payloads
let read_full caller_pdesc callee_pname = let read_all ?caller_summary ~callee_pname =
let open Option.Monad_infix in let open Option.Monad_infix in
Ondemand.analyze_proc_name ~caller_pdesc callee_pname Ondemand.analyze_proc_name
?caller_pdesc:(Option.map ~f:Summary.get_proc_desc caller_summary)
callee_pname
>>= fun summary -> >>= fun summary ->
of_summary summary of_summary summary >>| fun payload -> (Summary.get_proc_desc summary, payload)
>>| fun payload ->
(* we could return the proc_desc if some client needed this but this would complicate the return
type so for now let's not do that *) let read_full ~caller_summary ~callee_pname = read_all ~caller_summary ~callee_pname
(Summary.get_proc_desc summary, payload)
let read ~caller_summary ~callee_pname =
read_all ~caller_summary ~callee_pname |> Option.map ~f:snd
let read caller_pdesc callee_pname = read_full caller_pdesc callee_pname |> Option.map ~f:snd let read_toplevel_procedure callee_pname =
read_all ?caller_summary:None ~callee_pname |> Option.map ~f:snd
end end

@ -22,13 +22,15 @@ module type S = sig
val of_summary : Summary.t -> t option val of_summary : Summary.t -> t option
(** Read the corresponding part of the payload from the procedure summary *) (** Read the corresponding part of the payload from the procedure summary *)
val read_full : Procdesc.t -> Typ.Procname.t -> (Procdesc.t * t) option val read_full :
[@@warning "-32"] caller_summary:Summary.t -> callee_pname:Typ.Procname.t -> (Procdesc.t * t) option
(** Return the proc desc and payload for the given procedure. Runs the analysis on-demand if (** Return the proc desc and payload for the given procedure. Runs the analysis on-demand if
necessary. *) necessary. *)
val read : Procdesc.t -> Typ.Procname.t -> t option val read : caller_summary:Summary.t -> callee_pname:Typ.Procname.t -> t option
(** Return the payload for the given procedure. Runs the analysis on-demand if necessary. *) (** Return the payload for the given procedure. Runs the analysis on-demand if necessary. *)
val read_toplevel_procedure : Typ.Procname.t -> t option
end end
module Make (P : Payload) : S with type t = P.t module Make (P : Payload) : S with type t = P.t

@ -19,7 +19,7 @@ type proc_callback_args =
type proc_callback_t = proc_callback_args -> Summary.t type proc_callback_t = proc_callback_args -> Summary.t
type cluster_callback_args = type cluster_callback_args =
{procedures: (Tenv.t * Procdesc.t) list; source_file: SourceFile.t; exe_env: Exe_env.t} {procedures: (Tenv.t * Summary.t) list; source_file: SourceFile.t; exe_env: Exe_env.t}
type cluster_callback_t = cluster_callback_args -> unit type cluster_callback_t = cluster_callback_args -> unit
@ -46,7 +46,7 @@ let get_procedure_definition exe_env proc_name =
Procdesc.load proc_name Procdesc.load proc_name
|> Option.map ~f:(fun proc_desc -> |> Option.map ~f:(fun proc_desc ->
let tenv = Exe_env.get_tenv exe_env proc_name in let tenv = Exe_env.get_tenv exe_env proc_name in
(tenv, proc_desc) ) (tenv, Summary.reset proc_desc) )
(** Invoke all registered procedure callbacks on the given procedure. *) (** Invoke all registered procedure callbacks on the given procedure. *)
@ -91,8 +91,8 @@ let iterate_cluster_callbacks all_procs exe_env source_file =
let environment = {procedures; source_file; exe_env} in let environment = {procedures; source_file; exe_env} in
let language_matches language = let language_matches language =
match procedures with match procedures with
| (_, pdesc) :: _ -> | (_, summary) :: _ ->
Language.equal language (Typ.Procname.get_language (Procdesc.get_proc_name pdesc)) Language.equal language (Typ.Procname.get_language (Summary.get_proc_name summary))
| _ -> | _ ->
true true
in in

@ -24,7 +24,7 @@ type proc_callback_args =
type proc_callback_t = proc_callback_args -> Summary.t type proc_callback_t = proc_callback_args -> Summary.t
type cluster_callback_args = type cluster_callback_args =
{procedures: (Tenv.t * Procdesc.t) list; source_file: SourceFile.t; exe_env: Exe_env.t} {procedures: (Tenv.t * Summary.t) list; source_file: SourceFile.t; exe_env: Exe_env.t}
type cluster_callback_t = cluster_callback_args -> unit type cluster_callback_t = cluster_callback_args -> unit

@ -216,7 +216,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
, _ , _
, _ ) -> , _ ) ->
let domain_summary = let domain_summary =
Payload.read (Summary.get_proc_desc proc_data.summary) callee_procname Payload.read ~caller_summary:proc_data.summary ~callee_pname:callee_procname
in in
let receiver = let receiver =
Domain.LocalAccessPath.make Domain.LocalAccessPath.make
@ -247,7 +247,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(* treat it like a normal call *) (* treat it like a normal call *)
apply_callee_summary domain_summary caller_pname return_base actuals astate apply_callee_summary domain_summary caller_pname return_base actuals astate
| Call (ret_id_typ, Direct callee_procname, actuals, _, _) -> | Call (ret_id_typ, Direct callee_procname, actuals, _, _) ->
let summary = Payload.read (Summary.get_proc_desc proc_data.summary) callee_procname in let summary =
Payload.read ~caller_summary:proc_data.summary ~callee_pname:callee_procname
in
apply_callee_summary summary caller_pname ret_id_typ actuals astate apply_callee_summary summary caller_pname ret_id_typ actuals astate
| Assign (lhs_ae, HilExp.AccessExpression rhs_ae, _) -> ( | Assign (lhs_ae, HilExp.AccessExpression rhs_ae, _) -> (
(* creating an alias for the rhs binding; assume all reads will now occur through the (* creating an alias for the rhs binding; assume all reads will now occur through the

@ -69,9 +69,12 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
type extras = ProcData.no_extras type extras = ProcData.no_extras
let is_compile_time_constructed pdesc pv = let is_compile_time_constructed summary pv =
let init_pname = Pvar.get_initializer_pname pv in let init_pname = Pvar.get_initializer_pname pv in
match Option.bind init_pname ~f:(Payload.read pdesc) with match
Option.bind init_pname ~f:(fun callee_pname ->
Payload.read ~caller_summary:summary ~callee_pname )
with
| Some (Bottom, _) -> | Some (Bottom, _) ->
(* we analyzed the initializer for this global and found that it doesn't require any runtime (* we analyzed the initializer for this global and found that it doesn't require any runtime
initialization so cannot participate in SIOF *) initialization so cannot participate in SIOF *)
@ -94,13 +97,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Staged.unstage (filter_global_accesses (Domain.VarNames.of_list always_initialized)) Staged.unstage (filter_global_accesses (Domain.VarNames.of_list always_initialized))
let get_globals pdesc e = let get_globals summary e =
let is_dangerous_global pv = let is_dangerous_global pv =
Pvar.is_global pv Pvar.is_global pv
&& (not (Pvar.is_static_local pv)) && (not (Pvar.is_static_local pv))
&& (not (Pvar.is_pod pv)) && (not (Pvar.is_pod pv))
&& (not (Pvar.is_compile_constant pv)) && (not (Pvar.is_compile_constant pv))
&& (not (is_compile_time_constructed pdesc pv)) && (not (is_compile_time_constructed summary pv))
&& is_not_always_initialized pv && is_not_always_initialized pv
in in
Exp.program_vars e Exp.program_vars e
@ -127,20 +130,19 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(NonBottom trace_with_non_init_globals, snd astate) (NonBottom trace_with_non_init_globals, snd astate)
let add_actuals_globals astate0 pdesc call_loc actuals = let add_actuals_globals astate0 summary call_loc actuals =
List.fold_left actuals ~init:astate0 ~f:(fun astate (e, _) -> List.fold_left actuals ~init:astate0 ~f:(fun astate (e, _) ->
get_globals pdesc e |> add_globals astate call_loc ) get_globals summary e |> add_globals astate call_loc )
let at_least_nonbottom = Domain.join (NonBottom SiofTrace.bottom, Domain.VarNames.empty) let at_least_nonbottom = Domain.join (NonBottom SiofTrace.bottom, Domain.VarNames.empty)
let exec_instr astate {ProcData.summary} _ (instr : Sil.instr) = let exec_instr astate {ProcData.summary} _ (instr : Sil.instr) =
let pdesc = Summary.get_proc_desc summary in
match instr with match instr with
| Store (Lvar global, Typ.{desc= Tptr _}, Lvar _, loc) | Store (Lvar global, Typ.{desc= Tptr _}, Lvar _, loc)
when (Option.equal Typ.Procname.equal) when (Option.equal Typ.Procname.equal)
(Pvar.get_initializer_pname global) (Pvar.get_initializer_pname global)
(Some (Procdesc.get_proc_name pdesc)) -> (Some (Summary.get_proc_name summary)) ->
(* if we are just taking the reference of another global then we are not really accessing (* if we are just taking the reference of another global then we are not really accessing
it. This is a dumb heuristic as something also might take that result and then it. This is a dumb heuristic as something also might take that result and then
dereference it, thus requiring the target object to be initialized. Solving this would dereference it, thus requiring the target object to be initialized. Solving this would
@ -152,7 +154,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Load (_, exp, _, loc) (* dereference -> add all the dangerous variables *) | Load (_, exp, _, loc) (* dereference -> add all the dangerous variables *)
| Store (_, _, exp, loc) (* except in the case above, consider all reads as dangerous *) | Store (_, _, exp, loc) (* except in the case above, consider all reads as dangerous *)
| Prune (exp, loc, _, _) -> | Prune (exp, loc, _, _) ->
get_globals pdesc exp |> add_globals astate loc get_globals summary exp |> add_globals astate loc
| Call (_, Const (Cfun callee_pname), _, _, _) when is_whitelisted callee_pname -> | Call (_, Const (Cfun callee_pname), _, _, _) when is_whitelisted callee_pname ->
at_least_nonbottom astate at_least_nonbottom astate
| Call (_, Const (Cfun callee_pname), _, _, _) when is_modelled callee_pname -> | Call (_, Const (Cfun callee_pname), _, _, _) when is_modelled callee_pname ->
@ -169,10 +171,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Call (_, Const (Cfun (ObjC_Cpp cpp_pname as callee_pname)), _ :: actuals_without_self, loc, _) | Call (_, Const (Cfun (ObjC_Cpp cpp_pname as callee_pname)), _ :: actuals_without_self, loc, _)
when Typ.Procname.is_constructor callee_pname && Typ.Procname.ObjC_Cpp.is_constexpr cpp_pname when Typ.Procname.is_constructor callee_pname && Typ.Procname.ObjC_Cpp.is_constexpr cpp_pname
-> ->
add_actuals_globals astate pdesc loc actuals_without_self add_actuals_globals astate summary loc actuals_without_self
| Call (_, Const (Cfun callee_pname), actuals, loc, _) -> | Call (_, Const (Cfun callee_pname), actuals, loc, _) ->
let callee_astate = let callee_astate =
match Payload.read pdesc callee_pname with match Payload.read ~caller_summary:summary ~callee_pname with
| Some (NonBottom trace, initialized_by_callee) -> | Some (NonBottom trace, initialized_by_callee) ->
let already_initialized = snd astate in let already_initialized = snd astate in
let dangerous_accesses = let dangerous_accesses =
@ -193,12 +195,12 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| None -> | None ->
(Bottom, Domain.VarNames.empty) (Bottom, Domain.VarNames.empty)
in in
add_actuals_globals astate pdesc loc actuals add_actuals_globals astate summary loc actuals
|> Domain.join callee_astate |> Domain.join callee_astate
|> (* make sure it's not Bottom: we made a function call so this needs initialization *) |> (* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_nonbottom at_least_nonbottom
| Call (_, _, actuals, loc, _) -> | Call (_, _, actuals, loc, _) ->
add_actuals_globals astate pdesc loc actuals add_actuals_globals astate summary loc actuals
|> (* make sure it's not Bottom: we made a function call so this needs initialization *) |> (* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_nonbottom at_least_nonbottom
| Metadata _ -> | Metadata _ ->
@ -218,9 +220,9 @@ let is_foreign current_tu v =
true true
let report_siof summary trace pdesc gname loc = let report_siof summary trace gname loc =
let trace_of_pname pname = let trace_of_pname pname =
match Payload.read pdesc pname with match Payload.read ~caller_summary:summary ~callee_pname:pname with
| Some (NonBottom summary, _) -> | Some (NonBottom summary, _) ->
summary summary
| _ -> | _ ->
@ -244,7 +246,8 @@ let report_siof summary trace pdesc gname loc =
else List.iter ~f:report_one_path reportable_paths else List.iter ~f:report_one_path reportable_paths
let siof_check pdesc gname (summary : Summary.t) = let siof_check gname (summary : Summary.t) =
let pdesc = Summary.get_proc_desc summary in
match summary.payloads.siof with match summary.payloads.siof with
| Some (NonBottom post, _) -> | Some (NonBottom post, _) ->
let attrs = Procdesc.get_attributes pdesc in let attrs = Procdesc.get_attributes pdesc in
@ -260,7 +263,7 @@ let siof_check pdesc gname (summary : Summary.t) =
if not (SiofTrace.Sinks.is_empty foreign_sinks) then if not (SiofTrace.Sinks.is_empty foreign_sinks) then
report_siof summary report_siof summary
(SiofTrace.update_sinks post foreign_sinks) (SiofTrace.update_sinks post foreign_sinks)
pdesc gname attrs.ProcAttributes.loc gname attrs.ProcAttributes.loc
| Some (Bottom, _) | None -> | Some (Bottom, _) | None ->
() ()
@ -310,7 +313,7 @@ let checker {Callbacks.tenv; summary; get_procs_in_file} : Summary.t =
in in
( match Typ.Procname.get_global_name_of_initializer pname with ( match Typ.Procname.get_global_name_of_initializer pname with
| Some gname -> | Some gname ->
siof_check proc_desc gname updated_summary siof_check gname updated_summary
| None -> | None ->
() ) ; () ) ;
updated_summary updated_summary

@ -567,15 +567,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
specs specs
let merge_callee_map call_site pdesc callee_pname tenv specs astate = let merge_callee_map call_site summary callee_pname tenv specs astate =
match Payload.read pdesc callee_pname with match Payload.read ~caller_summary:summary ~callee_pname with
| None -> | None ->
astate astate
| Some callee_call_map -> | Some callee_call_map ->
let add_call_site annot sink calls astate = let add_call_site annot sink calls astate =
if AnnotReachabilityDomain.CallSites.is_empty calls then astate if AnnotReachabilityDomain.CallSites.is_empty calls then astate
else else
let pname = Procdesc.get_proc_name pdesc in let pname = Summary.get_proc_name summary in
List.fold List.fold
~f:(fun astate (spec : AnnotationSpec.t) -> ~f:(fun astate (spec : AnnotationSpec.t) ->
if spec.sanitizer_predicate tenv pname then astate if spec.sanitizer_predicate tenv pname then astate
@ -592,11 +592,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Sil.Call ((id, _), Const (Cfun callee_pname), _, _, _) when is_unlikely callee_pname -> | Sil.Call ((id, _), Const (Cfun callee_pname), _, _, _) when is_unlikely callee_pname ->
Domain.add_tracking_var (Var.of_id id) astate Domain.add_tracking_var (Var.of_id id) astate
| Sil.Call (_, Const (Cfun callee_pname), _, call_loc, _) -> | Sil.Call (_, Const (Cfun callee_pname), _, call_loc, _) ->
let pdesc = Summary.get_proc_desc summary in let caller_pname = Summary.get_proc_name summary in
let caller_pname = Procdesc.get_proc_name pdesc in
let call_site = CallSite.make callee_pname call_loc in let call_site = CallSite.make callee_pname call_loc in
check_call tenv callee_pname caller_pname call_site astate extras check_call tenv callee_pname caller_pname call_site astate extras
|> merge_callee_map call_site pdesc callee_pname tenv extras |> merge_callee_map call_site summary callee_pname tenv extras
| Sil.Load (id, exp, _, _) when is_tracking_exp astate exp -> | Sil.Load (id, exp, _, _) when is_tracking_exp astate exp ->
Domain.add_tracking_var (Var.of_id id) astate Domain.add_tracking_var (Var.of_id id) astate
| Sil.Store (Exp.Lvar pvar, _, exp, _) when is_tracking_exp astate exp -> | Sil.Store (Exp.Lvar pvar, _, exp, _) when is_tracking_exp astate exp ->

@ -22,12 +22,13 @@ module Payload = SummaryPayload.Make (struct
let field = Payloads.Fields.class_loads let field = Payloads.Fields.class_loads
end) end)
let do_call pdesc callee loc init = let do_call summary callee loc init =
Payload.read pdesc callee |> Option.fold ~init ~f:(ClassLoadsDomain.integrate_summary callee loc) Payload.read ~caller_summary:summary ~callee_pname:callee
|> Option.fold ~init ~f:(ClassLoadsDomain.integrate_summary callee loc)
(** fully load a class given the typename *) (** fully load a class given the typename *)
let rec load_class proc_desc tenv loc astate class_name = let rec load_class summary tenv loc astate class_name =
(* don't bother if class is already loaded *) (* don't bother if class is already loaded *)
if ClassLoadsDomain.mem_typename class_name astate then astate if ClassLoadsDomain.mem_typename class_name astate then astate
else else
@ -37,78 +38,78 @@ let rec load_class proc_desc tenv loc astate class_name =
let astate2 = let astate2 =
let class_initializer = Typ.Procname.(Java (Java.get_class_initializer class_name)) in let class_initializer = Typ.Procname.(Java (Java.get_class_initializer class_name)) in
(* NB may recurse if we are in class init but the shortcircuiting above makes it a no-op *) (* NB may recurse if we are in class init but the shortcircuiting above makes it a no-op *)
do_call proc_desc class_initializer loc astate1 do_call summary class_initializer loc astate1
in in
(* finally, recursively load all superclasses *) (* finally, recursively load all superclasses *)
Tenv.lookup tenv class_name Tenv.lookup tenv class_name
|> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Typ.Struct.supers) |> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Typ.Struct.supers)
|> List.fold ~init:astate2 ~f:(load_class proc_desc tenv loc) |> List.fold ~init:astate2 ~f:(load_class summary tenv loc)
let load_type proc_desc tenv loc (typ : Typ.t) astate = let load_type summary tenv loc (typ : Typ.t) astate =
match typ with match typ with
| {desc= Tstruct name} | {desc= Tptr ({desc= Tstruct name}, _)} -> | {desc= Tstruct name} | {desc= Tptr ({desc= Tstruct name}, _)} ->
load_class proc_desc tenv loc astate name load_class summary tenv loc astate name
| _ -> | _ ->
astate astate
let rec load_array proc_desc tenv loc (typ : Typ.t) astate = let rec load_array summary tenv loc (typ : Typ.t) astate =
match typ with match typ with
| {desc= Tarray {elt}} -> | {desc= Tarray {elt}} ->
load_array proc_desc tenv loc elt astate load_array summary tenv loc elt astate
| _ -> | _ ->
load_type proc_desc tenv loc typ astate load_type summary tenv loc typ astate
let rec add_loads_of_exp proc_desc tenv loc (exp : Exp.t) astate = let rec add_loads_of_exp summary tenv loc (exp : Exp.t) astate =
match exp with match exp with
| Const (Cclass class_ident) -> | Const (Cclass class_ident) ->
(* [X.class] expressions *) (* [X.class] expressions *)
let class_str = Ident.name_to_string class_ident |> Mangled.from_string in let class_str = Ident.name_to_string class_ident |> Mangled.from_string in
let class_name = Typ.JavaClass class_str in let class_name = Typ.JavaClass class_str in
load_class proc_desc tenv loc astate class_name load_class summary tenv loc astate class_name
| Sizeof {typ= {desc= Tarray {elt}}} -> | Sizeof {typ= {desc= Tarray {elt}}} ->
(* anewarray / multinewarray *) (* anewarray / multinewarray *)
load_array proc_desc tenv loc elt astate load_array summary tenv loc elt astate
| Cast (_, e) | UnOp (_, e, _) | Exn e -> | Cast (_, e) | UnOp (_, e, _) | Exn e ->
(* NB Cast is only used for primitive types *) (* NB Cast is only used for primitive types *)
add_loads_of_exp proc_desc tenv loc e astate add_loads_of_exp summary tenv loc e astate
| BinOp (_, e1, e2) -> | BinOp (_, e1, e2) ->
add_loads_of_exp proc_desc tenv loc e1 astate |> add_loads_of_exp proc_desc tenv loc e2 add_loads_of_exp summary tenv loc e1 astate |> add_loads_of_exp summary tenv loc e2
| Lfield (e, _, typ') -> | Lfield (e, _, typ') ->
(* getfield / getstatic / putfield / putstatic *) (* getfield / getstatic / putfield / putstatic *)
load_type proc_desc tenv loc typ' astate |> add_loads_of_exp proc_desc tenv loc e load_type summary tenv loc typ' astate |> add_loads_of_exp summary tenv loc e
| Var _ | Const _ | Closure _ | Sizeof _ | Lindex _ | Lvar _ -> | Var _ | Const _ | Closure _ | Sizeof _ | Lindex _ | Lvar _ ->
astate astate
let exec_call pdesc tenv callee args loc astate = let exec_call summary tenv callee args loc astate =
match args with match args with
| [_; (Exp.Sizeof {typ}, _)] when Typ.Procname.equal callee BuiltinDecl.__instanceof -> | [_; (Exp.Sizeof {typ}, _)] when Typ.Procname.equal callee BuiltinDecl.__instanceof ->
(* this matches downcasts/instanceof and exception handlers *) (* this matches downcasts/instanceof and exception handlers *)
load_type pdesc tenv loc typ astate load_type summary tenv loc typ astate
| _ -> | _ ->
(* invokeinterface / invokespecial / invokestatic / invokevirtual / new *) (* invokeinterface / invokespecial / invokestatic / invokevirtual / new *)
List.fold args ~init:astate ~f:(fun acc (exp, _) -> add_loads_of_exp pdesc tenv loc exp acc) List.fold args ~init:astate ~f:(fun acc (exp, _) -> add_loads_of_exp summary tenv loc exp acc)
|> do_call pdesc callee loc |> do_call summary callee loc
let exec_instr pdesc tenv astate _ (instr : Sil.instr) = let exec_instr summary tenv astate _ (instr : Sil.instr) =
match instr with match instr with
| Call (_, Const (Cfun callee), args, loc, _) -> | Call (_, Const (Cfun callee), args, loc, _) ->
exec_call pdesc tenv callee args loc astate exec_call summary tenv callee args loc astate
| Load (_, exp, _, loc) | Prune (exp, loc, _, _) -> | Load (_, exp, _, loc) | Prune (exp, loc, _, _) ->
(* NB the java frontend seems to always translate complex guards into a sequence of (* NB the java frontend seems to always translate complex guards into a sequence of
instructions plus a prune on logical vars only. So the below is only for completeness. *) instructions plus a prune on logical vars only. So the below is only for completeness. *)
add_loads_of_exp pdesc tenv loc exp astate add_loads_of_exp summary tenv loc exp astate
| Store (e1, _, e2, loc) -> | Store (e1, _, e2, loc) ->
add_loads_of_exp pdesc tenv loc e1 astate |> add_loads_of_exp pdesc tenv loc e2 add_loads_of_exp summary tenv loc e1 astate |> add_loads_of_exp summary tenv loc e2
| _ -> | _ ->
astate astate
let report_loads proc_desc summary astate = let report_loads summary astate =
let report_load ({ClassLoadsDomain.Event.loc; elem} as event) = let report_load ({ClassLoadsDomain.Event.loc; elem} as event) =
if String.is_prefix ~prefix:"java." elem then () if String.is_prefix ~prefix:"java." elem then ()
else else
@ -116,7 +117,7 @@ let report_loads proc_desc summary astate =
let msg = Format.asprintf "Class %s loaded" elem in let msg = Format.asprintf "Class %s loaded" elem in
Reporting.log_warning summary ~loc ~ltr IssueType.class_load msg Reporting.log_warning summary ~loc ~ltr IssueType.class_load msg
in in
let pname = Procdesc.get_proc_name proc_desc in let pname = Summary.get_proc_name summary in
Typ.Procname.get_class_name pname Typ.Procname.get_class_name pname
|> Option.iter ~f:(fun clazz -> |> Option.iter ~f:(fun clazz ->
let method_strname = Typ.Procname.get_method pname in let method_strname = Typ.Procname.get_method pname in
@ -133,10 +134,10 @@ let analyze_procedure {Callbacks.tenv; summary} =
(* load the method's class *) (* load the method's class *)
let init = let init =
Typ.Procname.get_class_type_name proc_name Typ.Procname.get_class_type_name proc_name
|> Option.fold ~init:ClassLoadsDomain.bottom ~f:(load_class proc_desc tenv loc) |> Option.fold ~init:ClassLoadsDomain.bottom ~f:(load_class summary tenv loc)
in in
let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr proc_desc tenv) in let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr summary tenv) in
report_loads proc_desc summary post ; report_loads summary post ;
let result = Payload.update_summary post summary in let result = Payload.update_summary post summary in
L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Typ.Procname.pp proc_name ; L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Typ.Procname.pp proc_name ;
result result

@ -197,7 +197,8 @@ let should_report pdesc =
true true
let report_errors pdesc astate summary = let report_errors astate summary =
let pdesc = Summary.get_proc_desc summary in
let proc_name = Procdesc.get_proc_name pdesc in let proc_name = Procdesc.get_proc_name pdesc in
match astate with match astate with
| Some astate -> | Some astate ->
@ -224,13 +225,12 @@ let compute_summary summary tenv get_callee_summary inferbo_invariant_map =
let checker {Callbacks.tenv; summary; integer_type_widths} : Summary.t = let checker {Callbacks.tenv; summary; integer_type_widths} : Summary.t =
let proc_desc = Summary.get_proc_desc summary in
let inferbo_invariant_map = let inferbo_invariant_map =
BufferOverrunAnalysis.cached_compute_invariant_map summary tenv integer_type_widths BufferOverrunAnalysis.cached_compute_invariant_map summary tenv integer_type_widths
in in
let get_callee_summary = Payload.read proc_desc in let get_callee_summary callee_pname = Payload.read ~caller_summary:summary ~callee_pname in
let astate = compute_summary summary tenv get_callee_summary inferbo_invariant_map in let astate = compute_summary summary tenv get_callee_summary inferbo_invariant_map in
report_errors proc_desc astate summary ; report_errors astate summary ;
match astate with match astate with
| Some astate -> | Some astate ->
debug "Purity summary :%a \n" PurityDomain.pp astate ; debug "Purity summary :%a \n" PurityDomain.pp astate ;

@ -99,14 +99,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|| is_array_element_passed_by_ref callee_formals t access_expr idx || is_array_element_passed_by_ref callee_formals t access_expr idx
let report_on_function_params pdesc tenv maybe_uninit_vars actuals loc summary callee_formals_opt let report_on_function_params tenv maybe_uninit_vars actuals loc summary callee_formals_opt =
=
List.iteri actuals ~f:(fun idx e -> List.iteri actuals ~f:(fun idx e ->
match HilExp.ignore_cast e with match HilExp.ignore_cast e with
| HilExp.AccessExpression access_expr -> | HilExp.AccessExpression access_expr ->
let _, t = HilExp.AccessExpression.get_base access_expr in let _, t = HilExp.AccessExpression.get_base access_expr in
if if
should_report_var pdesc tenv maybe_uninit_vars access_expr should_report_var (Summary.get_proc_desc summary) tenv maybe_uninit_vars access_expr
&& (not (Typ.is_pointer t)) && (not (Typ.is_pointer t))
&& not && not
(Option.exists callee_formals_opt ~f:(fun callee_formals -> (Option.exists callee_formals_opt ~f:(fun callee_formals ->
@ -153,8 +152,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
else None else None
let remove_initialized_params pdesc call maybe_uninit_vars idx access_expr remove_fields = let remove_initialized_params summary call maybe_uninit_vars idx access_expr remove_fields =
match Payload.read pdesc call with match Payload.read ~caller_summary:summary ~callee_pname:call with
| Some {pre= init_formals; post= _} -> ( | Some {pre= init_formals; post= _} -> (
match init_nth_actual_param call idx init_formals with match init_nth_actual_param call idx init_formals with
| Some var_formal -> | Some var_formal ->
@ -171,8 +170,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(* true if a function initializes at least a param or a field of a struct param *) (* true if a function initializes at least a param or a field of a struct param *)
let function_initializes_some_formal_params pdesc call = let function_initializes_some_formal_params summary call =
match Payload.read pdesc call with match Payload.read ~caller_summary:summary ~callee_pname:call with
| Some {pre= initialized_formal_params; post= _} -> | Some {pre= initialized_formal_params; post= _} ->
not (D.is_empty initialized_formal_params) not (D.is_empty initialized_formal_params)
| _ -> | _ ->
@ -234,7 +233,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(* if it's a default constructor, we use the following heuristic: we assume that it initializes (* if it's a default constructor, we use the following heuristic: we assume that it initializes
correctly all fields when there is an implementation of the constructor that initilizes at least one correctly all fields when there is an implementation of the constructor that initilizes at least one
field. If there is no explicit implementation we cannot assume fields are initialized *) field. If there is no explicit implementation we cannot assume fields are initialized *)
if function_initializes_some_formal_params pdesc call then if function_initializes_some_formal_params summary call then
let maybe_uninit_vars = let maybe_uninit_vars =
(* in HIL/SIL the default constructor has only one param: the struct *) (* in HIL/SIL the default constructor has only one param: the struct *)
MaybeUninitVars.remove_all_fields tenv base astate.maybe_uninit_vars MaybeUninitVars.remove_all_fields tenv base astate.maybe_uninit_vars
@ -269,7 +268,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
~f:(is_fld_or_array_elem_passed_by_ref t access_expr idx) -> ( ~f:(is_fld_or_array_elem_passed_by_ref t access_expr idx) -> (
match pname_opt with match pname_opt with
| Some pname when Config.uninit_interproc -> | Some pname when Config.uninit_interproc ->
remove_initialized_params pdesc pname acc idx access_expr_to_remove false remove_initialized_params summary pname acc idx access_expr_to_remove false
| _ -> | _ ->
MaybeUninitVars.remove access_expr_to_remove acc ) MaybeUninitVars.remove access_expr_to_remove acc )
| base when Option.exists pname_opt ~f:Typ.Procname.is_constructor -> | base when Option.exists pname_opt ~f:Typ.Procname.is_constructor ->
@ -278,7 +277,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| _, {Typ.desc= Tptr _} -> ( | _, {Typ.desc= Tptr _} -> (
match pname_opt with match pname_opt with
| Some pname when Config.uninit_interproc -> | Some pname when Config.uninit_interproc ->
remove_initialized_params pdesc pname acc idx access_expr_to_remove true remove_initialized_params summary pname acc idx access_expr_to_remove true
| _ -> | _ ->
MaybeUninitVars.remove_everything_under tenv access_expr_to_remove acc ) MaybeUninitVars.remove_everything_under tenv access_expr_to_remove acc )
| _ -> | _ ->
@ -292,8 +291,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
( match call with ( match call with
| Direct _ -> | Direct _ ->
report_on_function_params pdesc tenv maybe_uninit_vars actuals loc summary report_on_function_params tenv maybe_uninit_vars actuals loc summary callee_formals_opt
callee_formals_opt
| Indirect _ -> | Indirect _ ->
() ) ; () ) ;
{astate with maybe_uninit_vars} {astate with maybe_uninit_vars}

@ -322,7 +322,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate astate
| NoEffect -> ( | NoEffect -> (
let rebased_summary_opt = let rebased_summary_opt =
Payload.read pdesc callee_pname Payload.read ~caller_summary:summary ~callee_pname
|> Option.map ~f:(fun summary -> |> Option.map ~f:(fun summary ->
let rebased_accesses = let rebased_accesses =
Ondemand.get_proc_desc callee_pname Ondemand.get_proc_desc callee_pname
@ -1170,15 +1170,16 @@ let make_results_table file_env =
(fun snapshot acc -> ReportMap.add {threads; snapshot; tenv; procdesc} acc) (fun snapshot acc -> ReportMap.add {threads; snapshot; tenv; procdesc} acc)
accesses acc accesses acc
in in
List.fold file_env ~init:ReportMap.empty ~f:(fun acc (tenv, proc_desc) -> List.fold file_env ~init:ReportMap.empty ~f:(fun acc (tenv, summary) ->
Procdesc.get_proc_name proc_desc |> Payload.read proc_desc Payload.read_toplevel_procedure (Summary.get_proc_name summary)
|> Option.fold ~init:acc ~f:(aggregate_post tenv proc_desc) ) |> Option.fold ~init:acc ~f:(aggregate_post tenv (Summary.get_proc_desc summary)) )
(* aggregate all of the procedures in the file env by their declaring (* aggregate all of the procedures in the file env by their declaring
class. this lets us analyze each class individually *) class. this lets us analyze each class individually *)
let aggregate_by_class file_env = let aggregate_by_class file_env =
List.fold file_env ~init:String.Map.empty ~f:(fun acc ((tenv, pdesc) as proc) -> List.fold file_env ~init:String.Map.empty ~f:(fun acc ((tenv, summary) as proc) ->
let pdesc = Summary.get_proc_desc summary in
if should_report_on_proc tenv pdesc then if should_report_on_proc tenv pdesc then
Procdesc.get_proc_name pdesc |> Typ.Procname.get_class_name Procdesc.get_proc_name pdesc |> Typ.Procname.get_class_name
|> Option.fold ~init:acc ~f:(fun acc classname -> |> Option.fold ~init:acc ~f:(fun acc classname ->

@ -81,7 +81,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
let do_unlock locks astate = List.filter_map ~f:get_lock_path locks |> Domain.release astate in let do_unlock locks astate = List.filter_map ~f:get_lock_path locks |> Domain.release astate in
let do_call callee loc astate = let do_call callee loc astate =
Payload.read (Summary.get_proc_desc summary) callee Payload.read ~caller_summary:summary ~callee_pname:callee
|> Option.value_map ~default:astate ~f:(Domain.integrate_summary tenv astate callee loc) |> Option.value_map ~default:astate ~f:(Domain.integrate_summary tenv astate callee loc)
in in
match instr with match instr with
@ -327,7 +327,7 @@ let should_report pdesc =
false false
let fold_reportable_summaries (tenv, current_pdesc) clazz ~init ~f = let fold_reportable_summaries (tenv, current_summary) clazz ~init ~f =
let methods = let methods =
Tenv.lookup tenv clazz Tenv.lookup tenv clazz
|> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Typ.Struct.methods) |> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Typ.Struct.methods)
@ -336,7 +336,7 @@ let fold_reportable_summaries (tenv, current_pdesc) clazz ~init ~f =
Ondemand.get_proc_desc mthd Ondemand.get_proc_desc mthd
|> Option.value_map ~default:acc ~f:(fun other_pdesc -> |> Option.value_map ~default:acc ~f:(fun other_pdesc ->
if should_report other_pdesc then if should_report other_pdesc then
Payload.read current_pdesc mthd Payload.read ~caller_summary:current_summary ~callee_pname:mthd
|> Option.map ~f:(fun payload -> (mthd, payload)) |> Option.map ~f:(fun payload -> (mthd, payload))
|> Option.fold ~init:acc ~f |> Option.fold ~init:acc ~f
else acc ) else acc )
@ -353,8 +353,8 @@ let fold_reportable_summaries (tenv, current_pdesc) clazz ~init ~f =
once, as opposed to twice with all other deadlock pairs. *) once, as opposed to twice with all other deadlock pairs. *)
let report_deadlocks env {StarvationDomain.order; ui} report_map' = let report_deadlocks env {StarvationDomain.order; ui} report_map' =
let open StarvationDomain in let open StarvationDomain in
let _, current_pdesc = env in let _, current_summary = env in
let current_pname = Procdesc.get_proc_name current_pdesc in let current_pname = Summary.get_proc_name current_summary in
let report_endpoint_elem current_elem endpoint_pname elem report_map = let report_endpoint_elem current_elem endpoint_pname elem report_map =
if if
not not
@ -409,8 +409,8 @@ let report_deadlocks env {StarvationDomain.order; ui} report_map' =
let report_starvation env {StarvationDomain.events; ui} report_map' = let report_starvation env {StarvationDomain.events; ui} report_map' =
let open StarvationDomain in let open StarvationDomain in
let _, current_pdesc = env in let _, current_summary = env in
let current_pname = Procdesc.get_proc_name current_pdesc in let current_pname = Summary.get_proc_name current_summary in
let report_remote_block ui_explain event current_lock endpoint_pname endpoint_elem report_map = let report_remote_block ui_explain event current_lock endpoint_pname endpoint_elem report_map =
let lock = endpoint_elem.Order.elem.first in let lock = endpoint_elem.Order.elem.first in
match endpoint_elem.Order.elem.eventually.elem with match endpoint_elem.Order.elem.eventually.elem with
@ -489,9 +489,10 @@ let report_starvation env {StarvationDomain.events; ui} report_map' =
let reporting {Callbacks.procedures; source_file} = let reporting {Callbacks.procedures; source_file} =
let report_procedure issue_log ((tenv, proc_desc) as env) = let report_procedure issue_log ((tenv, summary) as env) =
let proc_desc = Summary.get_proc_desc summary in
if should_report proc_desc then if should_report proc_desc then
Payload.read proc_desc (Procdesc.get_proc_name proc_desc) Payload.read_toplevel_procedure (Procdesc.get_proc_name proc_desc)
|> Option.value_map ~default:issue_log ~f:(fun summary -> |> Option.value_map ~default:issue_log ~f:(fun summary ->
report_deadlocks env summary ReportMap.empty report_deadlocks env summary ReportMap.empty
|> report_starvation env summary |> report_starvation env summary

@ -305,7 +305,7 @@ let remove_vars vars astate =
let call ~caller_summary call_loc callee_pname ~ret ~actuals astate = let call ~caller_summary call_loc callee_pname ~ret ~actuals astate =
match PulsePayload.read_full caller_summary.Summary.proc_desc callee_pname with match PulsePayload.read_full ~caller_summary ~callee_pname with
| Some (callee_proc_desc, preposts) -> | Some (callee_proc_desc, preposts) ->
let formals = let formals =
Procdesc.get_formals callee_proc_desc Procdesc.get_formals callee_proc_desc

@ -128,7 +128,7 @@ module Make (TaintSpecification : TaintSpec.S) = struct
(* read_summary will trigger ondemand analysis of the current proc. we don't want that. *) (* read_summary will trigger ondemand analysis of the current proc. we don't want that. *)
TaintDomain.bottom TaintDomain.bottom
else else
match Payload.read (Summary.get_proc_desc proc_data.summary) pname with match Payload.read ~caller_summary:proc_data.summary ~callee_pname:pname with
| Some summary -> | Some summary ->
TaintSpecification.of_summary_access_tree summary TaintSpecification.of_summary_access_tree summary
| None -> | None ->
@ -655,7 +655,7 @@ module Make (TaintSpecification : TaintSpec.S) = struct
add_actual_source source index actuals astate_with_sink proc_data ) add_actual_source source index actuals astate_with_sink proc_data )
in in
let astate_with_summary = let astate_with_summary =
match Payload.read (Summary.get_proc_desc proc_data.summary) callee_pname with match Payload.read ~caller_summary:proc_data.summary ~callee_pname with
| None -> | None ->
handle_unknown_call callee_pname astate_with_direct_sources handle_unknown_call callee_pname astate_with_direct_sources
| Some summary -> ( | Some summary -> (

Loading…
Cancel
Save