diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index edf6b7335..6f5b49191 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -522,10 +522,15 @@ module ArrObjCommon = struct Sem.eval_array_locs_length (deref_of model_env exp ~fn mem) mem - let size_exec exp ~fn model_env ~ret:(id, _) mem = - let arr_locs = deref_of model_env exp ~fn mem in - let mem = Dom.Mem.add_stack (Loc.of_id id) (Sem.eval_array_locs_length arr_locs mem) mem in - load_size_alias id arr_locs mem + let size_exec exp ~fn ({integer_type_widths} as model_env) ~ret:(id, _) mem = + let locs = Sem.eval integer_type_widths exp mem |> Dom.Val.get_all_locs in + match PowLoc.is_singleton_or_more locs with + | Singleton (Loc.Allocsite (Allocsite.LiteralString s)) -> + model_by_value (Dom.Val.of_int (String.length s)) id mem + | _ -> + let arr_locs = deref_of model_env exp ~fn mem in + let mem = Dom.Mem.add_stack (Loc.of_id id) (Sem.eval_array_locs_length arr_locs mem) mem in + load_size_alias id arr_locs mem let at arr_exp ~fn index_exp = @@ -1183,18 +1188,16 @@ module JavaString = struct {exec; check= no_check} - let copy_constructor_constant tgt_exp s = copy_constructor tgt_exp (Exp.Const (Const.Cstr s)) - - let empty_constructor tgt_exp = copy_constructor_constant tgt_exp "" + let empty_constructor tgt_exp = copy_constructor tgt_exp (Exp.Const (Const.Cstr "")) - (* We model Enum.name as returing a constant name, rather than getting real field names. We did - this because we couldn't think of any big gains from getting the real names. *) - let enum_name tgt_exp = - let {exec= constructor_exec} = copy_constructor_constant tgt_exp "EnumName" in - let exec ({integer_type_widths} as model_env) ~ret:((ret_id, _) as ret) mem = - let v = Sem.eval integer_type_widths tgt_exp mem in - constructor_exec model_env ~ret mem |> model_by_value v ret_id + (* We model Enum.name or Class.getCanonicalName as returning an arbitrary constant name, rather + than getting real names. We did this because we couldn't think of any big gains from getting + the real names in terms of analysis precision. *) + let inferbo_constant_string = + let constant_string_val = + Loc.of_allocsite (Allocsite.literal_string "__constant_string") |> Dom.Val.of_loc in + let exec _model_env ~ret:(ret_id, _) mem = model_by_value constant_string_val ret_id mem in {exec; check= no_check} @@ -1564,5 +1567,7 @@ module Call = struct ; +PatternMatch.implements_lang "Math" &:: "min" <>$ capt_exp $+ capt_exp $--> eval_binop ~f:(Itv.min_sem ~use_minmax_bound:true) - ; +PatternMatch.implements_lang "Enum" &:: "name" <>$ capt_exp $--> JavaString.enum_name ] + ; +PatternMatch.implements_lang "Enum" &:: "name" &::.*--> JavaString.inferbo_constant_string + ; +PatternMatch.implements_lang "Class" + &:: "getCanonicalName" &::.*--> JavaString.inferbo_constant_string ] end diff --git a/infer/tests/codetoanalyze/java/performance/StringTest.java b/infer/tests/codetoanalyze/java/performance/StringTest.java index fc05aec80..5392bd289 100644 --- a/infer/tests/codetoanalyze/java/performance/StringTest.java +++ b/infer/tests/codetoanalyze/java/performance/StringTest.java @@ -108,4 +108,8 @@ class StringTest { i = j; } } + + void class_get_canonical_name_constant(Integer a) { + for (int i = 0; i < a.getClass().getCanonicalName().length(); i++) {} + } } diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index 84816eaa6..d98d7d2f9 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -194,6 +194,8 @@ codetoanalyze/java/performance/PreconditionTest.java, PreconditionTest.checkNotN codetoanalyze/java/performance/StringBuilderTest.java, StringBuilderTest.append_linear(java.lang.String):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 25 + 5 ⋅ (s.length + 2) + 3 ⋅ (s.length + 3), O(s.length), degree = 1,{s.length + 3},call to void StringBuilderTest.new_linear(String),Loop at line 13,{s.length + 2},call to void StringBuilderTest.new_linear(String),Loop at line 13] codetoanalyze/java/performance/StringBuilderTest.java, StringBuilderTest.new_linear(java.lang.String):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 9 + 5 ⋅ s.length + 3 ⋅ (s.length + 1), O(s.length), degree = 1,{s.length + 1},Loop at line 13,{s.length},Loop at line 13] codetoanalyze/java/performance/StringTest.java, StringTest.byte_array_constructor_linear(byte[]):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 6 + 5 ⋅ data.length + 3 ⋅ (data.length + 1), O(data.length), degree = 1,{data.length + 1},Loop at line 55,{data.length},Loop at line 55] +codetoanalyze/java/performance/StringTest.java, StringTest.class_get_canonical_name_constant(java.lang.Integer):void, 1, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: Class Object.getClass(),Assignment,Array access: Offset: [-oo, +oo] Size: [0, +oo]] +codetoanalyze/java/performance/StringTest.java, StringTest.class_get_canonical_name_constant(java.lang.Integer):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 249, O(1), degree = 0] codetoanalyze/java/performance/StringTest.java, StringTest.index_substring_linear():java.lang.String, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 9 + this.mId.length, O(this.mId.length), degree = 1,{this.mId.length},call to int StringTest.indexof_linear(String),Modeled call to String.indexOf] codetoanalyze/java/performance/StringTest.java, StringTest.indexof_from_linear(java.lang.String,int):int, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 3 + (-j + m.length), O((-j + m.length)), degree = 1,{-j + m.length},Modeled call to String.indexOf] codetoanalyze/java/performance/StringTest.java, StringTest.indexof_linear(java.lang.String):int, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 2 + m.length, O(m.length), degree = 1,{m.length},Modeled call to String.indexOf]