diff --git a/infer/src/cost/cost.ml b/infer/src/cost/cost.ml index 3d35294a2..7f7535343 100644 --- a/infer/src/cost/cost.ml +++ b/infer/src/cost/cost.ml @@ -52,8 +52,8 @@ module InstrBasicCostWithReason = struct List.exists allocation_functions ~f:(fun f -> Procname.equal callee_pname f) - let is_autorelease_function callee_pname = - String.equal (Procname.get_method callee_pname) "autorelease" + let is_autorelease_function tenv callee_pname fun_arg_list = + CostAutoreleaseModels.Call.dispatch tenv callee_pname fun_arg_list |> Option.is_some (** The methods whose name start with one of the prefixes return an object that is owned by the @@ -82,6 +82,10 @@ module InstrBasicCostWithReason = struct ; get_formals } = extras in + let fun_arg_list = + List.map params ~f:(fun (exp, typ) -> + ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) + in let cost = match BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map @@ -90,10 +94,6 @@ module InstrBasicCostWithReason = struct CostDomain.unit_cost_atomic_operation | Some inferbo_mem -> ( let loc = InstrCFG.Node.loc instr_node in - let fun_arg_list = - List.map params ~f:(fun (exp, typ) -> - ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) - in match CostModels.Call.dispatch tenv callee_pname fun_arg_list with | Some model -> let node_hash = InstrCFG.Node.hash instr_node in @@ -128,9 +128,9 @@ module InstrBasicCostWithReason = struct CostDomain.plus CostDomain.unit_cost_allocation cost else cost in - if is_autorelease_function callee_pname then + if is_autorelease_function tenv callee_pname fun_arg_list then let autoreleasepool_trace = - Bounds.BoundTrace.of_modeled_function "autorelease" location + Bounds.BoundTrace.of_modeled_function (Procname.to_string callee_pname) location in CostDomain.plus cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace) else if diff --git a/infer/src/cost/costAutoreleaseModels.ml b/infer/src/cost/costAutoreleaseModels.ml new file mode 100644 index 000000000..5fb6b4d21 --- /dev/null +++ b/infer/src/cost/costAutoreleaseModels.ml @@ -0,0 +1,23 @@ +(* + * 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 + +module Call = struct + let dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher = + let open ProcnameDispatcher.Call in + make_dispatcher + [ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> () + ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" + &:: "initForReadingFromData:error:" &--> () + ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" + &:: "initForReadingWithData:" &--> () + ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" + &:: "unarchivedObjectOfClass:fromData:error:" &--> () + ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" + &:: "unarchivedObjectOfClasses:fromData:error:" &--> () ] +end diff --git a/infer/src/cost/costAutoreleaseModels.mli b/infer/src/cost/costAutoreleaseModels.mli new file mode 100644 index 000000000..ab4751a64 --- /dev/null +++ b/infer/src/cost/costAutoreleaseModels.mli @@ -0,0 +1,12 @@ +(* + * 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 + +module Call : sig + val dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher +end diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m b/infer/tests/codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m new file mode 100644 index 000000000..3f9759fb6 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m @@ -0,0 +1,38 @@ +/* + * 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. + */ +#import + +@interface ArcKeyedUnarchiver : NSObject +@end + +@implementation ArcKeyedUnarchiver + +- (void)callInitForReadingFromData_constant:(int)n data:(NSData*)data { + NSKeyedUnarchiver* x = [[NSKeyedUnarchiver alloc] initForReadingFromData:data + error:nil]; +} + +- (void)callInitForReadingWithData_constant:(int)n data:(NSData*)data { + NSKeyedUnarchiver* x = + [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; +} + +- (void)callUnarchivedObjectOfClass_constant:(int)n data:(NSData*)data { + NSArray* x = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] + fromData:data + error:nil]; +} + +- (void)callUnarchivedObjectOfClasses_constant:(int)n + set:(NSSet*)set + data:(NSData*)data { + NSArray* x = [NSKeyedUnarchiver unarchivedObjectOfClasses:set + fromData:data + error:nil]; +} + +@end diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp index dce262f7a..323ee226f 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, [] 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 autorelease] +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_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, [] @@ -12,26 +12,31 @@ codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.newObject, 0, OnUITh codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callAllocObject_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callCopyObject_zero:x:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_autoreleasepool_zero:, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to autorelease] +codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callMutableCopyObject_zero:x:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callNewObject_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.dealloc, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.callInitForReadingFromData_constant:data:, 1, OnUIThread:false, [autorelease,Modeled call to NSKeyedUnarchiver.initForReadingFromData:error:] +codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.callInitForReadingWithData_constant:data:, 1, OnUIThread:false, [autorelease,Modeled call to NSKeyedUnarchiver.initForReadingWithData:] +codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.callUnarchivedObjectOfClass_constant:data:, 1, OnUIThread:false, [autorelease,Modeled call to NSKeyedUnarchiver.unarchivedObjectOfClass:fromData:error:] +codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.callUnarchivedObjectOfClasses_constant:set:data:, 1, OnUIThread:false, [autorelease,Modeled call to NSKeyedUnarchiver.unarchivedObjectOfClasses:fromData:error:] +codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.dealloc, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/basic.m, Basic.autorelease_unreachable_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_autoreleasepool_zero:, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_nested_zero:, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_constant:, 1, OnUIThread:false, [autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease] -codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease] -codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_autorelease_constant, 1, OnUIThread:false, [autorelease,Modeled call to autorelease] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_constant:, 1, OnUIThread:false, [autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_autorelease_constant, 1, OnUIThread:false, [autorelease,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_no_autorelease_zero, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/basic.m, Basic.dealloc, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/basic.m, Basic.loop_in_autoreleasepool_zero:, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/basic.m, Basic.multiple_autorelease_constants:, 2 + n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.multiple_autorelease_constants:, 2 + n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/basic.m, Basic.no_autoreleased_in_loop_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.allocObject, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.copyObject:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.dealloc, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.giveMeObject, 1, OnUIThread:false, [autorelease,Modeled call to autorelease] +codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.giveMeObject, 1, OnUIThread:false, [autorelease,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.mutableCopyObject:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.newObject, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callAllocObject_zero:, 0, OnUIThread:false, []