diff --git a/infer/src/bufferoverrun/absLoc.ml b/infer/src/bufferoverrun/absLoc.ml index a2694f54c..66cbb8d08 100644 --- a/infer/src/bufferoverrun/absLoc.ml +++ b/infer/src/bufferoverrun/absLoc.ml @@ -21,6 +21,7 @@ module Allocsite = struct ; dimension: int ; represents_multiple_values: bool ; path: Symb.SymbolPath.partial option } + | LiteralString of string [@@deriving compare] let eq as1 as2 = @@ -35,7 +36,9 @@ module Allocsite = struct Boolean.False | Known {path= None}, Known {path= None} -> Boolean.of_bool ([%compare.equal: t] as1 as2) - | Known _, Symbol _ | Symbol _, Known _ -> + | LiteralString s1, LiteralString s2 -> + Boolean.of_bool (String.equal s1 s2) + | _, _ -> Boolean.False @@ -50,12 +53,16 @@ module Allocsite = struct F.fprintf fmt "%s-%d-%d-%d" proc_name node_hash inst_num dimension ; Option.iter path ~f:(fun path -> F.fprintf fmt "(%a)" (Symb.SymbolPath.pp_partial_paren ~paren:false) path ) + | LiteralString s -> + F.fprintf fmt "%S" s let pp = pp_paren ~paren:false let is_pretty = function Symbol _ | Known {path= Some _} -> true | _ -> false + let is_literal_string = function LiteralString s -> Some s | _ -> None + let to_string x = F.asprintf "%a" pp x let make : @@ -80,12 +87,21 @@ module Allocsite = struct let unknown = Unknown - let get_path = function Unknown -> None | Symbol path -> Some path | Known {path} -> path + let literal_string s = LiteralString s + + let get_path = function + | Unknown | LiteralString _ -> + None + | Symbol path -> + Some path + | Known {path} -> + path + let get_param_path = function | Symbol path -> Option.some_if (not (Symb.SymbolPath.represents_callsite_sound_partial path)) path - | Unknown | Known _ -> + | Unknown | Known _ | LiteralString _ -> None @@ -97,6 +113,8 @@ module Allocsite = struct | Known {path; represents_multiple_values} -> represents_multiple_values || Option.value_map path ~default:false ~f:Symb.SymbolPath.represents_multiple_values + | LiteralString _ -> + true end module Loc = struct @@ -182,6 +200,8 @@ module Loc = struct let is_field_of ~loc ~field_loc = match field_loc with Field (l, _) -> equal loc l | _ -> false + let is_literal_string = function Allocsite a -> Allocsite.is_literal_string a | _ -> None + let rec get_path = function | Var (LogicalVar _) -> None diff --git a/infer/src/bufferoverrun/bufferOverrunDomain.ml b/infer/src/bufferoverrun/bufferOverrunDomain.ml index 548d4d0c6..2d3b884f2 100644 --- a/infer/src/bufferoverrun/bufferOverrunDomain.ml +++ b/infer/src/bufferoverrun/bufferOverrunDomain.ml @@ -164,6 +164,20 @@ module Val = struct ; traces } + let of_literal_string : Typ.IntegerWidths.t -> string -> t = + fun integer_type_widths s -> + let allocsite = Allocsite.literal_string s in + let stride = Some (integer_type_widths.char_width / 8) in + let offset = Itv.zero in + let size = Itv.of_int (String.length s + 1) in + of_c_array_alloc allocsite ~stride ~offset ~size ~traces:TraceSet.empty + + + let deref_of_literal_string s = + let max_char = String.fold s ~init:0 ~f:(fun acc c -> max acc (Char.to_int c)) in + of_itv (Itv.set_lb_zero (Itv.of_int max_char)) + + let modify_itv : Itv.t -> t -> t = fun i x -> {x with itv= i} let unknown_bit : t -> t = fun x -> {x with itv= Itv.top; sym= Relation.Sym.top} @@ -470,20 +484,24 @@ module Val = struct let on_demand : default:t -> OndemandEnv.t -> Loc.t -> t = fun ~default {tenv; typ_of_param_path; may_last_field; entry_location; integer_type_widths} l -> - match Loc.get_path l with - | None -> - L.d_printfln_escaped "Val.on_demand for %a -> no path" Loc.pp l ; - default - | Some path -> ( - match typ_of_param_path path with + match Loc.is_literal_string l with + | Some s -> + deref_of_literal_string s + | None -> ( + match Loc.get_path l with | None -> - L.d_printfln_escaped "Val.on_demand for %a -> no type" Loc.pp l ; + L.d_printfln_escaped "Val.on_demand for %a -> no path" Loc.pp l ; default - | Some typ -> - L.d_printfln_escaped "Val.on_demand for %a" Loc.pp l ; - let may_last_field = may_last_field path in - let path = OndemandEnv.canonical_path typ_of_param_path path in - of_path tenv ~may_last_field integer_type_widths entry_location typ path ) + | Some path -> ( + match typ_of_param_path path with + | None -> + L.d_printfln_escaped "Val.on_demand for %a -> no type" Loc.pp l ; + default + | Some typ -> + L.d_printfln_escaped "Val.on_demand for %a" Loc.pp l ; + let may_last_field = may_last_field path in + let path = OndemandEnv.canonical_path typ_of_param_path path in + of_path tenv ~may_last_field integer_type_widths entry_location typ path ) ) module Itv = struct diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index ba781fda0..a1999ffba 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -23,8 +23,6 @@ type check_fun = model_env -> Dom.Mem.t -> PO.ConditionSet.checked_t -> PO.Condi type model = {exec: exec_fun; check: check_fun} -let no_exec _model_env ~ret:_ mem = mem - let no_check _model_env _mem cond_set = cond_set let no_model = @@ -179,9 +177,19 @@ let inferbo_set_size e1 e2 = {exec; check} -let model_by_value value (id, _) mem = Dom.Mem.add_stack (Loc.of_id id) value mem +let variable_initialization (e, typ) = + let exec model_env ~ret:_ mem = + match e with + | Exp.Lvar x when Pvar.is_global x -> + let mem, _ = BoUtils.Exec.decl_local model_env (mem, 1) (Loc.of_pvar x, typ) in + mem + | _ -> + mem + in + {exec; check= no_check} + -let nop = {exec= no_exec; check= no_check} +let model_by_value value (id, _) mem = Dom.Mem.add_stack (Loc.of_id id) value mem let by_value = let exec ~value _ ~ret mem = model_by_value value ret mem in @@ -524,7 +532,7 @@ module Call = struct make_dispatcher [ -"__inferbo_min" <>$ capt_exp $+ capt_exp $!--> inferbo_min ; -"__inferbo_set_size" <>$ capt_exp $+ capt_exp $!--> inferbo_set_size - ; -"__variable_initialization" <>--> nop + ; -"__variable_initialization" <>$ capt_arg $!--> variable_initialization ; -"__exit" <>--> bottom ; -"exit" <>--> bottom ; -"fgetc" <>--> by_value Dom.Val.Itv.m1_255 diff --git a/infer/src/bufferoverrun/bufferOverrunSemantics.ml b/infer/src/bufferoverrun/bufferOverrunSemantics.ml index 93a8434d0..f5628824d 100644 --- a/infer/src/bufferoverrun/bufferOverrunSemantics.ml +++ b/infer/src/bufferoverrun/bufferOverrunSemantics.ml @@ -12,11 +12,14 @@ open AbsLoc open! AbstractDomain.Types open BufferOverrunDomain -let eval_const : Const.t -> Val.t = function +let eval_const : Typ.IntegerWidths.t -> Const.t -> Val.t = + fun integer_type_widths -> function | Const.Cint intlit -> Val.of_big_int (IntLit.to_big_int intlit) | Const.Cfloat f -> f |> int_of_float |> Val.of_int + | Const.Cstr s -> + Val.of_literal_string integer_type_widths s | _ -> Val.Itv.top @@ -155,7 +158,7 @@ let rec eval : Typ.IntegerWidths.t -> Exp.t -> Mem.t -> Val.t = | Exp.BinOp (bop, e1, e2) -> eval_binop integer_type_widths bop e1 e2 mem | Exp.Const c -> - eval_const c + eval_const integer_type_widths c | Exp.Cast (t, e) -> let v = eval integer_type_widths e mem in let v = set_array_stride integer_type_widths t v in diff --git a/infer/src/bufferoverrun/itv.ml b/infer/src/bufferoverrun/itv.ml index 98a868b0c..72a443253 100644 --- a/infer/src/bufferoverrun/itv.ml +++ b/infer/src/bufferoverrun/itv.ml @@ -123,6 +123,8 @@ module ItvPure = struct let pos = (Bound.one, Bound.PInf) + let set_lb_zero (_, ub) = (Bound.zero, ub) + let top = (Bound.MInf, Bound.PInf) let zero = of_bound Bound.zero @@ -550,6 +552,8 @@ let incr = plus one let decr x = minus x one +let set_lb_zero = lift1 ItvPure.set_lb_zero + let get_iterator_itv : t -> t = lift1 ItvPure.get_iterator_itv let is_const : t -> Z.t option = bind1zo ItvPure.is_const diff --git a/infer/src/bufferoverrun/itv.mli b/infer/src/bufferoverrun/itv.mli index c15e50c8e..f4e0b0aff 100644 --- a/infer/src/bufferoverrun/itv.mli +++ b/infer/src/bufferoverrun/itv.mli @@ -152,6 +152,8 @@ val decr : t -> t val incr : t -> t +val set_lb_zero : t -> t + val neg : t -> t val normalize : t -> t diff --git a/infer/tests/codetoanalyze/c/bufferoverrun/array_content.c b/infer/tests/codetoanalyze/c/bufferoverrun/array_content.c index 4970e22b2..11e30a3a0 100644 --- a/infer/tests/codetoanalyze/c/bufferoverrun/array_content.c +++ b/infer/tests/codetoanalyze/c/bufferoverrun/array_content.c @@ -123,3 +123,44 @@ void literal_string_bad() { } } } + +void literal_string2_Good() { + int a[1]; + char s[] = "hello"; + for (int i = 0; i < 5; i++) { + if (s[i] > 'o') { + a[s[i]] = 0; + } + } +} + +void literal_string2_Bad_FN() { + int a[1]; + char s[] = "hello"; + for (int i = 0; i < 5; i++) { + if (s[i] > 'n') { + a[s[i]] = 0; + } + } +} + +void literal_string_parameter(char* s) { + int a[112]; // 'o' is 111, 'p' is 112 + a[s[0]] = 0; +} + +void call_literal_string_parameter1_Good() { + char* s = "hello"; + literal_string_parameter(s); +} + +void call_literal_string_parameter1_Bad() { + char* s = "hellp"; + literal_string_parameter(s); +} + +void call_literal_string_parameter2_Good() { + literal_string_parameter("hello"); +} + +void call_literal_string_parameter2_Bad() { literal_string_parameter("hellp"); } diff --git a/infer/tests/codetoanalyze/c/bufferoverrun/global.c b/infer/tests/codetoanalyze/c/bufferoverrun/global.c index 3a8b7f813..cbd14a84b 100644 --- a/infer/tests/codetoanalyze/c/bufferoverrun/global.c +++ b/infer/tests/codetoanalyze/c/bufferoverrun/global.c @@ -53,3 +53,5 @@ static void copyfilter_Good_FP(const char* s, const char* z, int b) { } } } + +static const char* global_string_array[] = {"a", "b", "c", "d", "e", "f"}; diff --git a/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp b/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp index 4c57ed0af..cf21877f9 100644 --- a/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp +++ b/infer/tests/codetoanalyze/c/bufferoverrun/issues.exp @@ -47,6 +47,8 @@ codetoanalyze/c/bufferoverrun/arith.c, unused_integer_underflow_Good_FP, 2, INTE codetoanalyze/c/bufferoverrun/arith.c, use_int64_max_Bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Assignment,,Array declaration,Array access: Offset: 15 Size: 10] codetoanalyze/c/bufferoverrun/arith.c, use_uint64_max_Bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Assignment,,Array declaration,Array access: Offset: 15 Size: 10] codetoanalyze/c/bufferoverrun/array_content.c, array_min_index_from_one_FP, 3, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] +codetoanalyze/c/bufferoverrun/array_content.c, call_literal_string_parameter1_Bad, 2, BUFFER_OVERRUN_L2, no_bucket, ERROR, [Call,,Parameter `*s`,,Array declaration,Array access: Offset: [0, 112] Size: 112 by call to `literal_string_parameter` ] +codetoanalyze/c/bufferoverrun/array_content.c, call_literal_string_parameter2_Bad, 0, BUFFER_OVERRUN_L2, no_bucket, ERROR, [Call,,Parameter `*s`,,Array declaration,Array access: Offset: [0, 112] Size: 112 by call to `literal_string_parameter` ] codetoanalyze/c/bufferoverrun/array_content.c, check_sorted_arr10_good_FP, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] codetoanalyze/c/bufferoverrun/array_content.c, check_sorted_arr_good_FP, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] codetoanalyze/c/bufferoverrun/array_content.c, check_sorted_ptr_good_FP, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] diff --git a/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp b/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp index 35356dc12..e747b2fde 100644 --- a/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp +++ b/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp @@ -81,6 +81,7 @@ codetoanalyze/cpp/bufferoverrun/std_array.cpp, new_int3_Bad, 3, INTEGER_OVERFLOW codetoanalyze/cpp/bufferoverrun/std_array.cpp, normal_array_bo, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 42 Size: 42] codetoanalyze/cpp/bufferoverrun/std_array.cpp, std_array_bo_Bad, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 42 Size: 42] codetoanalyze/cpp/bufferoverrun/std_string.cpp, call_length4_1_Bad, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Array declaration,Call,,Array declaration,Array access: Offset: 11 Size: 10 by call to `length4` ] +codetoanalyze/cpp/bufferoverrun/std_string.cpp, call_length4_2_Bad, 0, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Call,,Array declaration,Array access: Offset: 11 Size: 10 by call to `length4` ] codetoanalyze/cpp/bufferoverrun/std_string.cpp, compare_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/cpp/bufferoverrun/std_string.cpp, compare_Good_FP, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/cpp/bufferoverrun/std_string.cpp, empty_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 10 Size: 10] diff --git a/infer/tests/codetoanalyze/cpp/bufferoverrun/std_string.cpp b/infer/tests/codetoanalyze/cpp/bufferoverrun/std_string.cpp index fbca1f4b2..3c248f852 100644 --- a/infer/tests/codetoanalyze/cpp/bufferoverrun/std_string.cpp +++ b/infer/tests/codetoanalyze/cpp/bufferoverrun/std_string.cpp @@ -161,7 +161,7 @@ void call_length4_1_Bad() { void call_length4_2_Good() { length4("hello"); } -void call_length4_2_Bad_FN() { length4("hellohello"); } +void call_length4_2_Bad() { length4("hellohello"); } void size_Good() { std::string s("hello");