|
|
|
@ -15,12 +15,6 @@ module MaybeUninitVars = UninitDomain.MaybeUninitVars
|
|
|
|
|
module AliasedVars = AbstractDomain.FiniteSet (UninitDomain.VarPair)
|
|
|
|
|
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
|
|
|
|
|
let initializing_all_args = [BuiltinDecl.__set_array_length]
|
|
|
|
|
|
|
|
|
@ -38,7 +32,8 @@ let should_report_on_type t =
|
|
|
|
|
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
|
|
|
|
|
let get_locals tenv pdesc =
|
|
|
|
@ -75,15 +70,16 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
|
module CFG = CFG
|
|
|
|
|
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 =
|
|
|
|
|
F.asprintf "The value read from %a was never initialized" HilExp.AccessExpression.pp
|
|
|
|
|
access_expr
|
|
|
|
|
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
|
|
|
|
@ -129,18 +125,19 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
|
|| 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 ->
|
|
|
|
|
match HilExp.ignore_cast e with
|
|
|
|
|
| HilExp.AccessExpression access_expr ->
|
|
|
|
|
let _, t = HilExp.AccessExpression.get_base access_expr in
|
|
|
|
|
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
|
|
|
|
|
(Option.exists callee_formals_opt ~f:(fun callee_formals ->
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let remove_initialized_params summary call maybe_uninit_vars idx access_expr remove_fields =
|
|
|
|
|
match Payload.read ~caller_summary:summary ~callee_pname:call with
|
|
|
|
|
| Some {pre= init_formals; post= _} -> (
|
|
|
|
|
let remove_initialized_params {InterproceduralAnalysis.analyze_dependency} call maybe_uninit_vars
|
|
|
|
|
idx access_expr remove_fields =
|
|
|
|
|
match analyze_dependency call with
|
|
|
|
|
| Some (_, {UninitDomain.pre= init_formals; post= _}) -> (
|
|
|
|
|
match init_nth_actual_param call idx init_formals with
|
|
|
|
|
| Some var_formal ->
|
|
|
|
|
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 *)
|
|
|
|
|
let function_initializes_some_formal_params summary call =
|
|
|
|
|
match Payload.read ~caller_summary:summary ~callee_pname:call with
|
|
|
|
|
| Some {pre= initialized_formal_params; post= _} ->
|
|
|
|
|
let function_initializes_some_formal_params {InterproceduralAnalysis.analyze_dependency} call =
|
|
|
|
|
match analyze_dependency call with
|
|
|
|
|
| Some (_, {UninitDomain.pre= initialized_formal_params; post= _}) ->
|
|
|
|
|
not (D.is_empty initialized_formal_params)
|
|
|
|
|
| _ ->
|
|
|
|
|
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) =
|
|
|
|
|
let pdesc = Summary.get_proc_desc summary in
|
|
|
|
|
let check_access_expr ~loc rhs_access_expr =
|
|
|
|
|
if should_report_var pdesc tenv astate.maybe_uninit_vars rhs_access_expr then
|
|
|
|
|
report_intra rhs_access_expr loc summary
|
|
|
|
|
if should_report_var proc_desc tenv astate.maybe_uninit_vars rhs_access_expr then
|
|
|
|
|
report_intra rhs_access_expr loc analysis_data
|
|
|
|
|
in
|
|
|
|
|
let rec check_hil_expr ~loc = function
|
|
|
|
|
| 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
|
|
|
|
|
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 *)
|
|
|
|
|
if function_initializes_some_formal_params summary call then
|
|
|
|
|
if function_initializes_some_formal_params analysis_data call then
|
|
|
|
|
let maybe_uninit_vars =
|
|
|
|
|
(* in HIL/SIL the default constructor has only one param: the struct *)
|
|
|
|
|
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) -> (
|
|
|
|
|
match pname_opt with
|
|
|
|
|
| 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 )
|
|
|
|
|
| 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 *)
|
|
|
|
|
match pname_opt with
|
|
|
|
|
| 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
|
|
|
|
|
acc )
|
|
|
|
|
| _ ->
|
|
|
|
@ -323,7 +322,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
|
in
|
|
|
|
|
( match call with
|
|
|
|
|
| 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 _ ->
|
|
|
|
|
() ) ;
|
|
|
|
|
{astate with maybe_uninit_vars}
|
|
|
|
@ -339,10 +338,7 @@ end
|
|
|
|
|
module CFG = ProcCfg.Normal
|
|
|
|
|
module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (CFG))
|
|
|
|
|
|
|
|
|
|
let checker {Callbacks.exe_env; summary} : Summary.t =
|
|
|
|
|
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
|
|
|
|
|
let checker ({InterproceduralAnalysis.proc_desc; tenv} as analysis_data) =
|
|
|
|
|
(* 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 initial =
|
|
|
|
@ -352,10 +348,7 @@ let checker {Callbacks.exe_env; summary} : Summary.t =
|
|
|
|
|
in
|
|
|
|
|
let proc_data =
|
|
|
|
|
let formals = FormalMap.make proc_desc in
|
|
|
|
|
{ProcData.summary; tenv; extras= {formals; summary}}
|
|
|
|
|
{analysis_data; formals}
|
|
|
|
|
in
|
|
|
|
|
match Analyzer.compute_post proc_data ~initial proc_desc with
|
|
|
|
|
| Some {RecordDomain.prepost} ->
|
|
|
|
|
Payload.update_summary prepost summary
|
|
|
|
|
| None ->
|
|
|
|
|
summary
|
|
|
|
|
Analyzer.compute_post proc_data ~initial proc_desc
|
|
|
|
|
|> Option.map ~f:(fun {RecordDomain.prepost; _} -> prepost)
|
|
|
|
|