diff --git a/infer/src/checkers/uninit.ml b/infer/src/checkers/uninit.ml index 7af372c61..9b763a2f9 100644 --- a/infer/src/checkers/uninit.ml +++ b/infer/src/checkers/uninit.ml @@ -195,6 +195,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct acc + (* true if a function initializes at least a param or a field of a struct param *) + let function_initializes_some_formal_params pdesc call = + match Summary.read_summary pdesc call with + | Some {pre= initialized_formal_params; post= _} -> + not (D.is_empty initialized_formal_params) + | _ -> + false + + let exec_instr (astate: Domain.astate) {ProcData.pdesc; ProcData.extras; ProcData.tenv} _ (instr: HilInstr.t) = let update_prepost (((_, lhs_typ), apl) as lhs_ap) rhs = @@ -232,8 +241,18 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Call (_, Direct callee_pname, _, _, _) when Typ.Procname.equal callee_pname BuiltinDecl.objc_cpp_throw -> {astate with uninit_vars= D.empty} - | Call (_, HilInstr.Direct call, _, _, _) when is_dummy_constructor_of_a_struct call -> - astate + | Call (_, HilInstr.Direct call, [(HilExp.AccessExpression AddressOf Base base)], _, _) + when is_dummy_constructor_of_a_struct call -> + (* 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 pdesc call then + let uninit_vars' = + (* in HIL/SIL the default constructor has only one param: the struct *) + remove_all_fields tenv base astate.uninit_vars + in + {astate with uninit_vars= uninit_vars'} + else astate | Call (_, HilInstr.Direct call, actuals, _, loc) -> (* in case of intraprocedural only analysis we assume that parameters passed by reference to a function will be initialized inside that function *) diff --git a/infer/tests/codetoanalyze/cpp/uninit/default_constr.cpp b/infer/tests/codetoanalyze/cpp/uninit/default_constr.cpp new file mode 100644 index 000000000..8befcae8e --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/uninit/default_constr.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include + +struct InitializingStruct { + size_t match1; + size_t count1; + + InitializingStruct(); +}; + +InitializingStruct::InitializingStruct() : match1(0), count1(0) {} + +struct SemiInitializingStruct { + size_t match1; + size_t count1; + + SemiInitializingStruct(); +}; + +SemiInitializingStruct::SemiInitializingStruct() : match1(0) {} + +struct NonInitializingStruct { + size_t match1; + size_t count1; +}; + +void foo(size_t, size_t){}; + +void init_OK() { + InitializingStruct s; + + foo(s.count1, s.match1); +} + +void FN_init() { + SemiInitializingStruct s; + + foo(s.count1, s.match1); +} + +void init_bad() { + NonInitializingStruct s; + + foo(s.count1, s.match1); +} diff --git a/infer/tests/codetoanalyze/cpp/uninit/issues.exp b/infer/tests/codetoanalyze/cpp/uninit/issues.exp index d04eb18dd..f346faa84 100644 --- a/infer/tests/codetoanalyze/cpp/uninit/issues.exp +++ b/infer/tests/codetoanalyze/cpp/uninit/issues.exp @@ -1,3 +1,5 @@ +codetoanalyze/cpp/uninit/default_constr.cpp, init_bad, 3, UNINITIALIZED_VALUE, ERROR, [] +codetoanalyze/cpp/uninit/default_constr.cpp, init_bad, 3, UNINITIALIZED_VALUE, ERROR, [] codetoanalyze/cpp/uninit/members.cpp, access_members_bad, 4, UNINITIALIZED_VALUE, ERROR, [] codetoanalyze/cpp/uninit/members.cpp, access_members_bad2, 4, UNINITIALIZED_VALUE, ERROR, [] codetoanalyze/cpp/uninit/members.cpp, access_members_bad3, 4, UNINITIALIZED_VALUE, ERROR, []