make Siof take an Interprocedural.t

Summary:
An easy one. Will be needed eventually to make checkers/ its own dune
library.

Reviewed By: ngorogiannis

Differential Revision: D21401818

fbshipit-source-id: 64e8a4bf4
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent bafb74ffcc
commit c8882e308c

@ -52,3 +52,13 @@ let interprocedural_file payload_field checker {Callbacks.procedures; exe_env; s
in
checker
{InterproceduralAnalysis.procedures; source_file; file_exe_env= exe_env; analyze_file_dependency}
let intraprocedural_with_payload payload_field checker {Callbacks.summary; exe_env} =
let result =
checker
{ IntraproceduralAnalysis.proc_desc= Summary.get_proc_desc summary
; tenv= Exe_env.get_tenv exe_env (Summary.get_proc_name summary)
; err_log= Summary.get_err_log summary }
in
{summary with payloads= Field.fset payload_field summary.payloads result}

@ -24,3 +24,9 @@ val interprocedural_file :
(** [interprocedural_file field checker] expects [checker] to compute an {!IssueLog.t} from the
file-level analysis, given an inter-procedural analysis of dependencies that computes the
payload type corresponding to [field] *)
val intraprocedural_with_payload :
(Payloads.t, 'payload option) Field.t
-> (IntraproceduralAnalysis.t -> 'payload option)
-> Callbacks.proc_callback_t
(** runs an intra-procedural analysis that nonetheless produces a payload *)

@ -23,6 +23,10 @@ type callback_fun =
| DynamicDispatch of Callbacks.proc_callback_t
| File of {callback: Callbacks.file_callback_t; issue_dir: ResultsDirEntryName.id}
let interprocedural payload_field checker =
Procedure (CallbackOfChecker.interprocedural payload_field checker)
let dynamic_dispatch payload_field checker =
DynamicDispatch (CallbackOfChecker.interprocedural payload_field checker)
@ -31,22 +35,8 @@ let file issue_dir payload_field checker =
File {callback= CallbackOfChecker.interprocedural_file payload_field checker; issue_dir}
let proc_callback_of_intraprocedural ?payload_field checker {Callbacks.summary; exe_env} =
let result =
checker
{ IntraproceduralAnalysis.proc_desc= Summary.get_proc_desc summary
; tenv= Exe_env.get_tenv exe_env (Summary.get_proc_name summary)
; err_log= Summary.get_err_log summary }
in
match payload_field with
| None ->
summary
| Some payload_field ->
{summary with payloads= Field.fset payload_field summary.payloads result}
let intraprocedural_with_payload payload_field checker =
Procedure (proc_callback_of_intraprocedural ~payload_field checker)
Procedure (CallbackOfChecker.intraprocedural_with_payload payload_field checker)
type callback = callback_fun * Language.t
@ -90,7 +80,7 @@ let all_checkers =
; callbacks= [(Procedure Uninit.checker, Language.Clang)] }
; { name= "SIOF"
; active= Config.is_checker_enabled SIOF
; callbacks= [(Procedure Siof.checker, Language.Clang)] }
; callbacks= [(interprocedural Payloads.Fields.siof Siof.checker, Language.Clang)] }
; { name= "litho-required-props"
; active= Config.is_checker_enabled LithoRequiredProps
; callbacks= [(Procedure RequiredProps.checker, Language.Java)] }

@ -56,25 +56,16 @@ let is_modelled =
Procname.get_qualifiers pname |> QualifiedCppName.Match.match_qualifiers models_matcher
module Payload = SummaryPayload.Make (struct
type t = SiofDomain.Summary.t
let field = Payloads.Fields.siof
end)
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
module Domain = SiofDomain
type analysis_data = unit ProcData.t
type analysis_data = Domain.t InterproceduralAnalysis.t
let is_compile_time_constructed summary pv =
let is_compile_time_constructed {InterproceduralAnalysis.analyze_dependency} pv =
let init_pname = Pvar.get_initializer_pname pv in
match
Option.bind init_pname ~f:(fun callee_pname ->
Payload.read ~caller_summary:summary ~callee_pname )
with
| Some (Bottom, _) ->
match Option.bind init_pname ~f:(fun callee_pname -> analyze_dependency callee_pname) with
| Some (_, (Bottom, _)) ->
(* we analyzed the initializer for this global and found that it doesn't require any runtime
initialization so cannot participate in SIOF *)
true
@ -96,13 +87,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Staged.unstage (filter_global_accesses (Domain.VarNames.of_list always_initialized))
let get_globals summary e =
let get_globals analysis_data e =
let is_dangerous_global pv =
Pvar.is_global pv
&& (not (Pvar.is_static_local pv))
&& (not (Pvar.is_pod pv))
&& (not (Pvar.is_compile_constant pv))
&& (not (is_compile_time_constructed summary pv))
&& (not (is_compile_time_constructed analysis_data pv))
&& is_not_always_initialized pv
in
Exp.program_vars e
@ -129,19 +120,21 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(NonBottom trace_with_non_init_globals, snd astate)
let add_actuals_globals astate0 summary call_loc actuals =
let add_actuals_globals analysis_data astate0 call_loc actuals =
List.fold_left actuals ~init:astate0 ~f:(fun astate (e, _) ->
get_globals summary e |> add_globals astate call_loc )
get_globals analysis_data e |> add_globals astate call_loc )
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
({InterproceduralAnalysis.proc_desc; analyze_dependency; _} as analysis_data) _
(instr : Sil.instr) =
match instr with
| Store {e1= Lvar global; typ= Typ.{desc= Tptr _}; e2= Lvar _; loc}
when (Option.equal Procname.equal)
(Pvar.get_initializer_pname global)
(Some (Summary.get_proc_name summary)) ->
(Some (Procdesc.get_proc_name proc_desc)) ->
(* 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
dereference it, thus requiring the target object to be initialized. Solving this would
@ -153,7 +146,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Load {e= exp; loc} (* dereference -> add all the dangerous variables *)
| Store {e2= exp; loc} (* except in the case above, consider all reads as dangerous *)
| Prune (exp, loc, _, _) ->
get_globals summary exp |> add_globals astate loc
get_globals analysis_data exp |> add_globals astate loc
| Call (_, Const (Cfun callee_pname), _, _, _) when is_whitelisted callee_pname ->
at_least_nonbottom astate
| Call (_, Const (Cfun callee_pname), _, _, _) when is_modelled callee_pname ->
@ -169,11 +162,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Domain.join astate (NonBottom SiofTrace.bottom, Domain.VarNames.of_list init)
| Call (_, Const (Cfun (ObjC_Cpp cpp_pname as callee_pname)), _ :: actuals_without_self, loc, _)
when Procname.is_constructor callee_pname && Procname.ObjC_Cpp.is_constexpr cpp_pname ->
add_actuals_globals astate summary loc actuals_without_self
add_actuals_globals analysis_data astate loc actuals_without_self
| Call (_, Const (Cfun callee_pname), actuals, loc, _) ->
let callee_astate =
match Payload.read ~caller_summary:summary ~callee_pname with
| Some (NonBottom trace, initialized_by_callee) ->
match analyze_dependency callee_pname with
| Some (_, (NonBottom trace, initialized_by_callee)) ->
let already_initialized = snd astate in
let dangerous_accesses =
SiofTrace.sinks trace
@ -188,17 +181,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
dangerous_accesses
in
(NonBottom (SiofTrace.update_sinks trace sinks), initialized_by_callee)
| Some ((Bottom, _) as callee_astate) ->
| Some (_, ((Bottom, _) as callee_astate)) ->
callee_astate
| None ->
(Bottom, Domain.VarNames.empty)
in
add_actuals_globals astate summary loc actuals
add_actuals_globals analysis_data astate loc actuals
|> Domain.join callee_astate
|> (* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_nonbottom
| Call (_, _, actuals, loc, _) ->
add_actuals_globals astate summary loc actuals
add_actuals_globals analysis_data astate loc actuals
|> (* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_nonbottom
| Metadata _ ->
@ -218,10 +211,11 @@ let is_foreign current_tu v =
true
let report_siof summary trace gname loc =
let report_siof {InterproceduralAnalysis.proc_desc; err_log; analyze_dependency; _} trace gname loc
=
let trace_of_pname pname =
match Payload.read ~caller_summary:summary ~callee_pname:pname with
| Some (NonBottom summary, _) ->
match analyze_dependency pname with
| Some (_, (NonBottom summary, _)) ->
summary
| _ ->
SiofTrace.bottom
@ -237,7 +231,8 @@ let report_siof summary trace gname loc =
GlobalVar.pp (SiofTrace.Sink.kind final_sink)
in
let ltr = SiofTrace.trace_of_error loc gname trace in
SummaryReporting.log_error summary ~loc ~ltr IssueType.static_initialization_order_fiasco
let attrs = Procdesc.get_attributes proc_desc in
Reporting.log_error attrs err_log ~loc ~ltr IssueType.static_initialization_order_fiasco
description
in
let reportable_paths = SiofTrace.get_reportable_sink_paths trace ~trace_of_pname in
@ -246,32 +241,26 @@ let report_siof summary trace gname loc =
else List.iter ~f:report_one_path reportable_paths
let siof_check gname (summary : Summary.t) =
let pdesc = Summary.get_proc_desc summary in
match summary.payloads.siof with
let siof_check ({InterproceduralAnalysis.proc_desc} as analysis_data) gname summary =
match summary with
| Some (NonBottom post, _) ->
let attrs = Procdesc.get_attributes pdesc in
let tu =
let attrs = Procdesc.get_attributes pdesc in
attrs.ProcAttributes.translation_unit
in
let attrs = Procdesc.get_attributes proc_desc in
let tu = attrs.ProcAttributes.translation_unit in
let foreign_sinks =
SiofTrace.Sinks.filter
(fun sink -> SiofTrace.Sink.kind sink |> is_foreign tu)
(SiofTrace.sinks post)
in
if not (SiofTrace.Sinks.is_empty foreign_sinks) then
report_siof summary
report_siof analysis_data
(SiofTrace.update_sinks post foreign_sinks)
gname attrs.ProcAttributes.loc
| Some (Bottom, _) | None ->
()
let checker {Callbacks.exe_env; summary} : Summary.t =
let proc_desc = Summary.get_proc_desc summary in
let checker ({InterproceduralAnalysis.proc_desc} as analysis_data) =
let pname = Procdesc.get_proc_name proc_desc in
let tenv = Exe_env.get_tenv exe_env pname in
let standard_streams_initialized_in_tu =
let includes_iostream tu =
let magic_iostream_marker =
@ -287,30 +276,24 @@ let checker {Callbacks.exe_env; summary} : Summary.t =
in
includes_iostream (Procdesc.get_attributes proc_desc).ProcAttributes.translation_unit
in
let proc_data = {ProcData.summary; tenv; extras= ()} in
let initial =
( Bottom
, if standard_streams_initialized_in_tu then SiofDomain.VarNames.of_list standard_streams
else SiofDomain.VarNames.empty )
in
let updated_summary =
let summary =
(* If the function is constexpr then it doesn't participate in SIOF. The checker should be able
to figure this out when analyzing the function, but we might as well use the user's
specification if it's given to us. This also serves as an optimization as this skips the
analysis of the function. *)
if
match pname with ObjC_Cpp cpp_pname -> Procname.ObjC_Cpp.is_constexpr cpp_pname | _ -> false
then Payload.update_summary initial summary
else
match Analyzer.compute_post proc_data ~initial proc_desc with
| Some post ->
Payload.update_summary post summary
| None ->
summary
then Some initial
else Analyzer.compute_post analysis_data ~initial proc_desc
in
( match Procname.get_global_name_of_initializer pname with
| Some gname ->
siof_check gname updated_summary
siof_check analysis_data gname summary
| None ->
() ) ;
updated_summary
summary

@ -7,4 +7,4 @@
open! IStd
val checker : Callbacks.proc_callback_t
val checker : SiofDomain.t InterproceduralAnalysis.t -> SiofDomain.t option

Loading…
Cancel
Save