diff --git a/infer/src/absint/ProcnameDispatcher.ml b/infer/src/absint/ProcnameDispatcher.ml index 506e60d57..aeca09b3e 100644 --- a/infer/src/absint/ProcnameDispatcher.ml +++ b/infer/src/absint/ProcnameDispatcher.ml @@ -575,15 +575,20 @@ module Call = struct List.find_map matchers ~f:(fun (matcher : _ matcher) -> matcher.on_java context java args) in fun context procname args -> - match procname with - | ObjC_Cpp objc_cpp -> - on_objc_cpp context objc_cpp args - | C c -> - on_c context c args - | Java java -> - on_java context java args - | _ -> - None + let rec match_procname procname = + match (procname : Procname.t) with + | ObjC_Cpp objc_cpp -> + on_objc_cpp context objc_cpp args + | C c -> + on_c context c args + | Java java -> + on_java context java args + | WithBlockParameters (procname, _) -> + match_procname procname + | _ -> + None + in + match_procname procname let merge_dispatchers : diff --git a/infer/src/bufferoverrun/polynomials.ml b/infer/src/bufferoverrun/polynomials.ml index 0f2c26c05..3792728c4 100644 --- a/infer/src/bufferoverrun/polynomials.ml +++ b/infer/src/bufferoverrun/polynomials.ml @@ -331,8 +331,7 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct |> M.fold (fun s p acc -> plus_poly (mult_symb_poly (mult_poly p p1) s) acc) p2.terms - let mult : t -> t -> t = - fun p1 p2 -> + let mult_common p1 p2 ~join_autoreleasepool_trace = let poly = mult_poly p1.poly p2.poly in let autoreleasepool_trace = if is_zero_poly poly then None @@ -342,6 +341,15 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct {poly; autoreleasepool_trace} + let mult p1 p2 = mult_common p1 p2 ~join_autoreleasepool_trace + + (** It takes only the trace of the body part, because the trace for the iteration number will be + taken later from symbolic values. *) + let mult_loop ~iter ~body = + mult_common iter body ~join_autoreleasepool_trace:(fun _iter_poly _body_poly _iter body -> + body ) + + let rec of_valclass : (NonNegativeInt.t, S.t, 't) Bounds.valclass -> ('t, t, 't) below_above = function | ValTop trace -> @@ -759,6 +767,11 @@ module NonNegativePolynomial = struct let mult = top_lifted_increasing ~f:NonNegativeNonTopPolynomial.mult + let mult_loop ~iter ~body = + top_lifted_increasing iter body ~f:(fun iter body -> + NonNegativeNonTopPolynomial.mult_loop ~iter ~body ) + + let min_default_left p1 p2 = AbstractDomain.StackedUtils.combine ~dir:`Decreasing p1 p2 ~f:NonNegativeNonTopPolynomial.min_default_left ~f_above:TopTraces.join diff --git a/infer/src/bufferoverrun/polynomials.mli b/infer/src/bufferoverrun/polynomials.mli index 7f5176c87..d5a048ade 100644 --- a/infer/src/bufferoverrun/polynomials.mli +++ b/infer/src/bufferoverrun/polynomials.mli @@ -79,6 +79,8 @@ module NonNegativePolynomial : sig val mult : t -> t -> t + val mult_loop : iter:t -> body:t -> t + val min_default_left : t -> t -> t val subst : Procname.t -> Location.t -> t -> Bound.eval_sym -> t diff --git a/infer/src/cost/cost.ml b/infer/src/cost/cost.ml index 4ce1b1df6..3199e90d4 100644 --- a/infer/src/cost/cost.ml +++ b/infer/src/cost/cost.ml @@ -128,7 +128,7 @@ module InstrBasicCostWithReason = struct (inferbo_mem_opt, CostAutoreleaseModels.Call.dispatch tenv callee_pname fun_arg_list) with | Some inferbo_mem, Some model -> - let autoreleasepool_size = model (Lazy.force model_env) ~ret inferbo_mem in + let autoreleasepool_size = model get_summary (Lazy.force model_env) ~ret inferbo_mem in CostDomain.plus_autoreleasepool_size autoreleasepool_size cost | _, _ -> if diff --git a/infer/src/cost/costAutoreleaseModels.ml b/infer/src/cost/costAutoreleaseModels.ml index 5becdb35f..d84a4778b 100644 --- a/infer/src/cost/costAutoreleaseModels.ml +++ b/infer/src/cost/costAutoreleaseModels.ml @@ -7,21 +7,50 @@ open! IStd module BasicCost = CostDomain.BasicCost +module BasicCostWithReason = CostDomain.BasicCostWithReason open BufferOverrunUtils.ModelEnv -let unit_cost {pname; location} ~ret:_ _inferbo_mem = +let unit_cost _get_summary {pname; location} ~ret:_ _inferbo_mem = let autoreleasepool_trace = Bounds.BoundTrace.of_modeled_function (Procname.to_string pname) location in BasicCost.one ~autoreleasepool_trace () +module NSArray = struct + let index_of_object_passing_test array get_summary ({pname; location= _} as model_env) ~ret + inferbo_mem = + match pname with + | WithBlockParameters (_, [block_name]) -> ( + match get_summary (Procname.Block block_name) with + | Some {CostDomain.post= callee_summary} -> + let {BasicCostWithReason.cost= callee_cost} = + CostDomain.get_cost_kind AutoreleasepoolSize callee_summary + in + let length = + CostModels.BoundsOfNSCollection.linear_length + ~of_function:(Procname.to_simplified_string pname) + array model_env ~ret inferbo_mem + in + BasicCost.mult_loop ~iter:length ~body:callee_cost + | None -> + BasicCost.zero ) + | _ -> + BasicCost.zero +end + module Call = struct - let dispatch : (Tenv.t, CostUtils.model, unit) ProcnameDispatcher.Call.dispatcher = + let dispatch : + ( Tenv.t + , (Procname.t -> CostDomain.summary option) -> CostUtils.model + , unit ) + ProcnameDispatcher.Call.dispatcher = let open ProcnameDispatcher.Call in make_dispatcher [ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> unit_cost ; -"CFAutorelease" &--> unit_cost + ; +PatternMatch.ObjectiveC.implements "NSArray" + &:: "indexOfObjectPassingTest:" $ capt_exp $--> NSArray.index_of_object_passing_test ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" &:: "initForReadingFromData:error:" &--> unit_cost ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" diff --git a/infer/src/cost/costAutoreleaseModels.mli b/infer/src/cost/costAutoreleaseModels.mli index 984656839..badc59bff 100644 --- a/infer/src/cost/costAutoreleaseModels.mli +++ b/infer/src/cost/costAutoreleaseModels.mli @@ -8,5 +8,9 @@ open! IStd module Call : sig - val dispatch : (Tenv.t, CostUtils.model, unit) ProcnameDispatcher.Call.dispatcher + val dispatch : + ( Tenv.t + , (Procname.t -> CostDomain.summary option) -> CostUtils.model + , unit ) + ProcnameDispatcher.Call.dispatcher end diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m b/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m index 3079924b0..9ef0d7145 100644 --- a/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/arc_block.m @@ -12,7 +12,7 @@ @implementation ArcBlock -+ (void)callIndexOfObjectPassingTest_linear_FN:(NSArray*)x { ++ (void)callIndexOfObjectPassingTest_linear:(NSArray*)x { int i = [x indexOfObjectPassingTest:^BOOL( NSObject* obj, NSUInteger idx, BOOL* stop) { NoArcCallee* o = [NoArcCallee giveMeObject]; diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp index fe8001a62..ed4a071fe 100644 --- a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp @@ -1,7 +1,7 @@ -${XCODE_ISYSROOT}/System/Library/Frameworks/Foundation.framework/Headers/NSArray.h, NSArray.indexOfObjectPassingTest:[objc_blockArcBlock.callIndexOfObjectPassingTest_linear_FN:_1], 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_linear_FN:, 0, OnUIThread:false, [] +${XCODE_ISYSROOT}/System/Library/Frameworks/Foundation.framework/Headers/NSArray.h, NSArray.indexOfObjectPassingTest:[objc_blockArcBlock.callIndexOfObjectPassingTest_linear:_1], 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_linear:, x->elements.length.ub, OnUIThread:false, [{x->elements.length.ub},Modeled call to indexOfObjectPassingTest:,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.dealloc, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/arc_block.m, objc_blockArcBlock.callIndexOfObjectPassingTest_linear_FN:_1, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] +codetoanalyze/objc/autoreleasepool/arc_block.m, objc_blockArcBlock.callIndexOfObjectPassingTest_linear:_1, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.allocObject, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.copyObject:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.dealloc, 0, OnUIThread:false, []