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 in
checker checker
{InterproceduralAnalysis.procedures; source_file; file_exe_env= exe_env; analyze_file_dependency} {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 (** [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 file-level analysis, given an inter-procedural analysis of dependencies that computes the
payload type corresponding to [field] *) 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 | DynamicDispatch of Callbacks.proc_callback_t
| File of {callback: Callbacks.file_callback_t; issue_dir: ResultsDirEntryName.id} | 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 = let dynamic_dispatch payload_field checker =
DynamicDispatch (CallbackOfChecker.interprocedural 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} 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 = 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 type callback = callback_fun * Language.t
@ -90,7 +80,7 @@ let all_checkers =
; callbacks= [(Procedure Uninit.checker, Language.Clang)] } ; callbacks= [(Procedure Uninit.checker, Language.Clang)] }
; { name= "SIOF" ; { name= "SIOF"
; active= Config.is_checker_enabled 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" ; { name= "litho-required-props"
; active= Config.is_checker_enabled LithoRequiredProps ; active= Config.is_checker_enabled LithoRequiredProps
; callbacks= [(Procedure RequiredProps.checker, Language.Java)] } ; callbacks= [(Procedure RequiredProps.checker, Language.Java)] }

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

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

Loading…
Cancel
Save