From 76d4563f8c17721f8db0abfc4129aa7d5ba720b3 Mon Sep 17 00:00:00 2001 From: Sungkeun Cho Date: Fri, 26 Mar 2021 08:27:41 -0700 Subject: [PATCH] [ConfigImpact] Collect all object fields that may have config values Summary: This diff add semantics for collecting all object fields that may have config values. The collected information is used to instantiate conditional unchecked callees introduced in the previous diff. How it works: * The summary is extended to have `config_fields:Fields.t`. It has all fields that may have config values intra-procedurally. * Before reporting to `config-impact-report.json`, it unions all `config_fields` from all specs. * Using `all_config_fields`, it instantiates each summaries and writes results to `config-impact-report.json`. Reviewed By: ezgicicek Differential Revision: D27326306 fbshipit-source-id: 42f16ca45 --- infer/src/checkers/ConfigImpactAnalysis.ml | 56 ++++++++++++++------- infer/src/checkers/ConfigImpactAnalysis.mli | 4 +- infer/src/integration/JsonReports.ml | 30 +++++++---- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/infer/src/checkers/ConfigImpactAnalysis.ml b/infer/src/checkers/ConfigImpactAnalysis.ml index 280f57836..dbffa3ae6 100644 --- a/infer/src/checkers/ConfigImpactAnalysis.ml +++ b/infer/src/checkers/ConfigImpactAnalysis.ml @@ -166,23 +166,28 @@ module Summary = struct { unchecked_callees: UncheckedCallees.t (** Set of unchecked callees *) ; unchecked_callees_cond: UncheckedCalleesCond.t (** Sets of unchecked callees that are called conditionally by object fields *) - ; has_call_stmt: bool (** True if a function includes a call statement *) } + ; has_call_stmt: bool (** True if a function includes a call statement *) + ; config_fields: Fields.t + (** Intra-procedurally collected fields that may have config values *) } - let pp f {unchecked_callees; unchecked_callees_cond; has_call_stmt} = + let pp f {unchecked_callees; unchecked_callees_cond; has_call_stmt; config_fields} = F.fprintf f - "@[@[unchecked callees:@,%a@],@ @[unchecked callees cond:@,%a@],@ @[has_call_stmt:%b@]@]" - UncheckedCallees.pp unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond - has_call_stmt + "@[@[unchecked callees:@,\ + %a@],@ @[unchecked callees cond:@,\ + %a@],@ @[has_call_stmt:%b@],@ @[config fields:%a@]@]" UncheckedCallees.pp unchecked_callees + UncheckedCalleesCond.pp unchecked_callees_cond has_call_stmt Fields.pp config_fields + let get_config_fields {config_fields} = config_fields + let get_unchecked_callees {unchecked_callees} = unchecked_callees - let instantiate_unchecked_callees_cond ~config_fields + let instantiate_unchecked_callees_cond ~all_config_fields ({unchecked_callees; unchecked_callees_cond} as x) = let unchecked_callees = UncheckedCalleesCond.fold (fun fields callees acc -> - if Fields.is_empty (Fields.inter config_fields fields) then + if Fields.is_empty (Fields.inter all_config_fields fields) then UncheckedCallees.union acc callees else acc ) unchecked_callees_cond unchecked_callees @@ -196,17 +201,22 @@ module Dom = struct ; field_checks: FieldChecks.t ; unchecked_callees: UncheckedCallees.t ; unchecked_callees_cond: UncheckedCalleesCond.t - ; mem: Mem.t } + ; mem: Mem.t + ; config_fields: Fields.t } - let pp f {config_checks; field_checks; unchecked_callees; unchecked_callees_cond; mem} = + let pp f + {config_checks; field_checks; unchecked_callees; unchecked_callees_cond; mem; config_fields} = F.fprintf f "@[@[config checks:@,\ %a@]@ @[field checks:@,\ %a@]@ @[unchecked callees:@,\ %a@]@ @[unchecked callees cond:@,\ - %a@]@ @[mem:%,%a@]@]" ConfigChecks.pp config_checks FieldChecks.pp field_checks - UncheckedCallees.pp unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond Mem.pp - mem + %a@]@ @[mem:@,\ + %a@]@ @[config fields:@,\ + %a@]@]" + ConfigChecks.pp config_checks FieldChecks.pp field_checks UncheckedCallees.pp + unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond Mem.pp mem Fields.pp + config_fields let leq ~lhs ~rhs = @@ -215,6 +225,7 @@ module Dom = struct && UncheckedCallees.leq ~lhs:lhs.unchecked_callees ~rhs:rhs.unchecked_callees && UncheckedCalleesCond.leq ~lhs:lhs.unchecked_callees_cond ~rhs:rhs.unchecked_callees_cond && Mem.leq ~lhs:lhs.mem ~rhs:rhs.mem + && Fields.leq ~lhs:lhs.config_fields ~rhs:rhs.config_fields let join x y = @@ -223,7 +234,8 @@ module Dom = struct ; unchecked_callees= UncheckedCallees.join x.unchecked_callees y.unchecked_callees ; unchecked_callees_cond= UncheckedCalleesCond.join x.unchecked_callees_cond y.unchecked_callees_cond - ; mem= Mem.join x.mem y.mem } + ; mem= Mem.join x.mem y.mem + ; config_fields= Fields.join x.config_fields y.config_fields } let widen ~prev ~next ~num_iters = @@ -234,11 +246,12 @@ module Dom = struct ; unchecked_callees_cond= UncheckedCalleesCond.widen ~prev:prev.unchecked_callees_cond ~next:next.unchecked_callees_cond ~num_iters - ; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters } + ; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters + ; config_fields= Fields.widen ~prev:prev.config_fields ~next:next.config_fields ~num_iters } - let to_summary has_call_stmt {unchecked_callees; unchecked_callees_cond} = - {Summary.unchecked_callees; unchecked_callees_cond; has_call_stmt} + let to_summary has_call_stmt {unchecked_callees; unchecked_callees_cond; config_fields} = + {Summary.unchecked_callees; unchecked_callees_cond; has_call_stmt; config_fields} let init = @@ -246,7 +259,8 @@ module Dom = struct ; field_checks= FieldChecks.top ; unchecked_callees= UncheckedCallees.bottom ; unchecked_callees_cond= UncheckedCalleesCond.bottom - ; mem= Mem.bottom } + ; mem= Mem.bottom + ; config_fields= Fields.bottom } let add_mem loc v ({mem} as astate) = {astate with mem= Mem.add loc v mem} @@ -261,6 +275,12 @@ module Dom = struct let store_config pvar id astate = copy_mem ~tgt:(Loc.of_pvar pvar) ~src:(Loc.of_id id) astate + let store_field fn id ({mem; config_fields} as astate) = + let {Val.config} = Mem.lookup (Loc.of_id id) mem in + if ConfigLifted.is_top config then astate + else {astate with config_fields= Fields.add fn config_fields} + + let boolean_value id_tgt id_src astate = copy_mem ~tgt:(Loc.of_id id_tgt) ~src:(Loc.of_id id_src) astate @@ -371,6 +391,8 @@ module TransferFunctions = struct Dom.load_field id fn astate | Store {e1= Lvar pvar; e2= Var id} -> Dom.store_config pvar id astate + | Store {e1= Lfield (_, fn, _); e2= Var id} -> + Dom.store_field fn id astate | Call ((ret, _), Const (Cfun callee), [(Var id, _)], _, _) when is_java_boolean_value_method callee -> Dom.boolean_value ret id astate diff --git a/infer/src/checkers/ConfigImpactAnalysis.mli b/infer/src/checkers/ConfigImpactAnalysis.mli index 76a8d7d27..f9250a61d 100644 --- a/infer/src/checkers/ConfigImpactAnalysis.mli +++ b/infer/src/checkers/ConfigImpactAnalysis.mli @@ -32,9 +32,11 @@ module Summary : sig val pp : Format.formatter -> t -> unit + val get_config_fields : t -> Fields.t + val get_unchecked_callees : t -> UncheckedCallees.t - val instantiate_unchecked_callees_cond : config_fields:Fields.t -> t -> t + val instantiate_unchecked_callees_cond : all_config_fields:Fields.t -> t -> t end val checker : Summary.t InterproceduralAnalysis.t -> Summary.t option diff --git a/infer/src/integration/JsonReports.ml b/infer/src/integration/JsonReports.ml index ab114b599..66655d3d2 100644 --- a/infer/src/integration/JsonReports.ml +++ b/infer/src/integration/JsonReports.ml @@ -343,15 +343,26 @@ let write_costs proc_name loc cost_opt (outfile : Utils.outfile) = JsonCostsPrinter.pp outfile.fmt {loc; proc_name; cost_opt} -let write_config_impact proc_name loc config_impact_opt (outfile : Utils.outfile) = +let get_all_config_fields () = + lazy + (let all_config_fields = ref ConfigImpactAnalysis.Fields.empty in + Summary.OnDisk.iter_specs ~f:(fun summary -> + Payloads.config_impact_analysis summary.payloads + |> Option.iter ~f:(fun summary -> + all_config_fields := + ConfigImpactAnalysis.Fields.union !all_config_fields + (ConfigImpactAnalysis.Summary.get_config_fields summary) ) ) ; + !all_config_fields ) + + +let write_config_impact all_config_fields proc_name loc config_impact_opt (outfile : Utils.outfile) + = if ExternalConfigImpactData.is_in_config_data_file proc_name then - let config_fields = - (* TODO: This value should be non-empty by an additional analysis. *) - ConfigImpactAnalysis.Fields.empty - in let config_impact_opt = Option.map config_impact_opt - ~f:(ConfigImpactAnalysis.Summary.instantiate_unchecked_callees_cond ~config_fields) + ~f: + (ConfigImpactAnalysis.Summary.instantiate_unchecked_callees_cond + ~all_config_fields:(Lazy.force all_config_fields)) in JsonConfigImpactPrinter.pp outfile.fmt {loc; proc_name; config_impact_opt} @@ -363,9 +374,9 @@ let write_lint_issues filters (issues_outf : Utils.outfile) linereader procname let process_summary proc_name loc ~cost:(cost_opt, costs_outf) - ~config_impact:(config_impact_opt, config_impact_outf) err_log issues_acc = + ~config_impact:(config_impact_opt, config_impact_outf, all_config_fields) err_log issues_acc = write_costs proc_name loc cost_opt costs_outf ; - write_config_impact proc_name loc config_impact_opt config_impact_outf ; + write_config_impact all_config_fields proc_name loc config_impact_opt config_impact_outf ; collect_issues proc_name loc err_log issues_acc @@ -373,11 +384,12 @@ let process_all_summaries_and_issues ~issues_outf ~costs_outf ~config_impact_out let linereader = LineReader.create () in let filters = Inferconfig.create_filters () in let all_issues = ref [] in + let all_config_fields = get_all_config_fields () in Summary.OnDisk.iter_report_summaries_from_config ~f:(fun proc_name loc cost_opt config_impact_opt err_log -> all_issues := process_summary proc_name loc ~cost:(cost_opt, costs_outf) - ~config_impact:(config_impact_opt, config_impact_outf) + ~config_impact:(config_impact_opt, config_impact_outf, all_config_fields) err_log !all_issues ) ; all_issues := Issue.sort_filter_issues !all_issues ; List.iter