make uninit an `interprocedural`

Summary: Making checkers/ its own dune library.

Reviewed By: skcho

Differential Revision: D21407071

fbshipit-source-id: 564665f0b
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent 2fee63ef75
commit 221e7bd6b4

@ -125,7 +125,7 @@ let all_checkers =
[(checker, Language.Clang); (checker, Language.Java)] ) } [(checker, Language.Clang); (checker, Language.Java)] ) }
; { name= "uninitialized variables" ; { name= "uninitialized variables"
; active= Config.is_checker_enabled Uninit ; active= Config.is_checker_enabled Uninit
; callbacks= [(Procedure Uninit.checker, Language.Clang)] } ; callbacks= [(interprocedural Payloads.Fields.uninit Uninit.checker, Language.Clang)] }
; { name= "SIOF" ; { name= "SIOF"
; active= Config.is_checker_enabled SIOF ; active= Config.is_checker_enabled SIOF
; callbacks= [(interprocedural Payloads.Fields.siof Siof.checker, Language.Clang)] } ; callbacks= [(interprocedural Payloads.Fields.siof Siof.checker, Language.Clang)] }

@ -15,12 +15,6 @@ module MaybeUninitVars = UninitDomain.MaybeUninitVars
module AliasedVars = AbstractDomain.FiniteSet (UninitDomain.VarPair) module AliasedVars = AbstractDomain.FiniteSet (UninitDomain.VarPair)
module RecordDomain = UninitDomain.Record (MaybeUninitVars) (AliasedVars) (D) module RecordDomain = UninitDomain.Record (MaybeUninitVars) (AliasedVars) (D)
module Payload = SummaryPayload.Make (struct
type t = UninitDomain.Summary.t
let field = Payloads.Fields.uninit
end)
module Models = struct module Models = struct
let initializing_all_args = [BuiltinDecl.__set_array_length] let initializing_all_args = [BuiltinDecl.__set_array_length]
@ -38,7 +32,8 @@ let should_report_on_type t =
false false
type extras = {formals: FormalMap.t; summary: Summary.t} type analysis_data =
{analysis_data: UninitDomain.Summary.t InterproceduralAnalysis.t; formals: FormalMap.t}
module Initial = struct module Initial = struct
let get_locals tenv pdesc = let get_locals tenv pdesc =
@ -75,15 +70,16 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG module CFG = CFG
module Domain = RecordDomain module Domain = RecordDomain
type analysis_data = extras ProcData.t type nonrec analysis_data = analysis_data
let report_intra access_expr loc summary = let report_intra access_expr loc {InterproceduralAnalysis.proc_desc; err_log} =
let message = let message =
F.asprintf "The value read from %a was never initialized" HilExp.AccessExpression.pp F.asprintf "The value read from %a was never initialized" HilExp.AccessExpression.pp
access_expr access_expr
in in
let ltr = [Errlog.make_trace_element 0 loc "" []] in let ltr = [Errlog.make_trace_element 0 loc "" []] in
SummaryReporting.log_error summary ~loc ~ltr IssueType.uninitialized_value message let attrs = Procdesc.get_attributes proc_desc in
Reporting.log_error attrs err_log ~loc ~ltr IssueType.uninitialized_value message
let is_struct t = match t.Typ.desc with Typ.Tstruct _ -> true | _ -> false let is_struct t = match t.Typ.desc with Typ.Tstruct _ -> true | _ -> false
@ -129,18 +125,19 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|| is_array_element_passed_by_ref callee_formals t access_expr idx || is_array_element_passed_by_ref callee_formals t access_expr idx
let report_on_function_params tenv maybe_uninit_vars actuals loc summary callee_formals_opt = let report_on_function_params ({InterproceduralAnalysis.proc_desc; tenv} as analysis_data)
maybe_uninit_vars actuals loc callee_formals_opt =
List.iteri actuals ~f:(fun idx e -> List.iteri actuals ~f:(fun idx e ->
match HilExp.ignore_cast e with match HilExp.ignore_cast e with
| HilExp.AccessExpression access_expr -> | HilExp.AccessExpression access_expr ->
let _, t = HilExp.AccessExpression.get_base access_expr in let _, t = HilExp.AccessExpression.get_base access_expr in
if if
should_report_var (Summary.get_proc_desc summary) tenv maybe_uninit_vars access_expr should_report_var proc_desc tenv maybe_uninit_vars access_expr
&& (not (Typ.is_pointer t)) && (not (Typ.is_pointer t))
&& not && not
(Option.exists callee_formals_opt ~f:(fun callee_formals -> (Option.exists callee_formals_opt ~f:(fun callee_formals ->
is_struct_field_passed_by_ref callee_formals t access_expr idx )) is_struct_field_passed_by_ref callee_formals t access_expr idx ))
then report_intra access_expr loc summary then report_intra access_expr loc analysis_data
| _ -> | _ ->
() ) () )
@ -182,9 +179,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
else None else None
let remove_initialized_params summary call maybe_uninit_vars idx access_expr remove_fields = let remove_initialized_params {InterproceduralAnalysis.analyze_dependency} call maybe_uninit_vars
match Payload.read ~caller_summary:summary ~callee_pname:call with idx access_expr remove_fields =
| Some {pre= init_formals; post= _} -> ( match analyze_dependency call with
| Some (_, {UninitDomain.pre= init_formals; post= _}) -> (
match init_nth_actual_param call idx init_formals with match init_nth_actual_param call idx init_formals with
| Some var_formal -> | Some var_formal ->
let maybe_uninit_vars = MaybeUninitVars.remove access_expr maybe_uninit_vars in let maybe_uninit_vars = MaybeUninitVars.remove access_expr maybe_uninit_vars in
@ -200,20 +198,19 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(* true if a function initializes at least a param or a field of a struct param *) (* true if a function initializes at least a param or a field of a struct param *)
let function_initializes_some_formal_params summary call = let function_initializes_some_formal_params {InterproceduralAnalysis.analyze_dependency} call =
match Payload.read ~caller_summary:summary ~callee_pname:call with match analyze_dependency call with
| Some {pre= initialized_formal_params; post= _} -> | Some (_, {UninitDomain.pre= initialized_formal_params; post= _}) ->
not (D.is_empty initialized_formal_params) not (D.is_empty initialized_formal_params)
| _ -> | _ ->
false false
let exec_instr (astate : Domain.t) {ProcData.summary; extras= {formals}; tenv} _ let exec_instr (astate : Domain.t) {analysis_data= {proc_desc; tenv} as analysis_data; formals} _
(instr : HilInstr.t) = (instr : HilInstr.t) =
let pdesc = Summary.get_proc_desc summary in
let check_access_expr ~loc rhs_access_expr = let check_access_expr ~loc rhs_access_expr =
if should_report_var pdesc tenv astate.maybe_uninit_vars rhs_access_expr then if should_report_var proc_desc tenv astate.maybe_uninit_vars rhs_access_expr then
report_intra rhs_access_expr loc summary report_intra rhs_access_expr loc analysis_data
in in
let rec check_hil_expr ~loc = function let rec check_hil_expr ~loc = function
| HilExp.Cast (_, e) -> | HilExp.Cast (_, e) ->
@ -263,7 +260,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
(* if it's a default constructor, we use the following heuristic: we assume that it initializes (* if it's a default constructor, we use the following heuristic: we assume that it initializes
correctly all fields when there is an implementation of the constructor that initilizes at least one correctly all fields when there is an implementation of the constructor that initilizes at least one
field. If there is no explicit implementation we cannot assume fields are initialized *) field. If there is no explicit implementation we cannot assume fields are initialized *)
if function_initializes_some_formal_params summary call then if function_initializes_some_formal_params analysis_data call then
let maybe_uninit_vars = let maybe_uninit_vars =
(* in HIL/SIL the default constructor has only one param: the struct *) (* in HIL/SIL the default constructor has only one param: the struct *)
MaybeUninitVars.remove_all_fields tenv base astate.maybe_uninit_vars MaybeUninitVars.remove_all_fields tenv base astate.maybe_uninit_vars
@ -296,7 +293,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
~f:(is_fld_or_array_elem_passed_by_ref t access_expr idx) -> ( ~f:(is_fld_or_array_elem_passed_by_ref t access_expr idx) -> (
match pname_opt with match pname_opt with
| Some pname when Config.uninit_interproc -> | Some pname when Config.uninit_interproc ->
remove_initialized_params summary pname acc idx access_expr_to_remove false remove_initialized_params analysis_data pname acc idx access_expr_to_remove
false
| _ -> | _ ->
MaybeUninitVars.remove access_expr_to_remove acc ) MaybeUninitVars.remove access_expr_to_remove acc )
| base when Option.exists pname_opt ~f:Procname.is_constructor -> | base when Option.exists pname_opt ~f:Procname.is_constructor ->
@ -307,9 +305,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
to a function will be initialized inside that function *) to a function will be initialized inside that function *)
match pname_opt with match pname_opt with
| Some pname when Config.uninit_interproc -> | Some pname when Config.uninit_interproc ->
remove_initialized_params summary pname acc idx access_expr_to_remove true remove_initialized_params analysis_data pname acc idx access_expr_to_remove
true
| _ -> | _ ->
let locals = MaybeUninitVars.of_list (Initial.get_locals tenv pdesc) in let locals = MaybeUninitVars.of_list (Initial.get_locals tenv proc_desc) in
MaybeUninitVars.remove_everything_under tenv locals access_expr_to_remove MaybeUninitVars.remove_everything_under tenv locals access_expr_to_remove
acc ) acc )
| _ -> | _ ->
@ -323,7 +322,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
( match call with ( match call with
| Direct _ -> | Direct _ ->
report_on_function_params tenv maybe_uninit_vars actuals loc summary callee_formals_opt report_on_function_params analysis_data maybe_uninit_vars actuals loc callee_formals_opt
| Indirect _ -> | Indirect _ ->
() ) ; () ) ;
{astate with maybe_uninit_vars} {astate with maybe_uninit_vars}
@ -339,10 +338,7 @@ end
module CFG = ProcCfg.Normal module CFG = ProcCfg.Normal
module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (CFG)) module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (CFG))
let checker {Callbacks.exe_env; summary} : Summary.t = let checker ({InterproceduralAnalysis.proc_desc; tenv} as analysis_data) =
let proc_desc = Summary.get_proc_desc summary in
let proc_name = Summary.get_proc_name summary in
let tenv = Exe_env.get_tenv exe_env proc_name in
(* start with empty set of uninit local vars and empty set of init formal params *) (* start with empty set of uninit local vars and empty set of init formal params *)
let maybe_uninit_vars = Initial.get_locals tenv proc_desc in let maybe_uninit_vars = Initial.get_locals tenv proc_desc in
let initial = let initial =
@ -352,10 +348,7 @@ let checker {Callbacks.exe_env; summary} : Summary.t =
in in
let proc_data = let proc_data =
let formals = FormalMap.make proc_desc in let formals = FormalMap.make proc_desc in
{ProcData.summary; tenv; extras= {formals; summary}} {analysis_data; formals}
in in
match Analyzer.compute_post proc_data ~initial proc_desc with Analyzer.compute_post proc_data ~initial proc_desc
| Some {RecordDomain.prepost} -> |> Option.map ~f:(fun {RecordDomain.prepost; _} -> prepost)
Payload.update_summary prepost summary
| None ->
summary

@ -0,0 +1,10 @@
(*
* 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
val checker : UninitDomain.Summary.t InterproceduralAnalysis.t -> UninitDomain.Summary.t option
Loading…
Cancel
Save