make ClassLoads an `interprocedural`

Summary: Making checkers/ its own dune library.

Reviewed By: ngorogiannis

Differential Revision: D21407066

fbshipit-source-id: 80744bcd4
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent 34ae47a1b3
commit 74497ea7df

@ -79,7 +79,9 @@ let all_checkers =
; callbacks= [(intraprocedural SelfInBlock.checker, Language.Clang)] } ; callbacks= [(intraprocedural SelfInBlock.checker, Language.Clang)] }
; { name= "Class loading analysis" ; { name= "Class loading analysis"
; active= Config.is_checker_enabled ClassLoads ; active= Config.is_checker_enabled ClassLoads
; callbacks= [(Procedure ClassLoads.analyze_procedure, Language.Java)] } ; callbacks=
[(interprocedural Payloads.Fields.class_loads ClassLoads.analyze_procedure, Language.Java)]
}
; { name= "purity" ; { name= "purity"
; active= Config.(is_checker_enabled Purity || is_checker_enabled LoopHoisting) ; active= Config.(is_checker_enabled Purity || is_checker_enabled LoopHoisting)
; callbacks= ; callbacks=

@ -4,6 +4,7 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*) *)
open! IStd open! IStd
module L = Logging module L = Logging
@ -16,19 +17,14 @@ module L = Logging
- catch / throw with exception classes - catch / throw with exception classes
*) *)
module Payload = SummaryPayload.Make (struct let do_call {InterproceduralAnalysis.analyze_dependency} callee loc init =
type t = ClassLoadsDomain.summary analyze_dependency callee
|> Option.fold ~init ~f:(fun acc (_, summary) ->
let field = Payloads.Fields.class_loads ClassLoadsDomain.integrate_summary callee loc acc summary )
end)
let do_call summary callee loc init =
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 summary tenv loc astate class_name = let rec load_class ({InterproceduralAnalysis.tenv} as analysis_data) 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
@ -38,86 +34,87 @@ let rec load_class summary tenv loc astate class_name =
let astate2 = let astate2 =
let class_initializer = Procname.(Java (Java.get_class_initializer class_name)) in let class_initializer = 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 summary class_initializer loc astate1 do_call analysis_data 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.Struct.supers) |> Option.value_map ~default:[] ~f:(fun tstruct -> tstruct.Struct.supers)
|> List.fold ~init:astate2 ~f:(load_class summary tenv loc) |> List.fold ~init:astate2 ~f:(load_class analysis_data loc)
let load_type summary tenv loc (typ : Typ.t) astate = let load_type analysis_data 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 summary tenv loc astate name load_class analysis_data loc astate name
| _ -> | _ ->
astate astate
let rec load_array summary tenv loc (typ : Typ.t) astate = let rec load_array analysis_data loc (typ : Typ.t) astate =
match typ with match typ with
| {desc= Tarray {elt}} -> | {desc= Tarray {elt}} ->
load_array summary tenv loc elt astate load_array analysis_data loc elt astate
| _ -> | _ ->
load_type summary tenv loc typ astate load_type analysis_data loc typ astate
let rec add_loads_of_exp summary tenv loc (exp : Exp.t) astate = let rec add_loads_of_exp analysis_data 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 |> JavaClassName.from_string in let class_str = Ident.name_to_string class_ident |> JavaClassName.from_string in
let class_name = Typ.JavaClass class_str in let class_name = Typ.JavaClass class_str in
load_class summary tenv loc astate class_name load_class analysis_data loc astate class_name
| Sizeof {typ= {desc= Tarray {elt}}} -> | Sizeof {typ= {desc= Tarray {elt}}} ->
(* anewarray / multinewarray *) (* anewarray / multinewarray *)
load_array summary tenv loc elt astate load_array analysis_data 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 summary tenv loc e astate add_loads_of_exp analysis_data loc e astate
| BinOp (_, e1, e2) -> | BinOp (_, e1, e2) ->
add_loads_of_exp summary tenv loc e1 astate |> add_loads_of_exp summary tenv loc e2 add_loads_of_exp analysis_data loc e1 astate |> add_loads_of_exp analysis_data loc e2
| Lfield (e, _, typ') -> | Lfield (e, _, typ') ->
(* getfield / getstatic / putfield / putstatic *) (* getfield / getstatic / putfield / putstatic *)
load_type summary tenv loc typ' astate |> add_loads_of_exp summary tenv loc e load_type analysis_data loc typ' astate |> add_loads_of_exp analysis_data loc e
| Var _ | Const _ | Closure _ | Sizeof _ | Lindex _ | Lvar _ -> | Var _ | Const _ | Closure _ | Sizeof _ | Lindex _ | Lvar _ ->
astate astate
let exec_call summary tenv callee args loc astate = let exec_call analysis_data callee args loc astate =
match args with match args with
| [_; (Exp.Sizeof {typ}, _)] when Procname.equal callee BuiltinDecl.__instanceof -> | [_; (Exp.Sizeof {typ}, _)] when Procname.equal callee BuiltinDecl.__instanceof ->
(* this matches downcasts/instanceof and exception handlers *) (* this matches downcasts/instanceof and exception handlers *)
load_type summary tenv loc typ astate load_type analysis_data 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 summary tenv loc exp acc) List.fold args ~init:astate ~f:(fun acc (exp, _) -> add_loads_of_exp analysis_data loc exp acc)
|> do_call summary callee loc |> do_call analysis_data callee loc
let exec_instr summary tenv astate _ (instr : Sil.instr) = let exec_instr analysis_data astate _ (instr : Sil.instr) =
match instr with match instr with
| Call (_, Const (Cfun callee), args, loc, _) -> | Call (_, Const (Cfun callee), args, loc, _) ->
exec_call summary tenv callee args loc astate exec_call analysis_data callee args loc astate
| Load {e= exp; loc} | Prune (exp, loc, _, _) -> | Load {e= 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 summary tenv loc exp astate add_loads_of_exp analysis_data loc exp astate
| Store {e1; e2; loc} -> | Store {e1; e2; loc} ->
add_loads_of_exp summary tenv loc e1 astate |> add_loads_of_exp summary tenv loc e2 add_loads_of_exp analysis_data loc e1 astate |> add_loads_of_exp analysis_data loc e2
| _ -> | _ ->
astate astate
let report_loads summary astate = let report_loads {InterproceduralAnalysis.proc_desc; err_log} 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
let ltr = ClassLoadsDomain.Event.make_loc_trace event in let ltr = ClassLoadsDomain.Event.make_loc_trace event in
let msg = Format.asprintf "Class %s loaded" elem in let msg = Format.asprintf "Class %s loaded" elem in
SummaryReporting.log_warning summary ~loc ~ltr IssueType.class_load msg let attrs = Procdesc.get_attributes proc_desc in
Reporting.log_warning attrs err_log ~loc ~ltr IssueType.class_load msg
in in
let pname = Summary.get_proc_name summary in let pname = Procdesc.get_proc_name proc_desc in
Procname.get_class_name pname Procname.get_class_name pname
|> Option.iter ~f:(fun clazz -> |> Option.iter ~f:(fun clazz ->
let method_strname = Procname.get_method pname in let method_strname = Procname.get_method pname in
@ -126,19 +123,16 @@ let report_loads summary astate =
ClassLoadsDomain.iter report_load astate ) ClassLoadsDomain.iter report_load astate )
let analyze_procedure {Callbacks.exe_env; summary} = let analyze_procedure ({InterproceduralAnalysis.proc_desc} as analysis_data) =
let proc_desc = Summary.get_proc_desc summary in
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
let tenv = Exe_env.get_tenv exe_env proc_name in
L.debug Analysis Verbose "CL: ANALYZING %a@." Procname.pp proc_name ; L.debug Analysis Verbose "CL: ANALYZING %a@." Procname.pp proc_name ;
let loc = Procdesc.get_loc proc_desc in let loc = Procdesc.get_loc proc_desc in
(* load the method's class *) (* load the method's class *)
let init = let init =
Procname.get_class_type_name proc_name Procname.get_class_type_name proc_name
|> Option.fold ~init:ClassLoadsDomain.bottom ~f:(load_class summary tenv loc) |> Option.fold ~init:ClassLoadsDomain.bottom ~f:(load_class analysis_data loc)
in in
let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr summary tenv) in let post = Procdesc.fold_instrs proc_desc ~init ~f:(exec_instr analysis_data) in
report_loads summary post ; report_loads analysis_data post ;
let result = Payload.update_summary post summary in
L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Procname.pp proc_name ; L.debug Analysis Verbose "CL: FINISHED ANALYZING %a@." Procname.pp proc_name ;
result Some post

@ -7,4 +7,5 @@
open! IStd open! IStd
val analyze_procedure : Callbacks.proc_callback_t val analyze_procedure :
ClassLoadsDomain.summary InterproceduralAnalysis.t -> ClassLoadsDomain.summary option

Loading…
Cancel
Save