From 2547a75b8b4089d315c9103a20108468808e3095 Mon Sep 17 00:00:00 2001 From: Qianyi Shu Date: Sun, 26 Jul 2020 14:18:31 -0700 Subject: [PATCH] [cost] add more inferbo and cost analysis models for NSString Summary: As title Reviewed By: skcho Differential Revision: D22663505 fbshipit-source-id: 61ee2d346 --- .../src/bufferoverrun/bufferOverrunModels.ml | 37 +++++++++++++++++++ infer/src/cost/costModels.ml | 29 ++++++++++++--- .../objc/performance/NSMutableString.m | 21 +++++++++++ .../codetoanalyze/objc/performance/NSString.m | 31 +++++++++------- .../objc/performance/cost-issues.exp | 18 +++++---- .../codetoanalyze/objc/performance/issues.exp | 5 --- 6 files changed, 109 insertions(+), 32 deletions(-) create mode 100644 infer/tests/codetoanalyze/objc/performance/NSMutableString.m diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index f9bec5e91..b6fb33f55 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -1365,6 +1365,8 @@ module JavaString = struct end module NSString = struct + let fn = JavaString.fn + let create_string_from_c_string src_exp = let exec model_env ~ret mem = let v = Sem.eval_string_len src_exp mem in @@ -1373,9 +1375,39 @@ module NSString = struct {exec; check= no_check} + let substring_from_index = JavaString.substring_no_end + let length = JavaString.length + (** For cost analysis *) let get_length = JavaString.get_length + + let concat = JavaString.concat + + let split exp = + let exec ({location} as model_env) ~ret:((id, _) as ret) mem = + let itv = + ArrObjCommon.eval_size model_env exp ~fn mem + |> JavaString.range_itv_one_max_one_mone |> Dom.Val.of_itv + in + let {exec} = malloc ~can_be_zero:false Exp.one in + let mem = exec model_env ~ret mem in + let dest_loc = Loc.of_id id |> PowLoc.singleton in + Dom.Mem.transform_mem ~f:(Dom.Val.set_array_length location ~length:itv) dest_loc mem + in + {exec; check= no_check} + + + let append_string str1_exp str2_exp = + let exec ({location} as model_env) ~ret:_ mem = + let to_add_len = JavaString.get_length model_env str2_exp mem |> Dom.Val.get_itv in + let arr_locs = JavaString.deref_of model_env str1_exp mem in + let mem = Dom.Mem.forget_size_alias arr_locs mem in + Dom.Mem.transform_mem + ~f:(Dom.Val.transform_array_length location ~f:(Itv.plus to_add_len)) + arr_locs mem + in + {exec; check= no_check} end module Preconditions = struct @@ -1543,6 +1575,11 @@ module Call = struct ; -"NSString" &:: "stringWithUTF8String:" <>$ capt_exp $!--> NSString.create_string_from_c_string ; -"NSString" &:: "length" <>$ capt_exp $--> NSString.length + ; -"NSString" &:: "stringByAppendingString:" <>$ capt_exp $+ capt_exp $!--> NSString.concat + ; -"NSString" &:: "substringFromIndex:" <>$ capt_exp $+ capt_exp + $--> NSString.substring_from_index + ; -"NSString" &:: "appendString:" <>$ capt_exp $+ capt_exp $--> NSString.append_string + ; -"NSString" &:: "componentsSeparatedByString:" <>$ capt_exp $+ any_arg $--> NSString.split ; (* C++ models *) -"boost" &:: "split" $ capt_arg_of_typ (-"std" &:: "vector") diff --git a/infer/src/cost/costModels.ml b/infer/src/cost/costModels.ml index 2d1513531..7ee494022 100644 --- a/infer/src/cost/costModels.ml +++ b/infer/src/cost/costModels.ml @@ -141,14 +141,19 @@ module BoundsOfArray = BoundsOf (CostUtils.Array) module BoundsOfCString = BoundsOf (CostUtils.CString) module NSString = struct - let op_on_two_str cost_op ~of_function str1 str2 ({location} as model_env) ~ret:_ mem = - let get_length str = - let itv = - BufferOverrunModels.NSString.get_length model_env str mem |> BufferOverrunDomain.Val.get_itv - in - CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location + let get_length str ~of_function ({location} as model_env) ~ret:_ mem = + let itv = + BufferOverrunModels.NSString.get_length model_env str mem |> BufferOverrunDomain.Val.get_itv in + CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location + + + let op_on_two_str cost_op ~of_function str1 str2 model_env ~ret mem = + let get_length str = get_length str ~of_function model_env ~ret mem in cost_op (get_length str1) (get_length str2) + + + let substring_from_index = JavaString.substring_no_end end module ImmutableSet = struct @@ -167,6 +172,9 @@ module Call = struct $--> BoundsOfCString.linear_length ~of_function:"google::StrLen" ; -"NSString" &:: "stringWithUTF8String:" <>$ capt_exp $--> BoundsOfCString.linear_length ~of_function:"NSString.stringWithUTF8String:" + ; -"NSString" &:: "stringByAppendingString:" <>$ capt_exp $+ capt_exp + $--> NSString.op_on_two_str BasicCost.plus + ~of_function:"NSString.stringByAppendingString:" ; -"NSString" &:: "stringByAppendingPathComponent:" <>$ capt_exp $+ capt_exp $--> NSString.op_on_two_str BasicCost.plus ~of_function:"NSString.stringByAppendingPathComponent:" @@ -175,6 +183,15 @@ module Call = struct ~of_function:"NSString.isEqualToString:" ; -"NSString" &:: "hasPrefix:" <>$ capt_exp $+ capt_exp $--> NSString.op_on_two_str BasicCost.min_default_left ~of_function:"NSString.hasPrefix:" + ; -"NSString" &:: "substringFromIndex:" <>$ capt_exp $+ capt_exp + $!--> NSString.substring_from_index + ; -"NSString" &:: "rangeOfString:" <>$ capt_exp $+ capt_exp + $!--> NSString.op_on_two_str BasicCost.mult ~of_function:"NSString.rangeOfString:" + ; -"NSMutableString" &:: "appendString:" <>$ any_arg $+ capt_exp + $--> NSString.get_length ~of_function:"NSMutableString.appendString:" + ; -"NSString" &:: "componentsSeparatedByString:" <>$ capt_exp $+ capt_exp + $--> NSString.op_on_two_str BasicCost.mult + ~of_function:"NSString.componentsSeparatedByString:" ; +PatternMatch.implements_collections &:: "sort" $ capt_exp $+...$--> BoundsOfCollection.n_log_n_length ~of_function:"Collections.sort" diff --git a/infer/tests/codetoanalyze/objc/performance/NSMutableString.m b/infer/tests/codetoanalyze/objc/performance/NSMutableString.m new file mode 100644 index 000000000..40ccae5a5 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/NSMutableString.m @@ -0,0 +1,21 @@ +/* + * 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 + +void nsmstring_append_string_constant(NSMutableString* str) { + [str appendString:@"hello"]; + + for (int i = 0; i < str.length; i++) { + } +} + +void nsmstring_append_string_linear(NSMutableString* str1, NSString* str2) { + [str1 appendString:str2]; + + for (int i = 0; i < str1.length; i++) { + } +} diff --git a/infer/tests/codetoanalyze/objc/performance/NSString.m b/infer/tests/codetoanalyze/objc/performance/NSString.m index 3ab14d978..2fcfe458e 100644 --- a/infer/tests/codetoanalyze/objc/performance/NSString.m +++ b/infer/tests/codetoanalyze/objc/performance/NSString.m @@ -9,43 +9,48 @@ NSString* mId; NSCharacterSet* characterSet; -NSString* string_by_appending_same_string_linear_FN(NSString* s) { +NSString* string_by_appending_same_string_linear(NSString* s) { NSString* str = [s stringByAppendingString:@"me"]; + for (int i = 0; i < str.length; i++) { + } return str; } -NSString* string_by_appending_string_linear_FN(NSString* s, NSString* m) { +NSString* string_by_appending_string_linear(NSString* s, NSString* m) { NSString* str = [s stringByAppendingString:m]; + for (int i = 0; i < str.length; i++) { + } return str; } -NSUInteger rangeof_character_from_set_linear_FN(NSString* m) { +NSUInteger rangeof_character_from_set_linear(NSString* m) { return [m rangeOfString:@"_"].location; } -NSUInteger rangeof_string_quadratic_FN(NSString* m, NSString* n) { +NSUInteger rangeof_string_quadratic(NSString* m, NSString* n) { return [m rangeOfString:n].location; } -NSString* substring_from_index_linear_FN() { - NSUInteger index = rangeof_character_from_set_linear_FN(mId); - return [mId substringToIndex:index]; -} - NSString* has_prefix_constant() { NSString* s = @""; return [s hasPrefix:s] ? [s substringFromIndex:1] : s; } -void component_seperated_by_string_linear_FP(NSString* m) { +void component_seperated_by_char_linear(NSString* m) { NSArray* arrayOfComponents = [m componentsSeparatedByString:@","]; for (int i = 0; i < arrayOfComponents.count; i++) { } } -void call_component_separated_by_string_constant_FP() { +void component_seperated_by_string_linear(NSString* m, NSString* sep) { + NSArray* arrayOfComponents = [m componentsSeparatedByString:sep]; + for (int i = 0; i < arrayOfComponents.count; i++) { + } +} + +void call_component_separated_by_char_constant() { NSString* s = @"hello"; - component_seperated_by_string_linear_FP(s); + component_seperated_by_char_linear(s); } void init_with_bytes_linear_FP(const void* bytes, @@ -76,7 +81,7 @@ void call_init_with_string_constant_FP() { init_with_string_linear_FP(s); } -void substring_no_end_linear_FP(NSString* s, int x) { +void substring_no_end_linear(NSString* s, int x) { NSString* sub = [s substringFromIndex:x]; for (int i = 0; i < sub.length; i++) { } diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index e6acd3519..4b848478d 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -49,25 +49,27 @@ codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_constant, 4, OnUI codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_linear, 3 + 11 ⋅ array.length + 3 ⋅ (array.length + 1), OnUIThread:false, [{array.length + 1},Loop,{array.length},Loop] codetoanalyze/objc/performance/NSMutableDictionary.m, nsmutabledictionary_set_element_in_loop_linear_FN, 14, OnUIThread:false, [] -codetoanalyze/objc/performance/NSString.m, call_component_separated_by_string_constant_FP, ⊤, OnUIThread:false, [Call to component_seperated_by_string_linear_FP,Unbounded loop,Loop] +codetoanalyze/objc/performance/NSMutableString.m, nsmstring_append_string_constant, 14 + 3 ⋅ str.length.ub + 3 ⋅ (str.length.ub + 1), OnUIThread:false, [{str.length.ub + 1},Loop,{str.length.ub},Loop] +codetoanalyze/objc/performance/NSMutableString.m, nsmstring_append_string_linear, 5 + str2.length.ub + 3 ⋅ str1.length.ub + 3 ⋅ (str1.length.ub + 1), OnUIThread:false, [{str1.length.ub + 1},Loop,{str1.length.ub},Loop,{str2.length.ub},Modeled call to NSMutableString.appendString:] +codetoanalyze/objc/performance/NSString.m, call_component_separated_by_char_constant, 46, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, call_init_with_string_constant_FP, ⊤, OnUIThread:false, [Call to init_with_string_linear_FP,Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, component_seperated_by_string_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSString.m, component_seperated_by_char_linear, 6 + m.length.ub + 3 ⋅ (-1+max(2, m.length.ub)) + 3 ⋅ (max(2, m.length.ub)), OnUIThread:false, [{max(2, m.length.ub)},Loop,{-1+max(2, m.length.ub)},Loop,{m.length.ub},Modeled call to NSString.componentsSeparatedByString:] +codetoanalyze/objc/performance/NSString.m, component_seperated_by_string_linear, 6 + sep.length.ub × m.length.ub + 3 ⋅ (-1+max(2, m.length.ub)) + 3 ⋅ (max(2, m.length.ub)), OnUIThread:false, [{max(2, m.length.ub)},Loop,{-1+max(2, m.length.ub)},Loop,{m.length.ub},Modeled call to NSString.componentsSeparatedByString:,{sep.length.ub},Modeled call to NSString.componentsSeparatedByString:] codetoanalyze/objc/performance/NSString.m, has_prefix_constant, 13, OnUIThread:false, [] codetoanalyze/objc/performance/NSString.m, init_with_bytes_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, init_with_string_constant_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, init_with_string_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, rangeof_character_from_set_linear_FN, 6, OnUIThread:false, [] -codetoanalyze/objc/performance/NSString.m, rangeof_string_quadratic_FN, 6, OnUIThread:false, [] +codetoanalyze/objc/performance/NSString.m, rangeof_character_from_set_linear, 5 + m.length.ub, OnUIThread:false, [{m.length.ub},Modeled call to NSString.rangeOfString:] +codetoanalyze/objc/performance/NSString.m, rangeof_string_quadratic, 5 + n.length.ub × m.length.ub, OnUIThread:false, [{m.length.ub},Modeled call to NSString.rangeOfString:,{n.length.ub},Modeled call to NSString.rangeOfString:] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, string_by_appending_path_component_linear, 4 + path.length.ub + file.length.ub, OnUIThread:false, [{file.length.ub},Modeled call to NSString.stringByAppendingPathComponent:,{path.length.ub},Modeled call to NSString.stringByAppendingPathComponent:] -codetoanalyze/objc/performance/NSString.m, string_by_appending_same_string_linear_FN, 8, OnUIThread:false, [] -codetoanalyze/objc/performance/NSString.m, string_by_appending_string_linear_FN, 7, OnUIThread:false, [] +codetoanalyze/objc/performance/NSString.m, string_by_appending_same_string_linear, 11 + s.length.ub + 3 ⋅ (s.length.ub + 2) + 3 ⋅ (s.length.ub + 3), OnUIThread:false, [{s.length.ub + 3},Loop,{s.length.ub + 2},Loop,{s.length.ub},Modeled call to NSString.stringByAppendingString:] +codetoanalyze/objc/performance/NSString.m, string_by_appending_string_linear, 8 + m.length.ub + 3 ⋅ (m.length.ub + s.length.ub) + s.length.ub + 3 ⋅ (m.length.ub + s.length.ub + 1), OnUIThread:false, [{m.length.ub + s.length.ub + 1},Loop,{s.length.ub},Modeled call to NSString.stringByAppendingString:,{m.length.ub + s.length.ub},Loop,{m.length.ub},Modeled call to NSString.stringByAppendingString:] codetoanalyze/objc/performance/NSString.m, string_has_prefix_linear, 4 + str.length.ub, OnUIThread:false, [{str.length.ub},Modeled call to NSString.hasPrefix:] codetoanalyze/objc/performance/NSString.m, string_is_equal_to_string_linear, 4 + str1.length.ub, OnUIThread:false, [{str1.length.ub},Modeled call to NSString.isEqualToString:] codetoanalyze/objc/performance/NSString.m, string_length_linear, 3 + 3 ⋅ s.length.ub + 4 ⋅ (s.length.ub + 1), OnUIThread:false, [{s.length.ub + 1},Loop,{s.length.ub},Loop] codetoanalyze/objc/performance/NSString.m, string_with_utf8_string_linear, 7 + 3 ⋅ p->strlen.ub + p->strlen.ub + 4 ⋅ (p->strlen.ub + 1), OnUIThread:false, [{p->strlen.ub + 1},Loop,{p->strlen.ub},Modeled call to NSString.stringWithUTF8String:,{p->strlen.ub},Loop] -codetoanalyze/objc/performance/NSString.m, substring_from_index_linear_FN, 13, OnUIThread:false, [] -codetoanalyze/objc/performance/NSString.m, substring_no_end_linear_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop] +codetoanalyze/objc/performance/NSString.m, substring_no_end_linear, 10 + 6 ⋅ (-x + s.length.ub), OnUIThread:false, [{-x + s.length.ub},Loop] codetoanalyze/objc/performance/araii.m, Araii.buffer, 4, OnUIThread:false, [] codetoanalyze/objc/performance/araii.m, Araii.dealloc, 4, OnUIThread:false, [] codetoanalyze/objc/performance/araii.m, Araii.initWithBuffer, 15, OnUIThread:false, [] diff --git a/infer/tests/codetoanalyze/objc/performance/issues.exp b/infer/tests/codetoanalyze/objc/performance/issues.exp index a0cbe5e9f..9f1a91a66 100644 --- a/infer/tests/codetoanalyze/objc/performance/issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/issues.exp @@ -22,10 +22,7 @@ codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_reomove_constant, 5, B codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_constant_FP, 6, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/NSMutableArray.m, nsmarray_set_linear, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSNumber.intValue,Binary operation: ([-oo, +oo] + 1):signed32] -codetoanalyze/objc/performance/NSString.m, call_component_separated_by_string_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Call to component_seperated_by_string_linear_FP,Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, call_init_with_string_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Call to init_with_string_linear_FP,Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, component_seperated_by_string_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, component_seperated_by_string_linear_FP, 2, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/NSString.m, init_with_bytes_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, init_with_bytes_linear_FP, 6, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSString.initWithBytes:length:encoding:,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/NSString.m, init_with_string_constant_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] @@ -34,8 +31,6 @@ codetoanalyze/objc/performance/NSString.m, init_with_string_linear_FP, 0, INFINI codetoanalyze/objc/performance/NSString.m, init_with_string_linear_FP, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSString.initWithString:,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] codetoanalyze/objc/performance/NSString.m, replace_linear_FP, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSString.stringByReplacingOccurrencesOfString:withString:,Binary operation: ([0, +oo] + 1):signed32] -codetoanalyze/objc/performance/NSString.m, substring_no_end_linear_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop] -codetoanalyze/objc/performance/NSString.m, substring_no_end_linear_FP, 2, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: NSString.substringFromIndex:,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/block.m, objc_blockblock_multiply_array_linear_FN_1, 3, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Assignment,,Unknown value from: NSArray.nextObject,Assignment,Binary operation: ([-oo, +oo] + [-oo, +oo]):signed64] codetoanalyze/objc/performance/compound_loop_guard.m, compound_while, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] codetoanalyze/objc/performance/compound_loop_guard.m, nested_while_and_or_constant, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here]