From aae741419c069bd6c4c269e47d5034b5b7e0cd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Thu, 22 Apr 2021 02:38:07 -0700 Subject: [PATCH] [ConfigImpact] Ignore unknown library calls with no cost model Summary: Looking at the recent silent analysis results, it seems that we report many direct unknown library calls (often cheap)... However, if these were called inside some other callee we wouldn't report them because their costs would be assumed to be constant by the cost analysis. This is a bit awkward. We should either report all unknown calls or suppress them altogether. Since we have too many reports per day and not a good way to determine whether an unknown library call is cheap/expensive, let's take option 2. Then, we would be only relying on two things to determine whether to report/not: - instantiated cost's degree > 1 - explicitly known to be expensive (i.e. modeled in ConfigImpact, like string append) Reviewed By: skcho Differential Revision: D27909003 fbshipit-source-id: 0391d226d --- infer/src/cost/ConfigImpactAnalysis.ml | 22 +++++++++++-------- infer/src/cost/costInstantiate.ml | 30 ++++++++++---------------- infer/src/cost/costInstantiate.mli | 4 +++- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/infer/src/cost/ConfigImpactAnalysis.ml b/infer/src/cost/ConfigImpactAnalysis.ml index 81cd57f9c..bb89019bb 100644 --- a/infer/src/cost/ConfigImpactAnalysis.ml +++ b/infer/src/cost/ConfigImpactAnalysis.ml @@ -379,7 +379,8 @@ module Dom = struct dispatch tenv pname args - let call tenv analyze_dependency ~is_cheap_call callee args location + let call tenv analyze_dependency ~(instantiated_cost : CostInstantiate.instantiated_cost) callee + args location ({config_checks; field_checks; unchecked_callees; unchecked_callees_cond} as astate) = let join_unchecked_callees new_unchecked_callees new_unchecked_callees_cond = if FieldChecks.is_top field_checks then @@ -413,6 +414,8 @@ module Dom = struct let has_expensive_callee = Option.exists callee_summary ~f:Summary.has_known_expensive_callee in + let is_cheap_call = match instantiated_cost with Cheap -> true | _ -> false in + let is_unmodeled_call = match instantiated_cost with NoModel -> true | _ -> false in match expensiveness_model with | None when is_cheap_call && not has_expensive_callee -> (* If callee is cheap by heuristics, ignore it. *) @@ -446,8 +449,8 @@ module Dom = struct join_unchecked_callees (UncheckedCallees.replace_location_by_call location callee_summary) (UncheckedCalleesCond.replace_location_by_call location callee_summary_cond) - | None when Procname.is_objc_init callee -> - (* If callee is unknown ObjC initializer, ignore it. *) + | None when Procname.is_objc_init callee || is_unmodeled_call -> + (* If callee is unknown ObjC initializer or has no cost model, ignore it. *) astate | _ -> (* Otherwise, add callee's name. *) @@ -462,7 +465,7 @@ type analysis_data = { interproc: (BufferOverrunAnalysisSummary.t option * Summary.t option * CostDomain.summary option) InterproceduralAnalysis.t - ; get_is_cheap_call: CostInstantiate.Call.t -> bool } + ; get_instantiated_cost: CostInstantiate.Call.t -> CostInstantiate.instantiated_cost } module TransferFunctions = struct module CFG = ProcCfg.Normal @@ -514,7 +517,8 @@ module TransferFunctions = struct fun tenv pname -> dispatch tenv pname |> Option.is_some - let exec_instr astate {interproc= {tenv; analyze_dependency}; get_is_cheap_call} node idx instr = + let exec_instr astate {interproc= {tenv; analyze_dependency}; get_instantiated_cost} node idx + instr = match (instr : Sil.instr) with | Load {id; e= Lvar pvar} -> Dom.load_config id pvar astate @@ -541,8 +545,8 @@ module TransferFunctions = struct CostInstantiate.Call. {loc= location; pname= callee; node= CFG.Node.to_instr idx node; args; ret} in - let is_cheap_call = get_is_cheap_call call in - Dom.call tenv analyze_dependency ~is_cheap_call callee args location astate ) + let instantiated_cost = get_instantiated_cost call in + Dom.call tenv analyze_dependency ~instantiated_cost callee args location astate ) | Prune (e, _, _, _) -> Dom.prune e astate | _ -> @@ -564,8 +568,8 @@ let has_call_stmt proc_desc = let checker ({InterproceduralAnalysis.proc_desc} as analysis_data) = - let get_is_cheap_call = CostInstantiate.get_is_cheap_call analysis_data in - let analysis_data = {interproc= analysis_data; get_is_cheap_call} in + let get_instantiated_cost = CostInstantiate.get_instantiated_cost analysis_data in + let analysis_data = {interproc= analysis_data; get_instantiated_cost} in Option.map (Analyzer.compute_post analysis_data ~initial:Dom.init proc_desc) ~f:(fun astate -> let has_call_stmt = has_call_stmt proc_desc in Dom.to_summary ~has_call_stmt astate ) diff --git a/infer/src/cost/costInstantiate.ml b/infer/src/cost/costInstantiate.ml index 0a2372fae..66258f868 100644 --- a/infer/src/cost/costInstantiate.ml +++ b/infer/src/cost/costInstantiate.ml @@ -32,7 +32,9 @@ type cost_args = type 'a interproc_analysis = (BufferOverrunAnalysisSummary.t option * 'a * CostDomain.summary option) InterproceduralAnalysis.t -let get_symbolic_cost +type instantiated_cost = Cheap | NoModel | Symbolic of CostDomain.BasicCost.t + +let get_instantiated_cost { tenv ; integer_type_widths ; get_callee_cost_summary_and_formals @@ -44,9 +46,7 @@ let get_symbolic_cost (BufferOverrunAnalysis.extract_pre (ProcCfg.InstrNode.id node) inferbo_invariant_map) in let loc = ProcCfg.InstrNode.loc node in - let get_symbolic cost = - if CostDomain.BasicCost.is_symbolic cost then `SymbolicCost cost else `Cheap - in + let get_symbolic cost = if CostDomain.BasicCost.is_symbolic cost then Symbolic cost else Cheap in let get_summary pname = Option.map ~f:fst (get_callee_cost_summary_and_formals pname) in match get_callee_cost_summary_and_formals pname with | Some (CostDomain.{post= cost_record}, callee_formals) -> @@ -56,14 +56,14 @@ let get_symbolic_cost ~inferbo_caller_mem:inferbo_mem ~callee_pname:pname ~callee_formals ~args ~callee_cost ~loc) .cost |> get_symbolic - else `Cheap + else Cheap | None -> let fun_arg_list = List.map args ~f:(fun (exp, typ) -> ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) in CostModels.Call.dispatch tenv pname fun_arg_list - |> Option.value_map ~default:`NoModel ~f:(fun model -> + |> Option.value_map ~default:NoModel ~f:(fun model -> let model_env = let node_hash = ProcCfg.InstrNode.hash node in BufferOverrunUtils.ModelEnv.mk_model_env pname ~node_hash loc tenv @@ -102,20 +102,12 @@ let prepare_call_args let get_cost_if_expensive analysis_data call = - match prepare_call_args analysis_data call |> get_symbolic_cost with - | `SymbolicCost cost -> + match prepare_call_args analysis_data call |> get_instantiated_cost with + | Symbolic cost -> Some cost - | `Cheap | `NoModel -> + | Cheap | NoModel -> None -let get_is_cheap_call analysis_data call = - match prepare_call_args analysis_data call |> get_symbolic_cost with - | `SymbolicCost _ -> - (* symbolic costs (e.g. 4n+5 or log(n) are considered expensive) *) - false - | `Cheap -> - true - | `NoModel -> - (* unmodeled calls are considered expensive *) - false +let get_instantiated_cost analysis_data call = + prepare_call_args analysis_data call |> get_instantiated_cost diff --git a/infer/src/cost/costInstantiate.mli b/infer/src/cost/costInstantiate.mli index f87ee2b9e..582a1f0df 100644 --- a/infer/src/cost/costInstantiate.mli +++ b/infer/src/cost/costInstantiate.mli @@ -22,6 +22,8 @@ end type 'a interproc_analysis = (BufferOverrunAnalysisSummary.t option * 'a * CostDomain.summary option) InterproceduralAnalysis.t +type instantiated_cost = Cheap | NoModel | Symbolic of CostDomain.BasicCost.t + val get_cost_if_expensive : 'a interproc_analysis -> Call.t -> CostDomain.BasicCost.t option -val get_is_cheap_call : 'a interproc_analysis -> Call.t -> bool +val get_instantiated_cost : 'a interproc_analysis -> Call.t -> instantiated_cost