diff --git a/infer/src/IR/BuiltinDecl.ml b/infer/src/IR/BuiltinDecl.ml index 0a99caa7b..ee860f355 100644 --- a/infer/src/IR/BuiltinDecl.ml +++ b/infer/src/IR/BuiltinDecl.ml @@ -184,3 +184,5 @@ let vwscanf = create_procname "vwscanf" let wscanf = create_procname "wscanf" let zero_initialization = create_procname "__infer_zero_initialization" + +let match_builtin builtin _ s = String.equal s (Procname.get_method builtin) diff --git a/infer/src/IR/BuiltinDecl.mli b/infer/src/IR/BuiltinDecl.mli index d0a3d2b79..3b902e868 100644 --- a/infer/src/IR/BuiltinDecl.mli +++ b/infer/src/IR/BuiltinDecl.mli @@ -20,3 +20,5 @@ val __infer_skip_function : Procname.t val __infer_skip_gcc_asm_stmt : Procname.t val __infer_generic_selection_expr : Procname.t + +val match_builtin : t -> 'a -> string -> bool diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index 9ade7d563..33c66aeef 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -1642,30 +1642,29 @@ module Call = struct let short_typ = Typ.mk (Typ.Tint Typ.IUShort) in let char_ptr = Typ.mk (Typ.Tptr (char_typ, Pk_pointer)) in let short_array = Typ.mk (Typ.Tptr (Typ.mk_array short_typ, Pk_pointer)) in - let match_builtin builtin _ s = String.equal s (Procname.get_method builtin) in make_dispatcher [ (* Clang common models *) - +match_builtin BuiltinDecl.__cast <>$ capt_exp $+ capt_exp $+...$--> cast - ; +match_builtin BuiltinDecl.__exit <>--> bottom - ; +match_builtin BuiltinDecl.__get_array_length <>$ capt_exp $!--> get_array_length - ; +match_builtin BuiltinDecl.objc_cpp_throw <>--> bottom - ; +match_builtin BuiltinDecl.__new + +BuiltinDecl.(match_builtin __cast) <>$ capt_exp $+ capt_exp $+...$--> cast + ; +BuiltinDecl.(match_builtin __exit) <>--> bottom + ; +BuiltinDecl.(match_builtin __get_array_length) <>$ capt_exp $!--> get_array_length + ; +BuiltinDecl.(match_builtin objc_cpp_throw) <>--> bottom + ; +BuiltinDecl.(match_builtin __new) <>$ any_arg_of_typ (+PatternMatch.Java.implements_collection) $+...$--> Collection.new_collection - ; +match_builtin BuiltinDecl.__new + ; +BuiltinDecl.(match_builtin __new) <>$ any_arg_of_typ (+PatternMatch.Java.implements_map) $+...$--> Collection.new_collection - ; +match_builtin BuiltinDecl.__new + ; +BuiltinDecl.(match_builtin __new) <>$ any_arg_of_typ (+PatternMatch.Java.implements_org_json "JSONArray") $+...$--> Collection.new_collection - ; +match_builtin BuiltinDecl.__new + ; +BuiltinDecl.(match_builtin __new) <>$ any_arg_of_typ (+PatternMatch.Java.implements_pseudo_collection) $+...$--> Collection.new_collection - ; +match_builtin BuiltinDecl.__new <>$ capt_exp $+...$--> malloc ~can_be_zero:true - ; +match_builtin BuiltinDecl.__new_array <>$ capt_exp $+...$--> malloc ~can_be_zero:true - ; +match_builtin BuiltinDecl.__placement_new + ; +BuiltinDecl.(match_builtin __new) <>$ capt_exp $+...$--> malloc ~can_be_zero:true + ; +BuiltinDecl.(match_builtin __new_array) <>$ capt_exp $+...$--> malloc ~can_be_zero:true + ; +BuiltinDecl.(match_builtin __placement_new) <>$ capt_exp $+ capt_arg $+? capt_arg $!--> placement_new - ; +match_builtin BuiltinDecl.__set_array_length + ; +BuiltinDecl.(match_builtin __set_array_length) <>$ capt_arg $+ capt_exp $!--> set_array_length ; -"calloc" <>$ capt_exp $+ capt_exp $!--> calloc ~can_be_zero:false ; -"exit" <>--> bottom @@ -1685,7 +1684,7 @@ module Call = struct ; -"strndup" <>$ capt_exp $+ capt_exp $+...$--> strndup ; -"vsnprintf" <>--> by_value Dom.Val.Itv.nat ; (* ObjC models *) - +match_builtin BuiltinDecl.__objc_alloc_no_fail <>$ capt_exp $+...$--> objc_malloc + +BuiltinDecl.(match_builtin __objc_alloc_no_fail) <>$ capt_exp $+...$--> objc_malloc ; -"CFArrayCreate" <>$ any_arg $+ capt_exp $+ capt_exp $+...$--> NSCollection.create_from_array ; -"CFArrayCreateCopy" <>$ any_arg $+ capt_exp $!--> create_copy_array diff --git a/infer/src/checkers/purityModels.ml b/infer/src/checkers/purityModels.ml index 180e5e6f4..a7306eedd 100644 --- a/infer/src/checkers/purityModels.ml +++ b/infer/src/checkers/purityModels.ml @@ -40,13 +40,12 @@ let getStarValue tenv s = module ProcName = struct let dispatch : (Tenv.t, PurityDomain.t, unit) ProcnameDispatcher.ProcName.dispatcher = let open ProcnameDispatcher.ProcName in - let match_builtin builtin _ s = String.equal s (Procname.get_method builtin) in make_dispatcher [ +pure_builtins <>--> PurityDomain.pure ; -"__variable_initialization" <>--> PurityDomain.pure - ; +match_builtin BuiltinDecl.__new <>--> PurityDomain.pure - ; +match_builtin BuiltinDecl.__new_array <>--> PurityDomain.pure - ; +match_builtin BuiltinDecl.__cast <>--> PurityDomain.pure + ; +BuiltinDecl.(match_builtin __new) <>--> PurityDomain.pure + ; +BuiltinDecl.(match_builtin __new_array) <>--> PurityDomain.pure + ; +BuiltinDecl.(match_builtin __cast) <>--> PurityDomain.pure ; -"__variable_initialization" <>--> PurityDomain.pure ; +(fun _ name -> BuiltinDecl.is_declared (Procname.from_string_c_fun name)) <>--> PurityDomain.impure_global diff --git a/infer/src/cost/ConfigImpactAnalysis.ml b/infer/src/cost/ConfigImpactAnalysis.ml index 91558952a..578b87873 100644 --- a/infer/src/cost/ConfigImpactAnalysis.ml +++ b/infer/src/cost/ConfigImpactAnalysis.ml @@ -341,33 +341,56 @@ module Dom = struct fields field_checks } ) - let is_known_expensive_method = - let dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher = + type known_expensiveness = KnownCheap | KnownExpensive + + let get_expensiveness_model = + let dispatch : (Tenv.t, known_expensiveness, unit) ProcnameDispatcher.Call.dispatcher = let open ProcnameDispatcher.Call in make_dispatcher - [ +PatternMatch.Java.implements_google "common.base.Preconditions" - &:: "checkArgument" $ any_arg $+ any_arg $+...$--> () + [ +BuiltinDecl.(match_builtin __cast) <>--> KnownCheap + ; +PatternMatch.Java.implements_google "common.base.Preconditions" + &:: "checkArgument" $ any_arg $+ any_arg $+...$--> KnownExpensive ; +PatternMatch.Java.implements_google "common.base.Preconditions" - &:: "checkElementIndex" $ any_arg $+ any_arg $+ any_arg $+...$--> () + &:: "checkElementIndex" $ any_arg $+ any_arg $+ any_arg $+...$--> KnownExpensive ; +PatternMatch.Java.implements_google "common.base.Preconditions" - &:: "checkNotNull" $ any_arg $+ any_arg $+...$--> () + &:: "checkNotNull" $ any_arg $+ any_arg $+...$--> KnownExpensive ; +PatternMatch.Java.implements_google "common.base.Preconditions" - &:: "checkPositionIndex" $ any_arg $+ any_arg $+ any_arg $+...$--> () + &:: "checkPositionIndex" $ any_arg $+ any_arg $+ any_arg $+...$--> KnownExpensive ; +PatternMatch.Java.implements_google "common.base.Preconditions" - &:: "checkState" $ any_arg $+ any_arg $+...$--> () - ; +PatternMatch.Java.implements_lang "String" &:: "concat" &--> () - ; +PatternMatch.Java.implements_lang "StringBuilder" &:: "append" &--> () ] + &:: "checkState" $ any_arg $+ any_arg $+...$--> KnownExpensive + ; +PatternMatch.Java.implements_lang "String" &:: "concat" &--> KnownExpensive + ; +PatternMatch.Java.implements_lang "StringBuilder" &:: "append" &--> KnownExpensive ] in fun tenv pname args -> let args = List.map args ~f:(fun (exp, typ) -> ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) in - dispatch tenv pname args |> Option.is_some + dispatch tenv pname args let call tenv analyze_dependency ~is_cheap_call callee args location ({config_checks; field_checks; unchecked_callees; unchecked_callees_cond} as astate) = + let join_unchecked_callees new_unchecked_callees new_unchecked_callees_cond = + if FieldChecks.is_top field_checks then + { astate with + unchecked_callees= UncheckedCallees.join unchecked_callees new_unchecked_callees + ; unchecked_callees_cond= + UncheckedCalleesCond.join unchecked_callees_cond new_unchecked_callees_cond } + else + let fields_to_add = FieldChecks.get_fields field_checks in + let unchecked_callees_cond = + UncheckedCalleesCond.weak_update fields_to_add new_unchecked_callees + unchecked_callees_cond + in + let unchecked_callees_cond = + UncheckedCalleesCond.fold + (fun fields callees acc -> + UncheckedCalleesCond.weak_update (Fields.union fields fields_to_add) callees acc ) + new_unchecked_callees_cond unchecked_callees_cond + in + {astate with unchecked_callees_cond} + in if ConfigChecks.is_top config_checks then let (callee_summary : Summary.t option) = match analyze_dependency callee with @@ -376,53 +399,40 @@ module Dom = struct | Some (_, (_, analysis_data, _)) -> analysis_data in - let is_expensive = is_known_expensive_method tenv callee args in + let expensiveness_model = get_expensiveness_model tenv callee args in let has_expensive_callee = Option.exists callee_summary ~f:Summary.has_known_expensive_callee in - if is_cheap_call && (not is_expensive) && not has_expensive_callee then - (* If callee is cheap by heuristics, ignore it. *) - astate - else - let new_unchecked_callees, new_unchecked_callees_cond = - if is_expensive then - ( UncheckedCallees.singleton - (UncheckedCallee.make ~is_known_expensive:true callee location) - , UncheckedCalleesCond.empty ) - else - match callee_summary with - | Some - { Summary.unchecked_callees= callee_summary - ; unchecked_callees_cond= callee_summary_cond - ; has_call_stmt } - when has_call_stmt -> - (* If callee's summary is not leaf, use it. *) - ( UncheckedCallees.replace_location_by_call location callee_summary - , UncheckedCalleesCond.replace_location_by_call location callee_summary_cond ) - | _ -> - (* Otherwise, add callee's name. *) - ( UncheckedCallees.singleton - (UncheckedCallee.make ~is_known_expensive:false callee location) - , UncheckedCalleesCond.empty ) - in - if FieldChecks.is_top field_checks then - { astate with - unchecked_callees= UncheckedCallees.join unchecked_callees new_unchecked_callees - ; unchecked_callees_cond= - UncheckedCalleesCond.join unchecked_callees_cond new_unchecked_callees_cond } - else - let fields_to_add = FieldChecks.get_fields field_checks in - let unchecked_callees_cond = - UncheckedCalleesCond.weak_update fields_to_add new_unchecked_callees - unchecked_callees_cond - in - let unchecked_callees_cond = - UncheckedCalleesCond.fold - (fun fields callees acc -> - UncheckedCalleesCond.weak_update (Fields.union fields fields_to_add) callees acc ) - new_unchecked_callees_cond unchecked_callees_cond - in - {astate with unchecked_callees_cond} + match expensiveness_model with + | None when is_cheap_call && not has_expensive_callee -> + (* If callee is cheap by heuristics, ignore it. *) + astate + | Some KnownCheap -> + (* If callee is known cheap by model, ignore it. *) + astate + | Some KnownExpensive -> + (* If callee is known expensive by model, add callee's name. *) + join_unchecked_callees + (UncheckedCallees.singleton + (UncheckedCallee.make ~is_known_expensive:true callee location)) + UncheckedCalleesCond.empty + | None -> ( + match callee_summary with + | Some + { Summary.unchecked_callees= callee_summary + ; unchecked_callees_cond= callee_summary_cond + ; has_call_stmt } + when has_call_stmt -> + (* If callee's summary is not leaf, use it. *) + join_unchecked_callees + (UncheckedCallees.replace_location_by_call location callee_summary) + (UncheckedCalleesCond.replace_location_by_call location callee_summary_cond) + | _ -> + (* Otherwise, add callee's name. *) + join_unchecked_callees + (UncheckedCallees.singleton + (UncheckedCallee.make ~is_known_expensive:false callee location)) + UncheckedCalleesCond.empty ) else astate end diff --git a/infer/src/cost/costAllocationModels.ml b/infer/src/cost/costAllocationModels.ml index 5c0921b4c..ac34ca505 100644 --- a/infer/src/cost/costAllocationModels.ml +++ b/infer/src/cost/costAllocationModels.ml @@ -11,11 +11,10 @@ module BasicCost = CostDomain.BasicCost module ProcName = struct let dispatch : (Tenv.t, BasicCost.t, unit) ProcnameDispatcher.ProcName.dispatcher = let open ProcnameDispatcher.ProcName in - let match_builtin builtin _ s = String.equal s (Procname.get_method builtin) in make_dispatcher - [ +match_builtin BuiltinDecl.__new <>--> BasicCost.one () - ; +match_builtin BuiltinDecl.__new_array <>--> BasicCost.one () - ; +match_builtin BuiltinDecl.__objc_alloc_no_fail <>--> BasicCost.one () - ; +match_builtin BuiltinDecl.malloc <>--> BasicCost.one () - ; +match_builtin BuiltinDecl.malloc_no_fail <>--> BasicCost.one () ] + [ +BuiltinDecl.(match_builtin __new) <>--> BasicCost.one () + ; +BuiltinDecl.(match_builtin __new_array) <>--> BasicCost.one () + ; +BuiltinDecl.(match_builtin __objc_alloc_no_fail) <>--> BasicCost.one () + ; +BuiltinDecl.(match_builtin malloc) <>--> BasicCost.one () + ; +BuiltinDecl.(match_builtin malloc_no_fail) <>--> BasicCost.one () ] end diff --git a/infer/src/pulse/PulseModels.ml b/infer/src/pulse/PulseModels.ml index 61bebb23f..60c00a5f0 100644 --- a/infer/src/pulse/PulseModels.ml +++ b/infer/src/pulse/PulseModels.ml @@ -1314,7 +1314,6 @@ module StringSet = Caml.Set.Make (String) module ProcNameDispatcher = struct let dispatch : (Tenv.t * Procname.t, model, arg_payload) ProcnameDispatcher.Call.dispatcher = let open ProcnameDispatcher.Call in - let match_builtin builtin _ s = String.equal s (Procname.get_method builtin) in let pushback_modeled = StringSet.of_list ["add"; "addAll"; "append"; "delete"; "remove"; "replace"; "poll"; "put"; "putAll"] @@ -1355,25 +1354,27 @@ module ProcNameDispatcher = struct let map_context_tenv f (x, _) = f x in make_dispatcher ( transfer_ownership_matchers @ abort_matchers - @ [ +match_builtin BuiltinDecl.free <>$ capt_arg_payload $--> C.free - ; +match_builtin BuiltinDecl.malloc <>$ capt_exp $--> C.malloc - ; +match_builtin BuiltinDecl.__delete <>$ capt_arg_payload $--> Cplusplus.delete - ; +match_builtin BuiltinDecl.__new <>$ capt_exp $--> Misc.alloc_not_null_call_ev ~desc:"new" - ; +match_builtin BuiltinDecl.__new_array + @ [ +BuiltinDecl.(match_builtin free) <>$ capt_arg_payload $--> C.free + ; +BuiltinDecl.(match_builtin malloc) <>$ capt_exp $--> C.malloc + ; +BuiltinDecl.(match_builtin __delete) <>$ capt_arg_payload $--> Cplusplus.delete + ; +BuiltinDecl.(match_builtin __new) <>$ capt_exp $--> Misc.alloc_not_null_call_ev ~desc:"new" - ; +match_builtin BuiltinDecl.__placement_new &++> Cplusplus.placement_new - ; +match_builtin BuiltinDecl.objc_cpp_throw <>--> Misc.early_exit - ; +match_builtin BuiltinDecl.__cast + ; +BuiltinDecl.(match_builtin __new_array) + <>$ capt_exp + $--> Misc.alloc_not_null_call_ev ~desc:"new" + ; +BuiltinDecl.(match_builtin __placement_new) &++> Cplusplus.placement_new + ; +BuiltinDecl.(match_builtin objc_cpp_throw) <>--> Misc.early_exit + ; +BuiltinDecl.(match_builtin __cast) <>$ capt_arg_payload $+...$--> Misc.id_first_arg ~desc:"cast" - ; +match_builtin BuiltinDecl.abort <>--> Misc.early_exit - ; +match_builtin BuiltinDecl.exit <>--> Misc.early_exit - ; +match_builtin BuiltinDecl.__infer_initializer_list + ; +BuiltinDecl.(match_builtin abort) <>--> Misc.early_exit + ; +BuiltinDecl.(match_builtin exit) <>--> Misc.early_exit + ; +BuiltinDecl.(match_builtin __infer_initializer_list) <>$ capt_arg_payload $+...$--> Misc.id_first_arg ~desc:"infer_init_list" ; +map_context_tenv (PatternMatch.Java.implements_lang "System") &:: "exit" <>--> Misc.early_exit - ; +match_builtin BuiltinDecl.__get_array_length <>--> Misc.return_unknown_size + ; +BuiltinDecl.(match_builtin __get_array_length) <>--> Misc.return_unknown_size ; (* consider that all fbstrings are small strings to avoid false positives due to manual ref-counting *) -"folly" &:: "fbstring_core" &:: "category" &--> Misc.return_int Int64.zero @@ -1663,7 +1664,7 @@ module ProcNameDispatcher = struct ; +map_context_tenv PatternMatch.Java.implements_iterator &:: "next" <>$ capt_arg_payload $!--> JavaIterator.next ~desc:"Iterator.next()" - ; +match_builtin BuiltinDecl.__instanceof + ; +BuiltinDecl.(match_builtin __instanceof) <>$ capt_arg_payload $+ capt_exp $--> Java.instance_of ; ( +map_context_tenv PatternMatch.Java.implements_enumeration &:: "nextElement" <>$ capt_arg_payload @@ -1677,7 +1678,7 @@ module ProcNameDispatcher = struct &--> C.malloc_no_param ; +map_context_tenv PatternMatch.ObjectiveC.is_core_foundation_create_or_copy &--> C.malloc_no_param - ; +match_builtin BuiltinDecl.malloc_no_fail <>$ capt_exp $--> C.malloc_not_null + ; +BuiltinDecl.(match_builtin malloc_no_fail) <>$ capt_exp $--> C.malloc_not_null ; +map_context_tenv PatternMatch.ObjectiveC.is_modelled_as_alloc &--> C.malloc_not_null_no_param ; +map_context_tenv PatternMatch.ObjectiveC.is_core_graphics_release @@ -1687,9 +1688,9 @@ module ProcNameDispatcher = struct <>$ capt_arg_payload $--> ObjCCoreFoundation.cf_bridging_release ; -"CFAutorelease" <>$ capt_arg_payload $--> ObjCCoreFoundation.cf_bridging_release ; -"CFBridgingRelease" <>$ capt_arg_payload $--> ObjCCoreFoundation.cf_bridging_release - ; +match_builtin BuiltinDecl.__objc_bridge_transfer + ; +BuiltinDecl.(match_builtin __objc_bridge_transfer) <>$ capt_arg_payload $--> ObjCCoreFoundation.cf_bridging_release - ; +match_builtin BuiltinDecl.__objc_alloc_no_fail + ; +BuiltinDecl.(match_builtin __objc_alloc_no_fail) <>$ capt_exp $--> ObjC.alloc_not_null_alloc_ev ~desc:"alloc" ; -"NSObject" &:: "init" <>$ capt_arg_payload $--> Misc.id_first_arg ~desc:"NSObject.init"