Summary: This checker is for internal usage. Reviewed By: ezgicicek Differential Revision: D26453750 fbshipit-source-id: a9397f2e4master
parent
c6a253b875
commit
7e6654cd25
@ -0,0 +1,239 @@
|
|||||||
|
(*
|
||||||
|
* 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
|
||||||
|
module F = Format
|
||||||
|
module ConfigName = FbGKInteraction.ConfigName
|
||||||
|
|
||||||
|
module Branch = struct
|
||||||
|
type t = True | False | Top
|
||||||
|
|
||||||
|
let pp f = function
|
||||||
|
| True ->
|
||||||
|
F.pp_print_string f "true branch"
|
||||||
|
| False ->
|
||||||
|
F.pp_print_string f "false branch"
|
||||||
|
| Top ->
|
||||||
|
AbstractDomain.TopLiftedUtils.pp_top f
|
||||||
|
|
||||||
|
|
||||||
|
let leq ~lhs ~rhs =
|
||||||
|
match (lhs, rhs) with True, True | False, False | _, Top -> true | _, _ -> false
|
||||||
|
|
||||||
|
|
||||||
|
let join x y = match (x, y) with True, True -> True | False, False -> False | _, _ -> Top
|
||||||
|
|
||||||
|
let widen ~prev ~next ~num_iters:_ = join prev next
|
||||||
|
|
||||||
|
let top = Top
|
||||||
|
|
||||||
|
let is_top = function Top -> true | True | False -> false
|
||||||
|
|
||||||
|
let neg = function True -> False | False -> True | Top -> Top
|
||||||
|
end
|
||||||
|
|
||||||
|
module ConfigChecks = AbstractDomain.SafeInvertedMap (ConfigName) (Branch)
|
||||||
|
|
||||||
|
module UncheckedCallee = struct
|
||||||
|
type t = {callee: Procname.t; location: Location.t} [@@deriving compare]
|
||||||
|
|
||||||
|
let pp f {callee; location} =
|
||||||
|
F.fprintf f "%a is called at %a" Procname.pp callee Location.pp location
|
||||||
|
|
||||||
|
|
||||||
|
let get_location {location} = location
|
||||||
|
|
||||||
|
let report {InterproceduralAnalysis.proc_desc; err_log} x =
|
||||||
|
let desc = F.asprintf "%a without config check" pp x in
|
||||||
|
let trace =
|
||||||
|
(* TODO *)
|
||||||
|
[]
|
||||||
|
in
|
||||||
|
Reporting.log_issue proc_desc err_log ~loc:(get_location x) ~ltr:trace ConfigImpactAnalysis
|
||||||
|
IssueType.config_impact_analysis desc
|
||||||
|
end
|
||||||
|
|
||||||
|
module UncheckedCallees = struct
|
||||||
|
include AbstractDomain.FiniteSet (UncheckedCallee)
|
||||||
|
|
||||||
|
let report analysis_data unchecked_callees =
|
||||||
|
iter (UncheckedCallee.report analysis_data) unchecked_callees
|
||||||
|
end
|
||||||
|
|
||||||
|
module Loc = struct
|
||||||
|
type t = Ident of Ident.t | Pvar of Pvar.t [@@deriving compare]
|
||||||
|
|
||||||
|
let pp f = function Ident id -> Ident.pp f id | Pvar pvar -> Pvar.pp Pp.text f pvar
|
||||||
|
|
||||||
|
let of_id id = Ident id
|
||||||
|
|
||||||
|
let of_pvar pvar = Pvar pvar
|
||||||
|
end
|
||||||
|
|
||||||
|
module ConfigLifted = AbstractDomain.Flat (ConfigName)
|
||||||
|
|
||||||
|
module Val = struct
|
||||||
|
type t = {config: ConfigLifted.t}
|
||||||
|
|
||||||
|
let pp f {config} = F.fprintf f "@[config:@,%a@]" ConfigLifted.pp config
|
||||||
|
|
||||||
|
let leq ~lhs ~rhs = ConfigLifted.leq ~lhs:lhs.config ~rhs:rhs.config
|
||||||
|
|
||||||
|
let join x y = {config= ConfigLifted.join x.config y.config}
|
||||||
|
|
||||||
|
let widen ~prev ~next ~num_iters =
|
||||||
|
{config= ConfigLifted.widen ~prev:prev.config ~next:next.config ~num_iters}
|
||||||
|
|
||||||
|
|
||||||
|
let bottom = {config= ConfigLifted.bottom}
|
||||||
|
|
||||||
|
let of_config config = {config= ConfigLifted.v config}
|
||||||
|
|
||||||
|
let get_config_opt {config} = ConfigLifted.get config
|
||||||
|
end
|
||||||
|
|
||||||
|
module Mem = struct
|
||||||
|
include AbstractDomain.Map (Loc) (Val)
|
||||||
|
|
||||||
|
let lookup loc mem = find_opt loc mem |> Option.value ~default:Val.bottom
|
||||||
|
end
|
||||||
|
|
||||||
|
module Summary = struct
|
||||||
|
type t = {unchecked_callees: UncheckedCallees.t}
|
||||||
|
|
||||||
|
let pp f {unchecked_callees} =
|
||||||
|
F.fprintf f "@[unchecked callees:@,%a@]" UncheckedCallees.pp unchecked_callees
|
||||||
|
end
|
||||||
|
|
||||||
|
module Dom = struct
|
||||||
|
type t = {config_checks: ConfigChecks.t; unchecked_callees: UncheckedCallees.t; mem: Mem.t}
|
||||||
|
|
||||||
|
let pp f {config_checks; unchecked_callees; mem} =
|
||||||
|
F.fprintf f "@[@[config checks:@,%a@]@ @[unchecked callees:@,%a@]@ @[mem:%,%a@]@]"
|
||||||
|
ConfigChecks.pp config_checks UncheckedCallees.pp unchecked_callees Mem.pp mem
|
||||||
|
|
||||||
|
|
||||||
|
let leq ~lhs ~rhs =
|
||||||
|
ConfigChecks.leq ~lhs:lhs.config_checks ~rhs:rhs.config_checks
|
||||||
|
&& UncheckedCallees.leq ~lhs:lhs.unchecked_callees ~rhs:rhs.unchecked_callees
|
||||||
|
&& Mem.leq ~lhs:lhs.mem ~rhs:rhs.mem
|
||||||
|
|
||||||
|
|
||||||
|
let join x y =
|
||||||
|
{ config_checks= ConfigChecks.join x.config_checks y.config_checks
|
||||||
|
; unchecked_callees= UncheckedCallees.join x.unchecked_callees y.unchecked_callees
|
||||||
|
; mem= Mem.join x.mem y.mem }
|
||||||
|
|
||||||
|
|
||||||
|
let widen ~prev ~next ~num_iters =
|
||||||
|
{ config_checks= ConfigChecks.widen ~prev:prev.config_checks ~next:next.config_checks ~num_iters
|
||||||
|
; unchecked_callees=
|
||||||
|
UncheckedCallees.widen ~prev:prev.unchecked_callees ~next:next.unchecked_callees ~num_iters
|
||||||
|
; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters }
|
||||||
|
|
||||||
|
|
||||||
|
let to_summary {unchecked_callees} = {Summary.unchecked_callees}
|
||||||
|
|
||||||
|
let init =
|
||||||
|
{config_checks= ConfigChecks.top; unchecked_callees= UncheckedCallees.bottom; mem= Mem.bottom}
|
||||||
|
|
||||||
|
|
||||||
|
let add_mem loc v ({mem} as astate) = {astate with mem= Mem.add loc v mem}
|
||||||
|
|
||||||
|
let copy_mem ~tgt ~src ({mem} as astate) = add_mem tgt (Mem.lookup src mem) astate
|
||||||
|
|
||||||
|
let call_config_check ret config astate = add_mem (Loc.of_id ret) (Val.of_config config) astate
|
||||||
|
|
||||||
|
let load_config id pvar astate = copy_mem ~tgt:(Loc.of_id id) ~src:(Loc.of_pvar pvar) astate
|
||||||
|
|
||||||
|
let store_config pvar id astate = copy_mem ~tgt:(Loc.of_pvar pvar) ~src:(Loc.of_id id) astate
|
||||||
|
|
||||||
|
let boolean_value id_tgt id_src astate =
|
||||||
|
copy_mem ~tgt:(Loc.of_id id_tgt) ~src:(Loc.of_id id_src) astate
|
||||||
|
|
||||||
|
|
||||||
|
let neg_branch res = Option.map ~f:(fun (config, branch) -> (config, Branch.neg branch)) res
|
||||||
|
|
||||||
|
let rec get_config_check_prune e mem =
|
||||||
|
match (e : Exp.t) with
|
||||||
|
| Var id ->
|
||||||
|
Mem.lookup (Loc.of_id id) mem
|
||||||
|
|> Val.get_config_opt
|
||||||
|
|> Option.map ~f:(fun config -> (config, Branch.True))
|
||||||
|
| UnOp (LNot, e, _) ->
|
||||||
|
get_config_check_prune e mem |> neg_branch
|
||||||
|
| BinOp ((Eq | Ne), Const _, Const _) ->
|
||||||
|
None
|
||||||
|
| (BinOp (Eq, e, (Const _ as const)) | BinOp (Eq, (Const _ as const), e)) when Exp.is_zero const
|
||||||
|
->
|
||||||
|
get_config_check_prune e mem |> neg_branch
|
||||||
|
| (BinOp (Ne, e, (Const _ as const)) | BinOp (Ne, (Const _ as const), e)) when Exp.is_zero const
|
||||||
|
->
|
||||||
|
get_config_check_prune e mem
|
||||||
|
| _ ->
|
||||||
|
None
|
||||||
|
|
||||||
|
|
||||||
|
let prune e ({config_checks; mem} as astate) =
|
||||||
|
get_config_check_prune e mem
|
||||||
|
|> Option.value_map ~default:astate ~f:(fun (config, branch) ->
|
||||||
|
{astate with config_checks= ConfigChecks.add config branch config_checks} )
|
||||||
|
|
||||||
|
|
||||||
|
let call callee location ({config_checks; unchecked_callees} as astate) =
|
||||||
|
if ConfigChecks.is_top config_checks then
|
||||||
|
let unchecked_callee = UncheckedCallee.{callee; location} in
|
||||||
|
{astate with unchecked_callees= UncheckedCallees.add unchecked_callee unchecked_callees}
|
||||||
|
else astate
|
||||||
|
end
|
||||||
|
|
||||||
|
module TransferFunctions = struct
|
||||||
|
module CFG = ProcCfg.NormalOneInstrPerNode
|
||||||
|
module Domain = Dom
|
||||||
|
|
||||||
|
type analysis_data = Summary.t InterproceduralAnalysis.t
|
||||||
|
|
||||||
|
let is_java_boolean_value_method pname =
|
||||||
|
Procname.get_class_name pname |> Option.exists ~f:(String.equal "java.lang.Boolean")
|
||||||
|
&& Procname.get_method pname |> String.equal "booleanValue"
|
||||||
|
|
||||||
|
|
||||||
|
let exec_instr astate {InterproceduralAnalysis.tenv} _node instr =
|
||||||
|
match (instr : Sil.instr) with
|
||||||
|
| Load {id; e= Lvar pvar} ->
|
||||||
|
Dom.load_config id pvar astate
|
||||||
|
| Store {e1= Lvar pvar; e2= Var id} ->
|
||||||
|
Dom.store_config pvar id astate
|
||||||
|
| Call ((ret, _), Const (Cfun callee), [(Var id, _)], _, _)
|
||||||
|
when is_java_boolean_value_method callee ->
|
||||||
|
Dom.boolean_value ret id astate
|
||||||
|
| Call ((ret, _), Const (Cfun callee), args, location, _) -> (
|
||||||
|
match FbGKInteraction.get_config_check tenv callee args with
|
||||||
|
| Some (`Config config) ->
|
||||||
|
Dom.call_config_check ret config astate
|
||||||
|
| Some (`Exp _) ->
|
||||||
|
astate
|
||||||
|
| None ->
|
||||||
|
(* normal function calls *)
|
||||||
|
Dom.call callee location astate )
|
||||||
|
| Prune (e, _, _, _) ->
|
||||||
|
Dom.prune e astate
|
||||||
|
| _ ->
|
||||||
|
astate
|
||||||
|
|
||||||
|
|
||||||
|
let pp_session_name node fmt =
|
||||||
|
F.fprintf fmt "Config impact function calls %a" CFG.Node.pp_id (CFG.Node.id node)
|
||||||
|
end
|
||||||
|
|
||||||
|
module Analyzer = AbstractInterpreter.MakeWTO (TransferFunctions)
|
||||||
|
|
||||||
|
let checker ({InterproceduralAnalysis.proc_desc} as analysis_data) =
|
||||||
|
Option.map (Analyzer.compute_post analysis_data ~initial:Dom.init proc_desc)
|
||||||
|
~f:(fun ({Dom.unchecked_callees} as astate) ->
|
||||||
|
UncheckedCallees.report analysis_data unchecked_callees ;
|
||||||
|
Dom.to_summary astate )
|
@ -0,0 +1,16 @@
|
|||||||
|
(*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
module Summary : sig
|
||||||
|
type t
|
||||||
|
|
||||||
|
val pp : Format.formatter -> t -> unit
|
||||||
|
end
|
||||||
|
|
||||||
|
val checker : Summary.t InterproceduralAnalysis.t -> Summary.t option
|
@ -0,0 +1,13 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
TESTS_DIR = ../../..
|
||||||
|
|
||||||
|
INFER_OPTIONS = --config-impact-analysis-only --debug-exceptions \
|
||||||
|
--report-force-relative-path
|
||||||
|
INFERPRINT_OPTIONS = --issues-tests
|
||||||
|
SOURCES = $(wildcard *.java)
|
||||||
|
|
||||||
|
include $(TESTS_DIR)/javac.make
|
Loading…
Reference in new issue