diff --git a/infer/src/bufferoverrun/bufferOverrunModels.ml b/infer/src/bufferoverrun/bufferOverrunModels.ml index 8d65a45a9..89143cebb 100644 --- a/infer/src/bufferoverrun/bufferOverrunModels.ml +++ b/infer/src/bufferoverrun/bufferOverrunModels.ml @@ -643,6 +643,53 @@ module StdBasicString = struct {exec; check= no_check} end +module StdVector = struct + (* The (3) constructor in https://en.cppreference.com/w/cpp/container/vector/vector *) + let constructor vec_exp size_exp = + let {exec= malloc_exec; check} = malloc ~can_be_zero:true size_exp in + let exec ({pname; node_hash; integer_type_widths; location} as model_env) ~ret:((id, _) as ret) + mem = + let mem = malloc_exec model_env ~ret mem in + let vec_locs = Sem.eval_locs vec_exp mem in + let deref_of_vec = + Allocsite.make pname ~node_hash ~inst_num:1 ~dimension:1 ~path:None + ~represents_multiple_values:false + |> Loc.of_allocsite + in + let array_v = + Sem.eval integer_type_widths (Exp.Var id) mem + |> Dom.Val.add_assign_trace_elem location vec_locs + in + mem + |> Dom.Mem.update_mem vec_locs (Dom.Val.of_loc deref_of_vec) + |> Dom.Mem.add_heap deref_of_vec array_v + in + {exec; check} + + + let at vec_exp index_exp = + let exec {pname; location} ~ret:(id, _) mem = + let deref_of_vec = Dom.Mem.find_set (Sem.eval_locs vec_exp mem) mem in + let array_v = + let locs = Dom.Val.get_all_locs deref_of_vec in + if PowLoc.is_bot locs then Dom.Val.unknown_from ~callee_pname:(Some pname) ~location + else Dom.Mem.find_set locs mem + in + Dom.Mem.add_stack (Loc.of_id id) array_v mem + and check {location; integer_type_widths} mem cond_set = + let idx = Sem.eval integer_type_widths index_exp mem in + let arr = + let deref_of_vec = Dom.Mem.find_set (Sem.eval_locs vec_exp mem) mem in + Dom.Mem.find_set (Dom.Val.get_all_locs deref_of_vec) mem + in + let relation = Dom.Mem.get_relation mem in + let latest_prune = Dom.Mem.get_latest_prune mem in + BoUtils.Check.array_access ~arr ~idx ~idx_sym_exp:None ~relation ~is_plus:true + ~last_included:false ~latest_prune location cond_set + in + {exec; check} +end + (* Java's Collections are represented like arrays. But we don't care about the elements. - when they are constructed, we set the size to 0 - each time we add an element, we increase the length of the array @@ -956,6 +1003,10 @@ module Call = struct $+ any_arg_of_typ (-"std" &:: "basic_string") $--> by_value Dom.Val.Itv.unknown_bool ; -"std" &:: "basic_string" &::.*--> no_model + ; -"std" &:: "vector" < any_typ &+ any_typ >:: "vector" $ capt_exp $+ capt_exp + $--> StdVector.constructor + ; -"std" &:: "vector" < any_typ &+ any_typ >:: "operator[]" $ capt_exp $+ capt_exp + $--> StdVector.at ; +PatternMatch.implements_collection &:: "" <>$ capt_var_exn $+ capt_exp $--> Collection.init (* model sets as lists *) diff --git a/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp b/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp index 9457b5d95..bc219be71 100644 --- a/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp +++ b/infer/tests/codetoanalyze/cpp/bufferoverrun/issues.exp @@ -107,19 +107,16 @@ codetoanalyze/cpp/bufferoverrun/this.cpp, CThis::access_Bad, 2, BUFFER_OVERRUN_L codetoanalyze/cpp/bufferoverrun/trivial.cpp, trivial, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_assert_Good, 6, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_assert_Good_2, 6, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] -codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_constructor_Good, 2, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_data_Good, 3, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::data,Assignment,Array access: Offset: [-oo, +oo] (⇐ [-oo, +oo] + 4) Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_push_back_Good, 3, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_reserve_Good, 4, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] -codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access, 2, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access2, 6, INTEGER_OVERFLOW_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::size,Binary operation: ([0, +oo] + 1):unsigned32] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access2, 7, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::size,,Unknown value from: std::vector>::size,Array declaration,Array access: Offset: [0, +oo] Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access3_Good, 3, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] -codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access4, 2, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] -codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access5, 3, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] +codetoanalyze/cpp/bufferoverrun/vector.cpp, FP_safe_access6, 2, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::empty,,Array declaration,Assignment,Array access: Offset: [-oo, +oo] Size: 2] codetoanalyze/cpp/bufferoverrun/vector.cpp, assert_Bad, 6, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] -codetoanalyze/cpp/bufferoverrun/vector.cpp, data_Bad, 4, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],,Unknown value from: std::vector>::data,Assignment,Array access: Offset: [-oo, +oo] (⇐ [-oo, +oo] + [-oo, +oo]) Size: [0, +oo]] -codetoanalyze/cpp/bufferoverrun/vector.cpp, out_of_bound_Bad, 2, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] +codetoanalyze/cpp/bufferoverrun/vector.cpp, constructor_Bad, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [,Array declaration,Assignment,Array access: Offset: 3 Size: 1] +codetoanalyze/cpp/bufferoverrun/vector.cpp, data_Bad, 4, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::data,Assignment,Array access: Offset: [-oo, +oo] (⇐ [-oo, +oo] + [-oo, +oo]) Size: [0, +oo]] codetoanalyze/cpp/bufferoverrun/vector.cpp, precise_subst_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Array declaration,Call,Parameter `*init`,Assignment,Call,Parameter `*__param_0->a`,Assignment,Call,,Parameter `count`,Call,Parameter `idx`,Assignment,Array access: Offset: -1 Size: 10 by call to `access_minus_one` ] codetoanalyze/cpp/bufferoverrun/vector.cpp, precise_subst_Good_FP, 3, BUFFER_OVERRUN_L3, no_bucket, ERROR, [Array declaration,Call,Parameter `*init`,Assignment,Call,Parameter `*__param_0->a`,Assignment,Call,,Parameter `count`,Call,Parameter `idx`,Assignment,Array access: Offset: [-1, 0] Size: 10 by call to `access_minus_one` ] codetoanalyze/cpp/bufferoverrun/vector.cpp, push_back_Bad, 3, BUFFER_OVERRUN_U5, no_bucket, ERROR, [,Unknown value from: std::vector>::operator[],Array access: Offset: [-oo, +oo] Size: [0, +oo]] diff --git a/infer/tests/codetoanalyze/cpp/bufferoverrun/vector.cpp b/infer/tests/codetoanalyze/cpp/bufferoverrun/vector.cpp index 6453a1676..97d9b4d68 100644 --- a/infer/tests/codetoanalyze/cpp/bufferoverrun/vector.cpp +++ b/infer/tests/codetoanalyze/cpp/bufferoverrun/vector.cpp @@ -8,16 +8,21 @@ #include #include -void out_of_bound_Bad(std::vector v) { +void FN_out_of_bound_Bad(std::vector v) { unsigned int n = v.size(); v[n] = 1; } -void FP_constructor_Good() { +void constructor_Good() { std::vector v(1); v[0] = 2; } +void constructor_Bad() { + std::vector v(1); + v[3] = 2; +} + void FP_push_back_Good() { std::vector v; v.push_back(1); @@ -43,7 +48,7 @@ void reserve_Bad() { v[0] = 2; } -void FP_safe_access(std::vector v) { +void safe_access(std::vector v) { if (v.size() >= 10) { v[9] = 1; } @@ -51,7 +56,7 @@ void FP_safe_access(std::vector v) { void call_safe_access_Good() { std::vector v(5, 0); - FP_safe_access(v); + safe_access(v); } void FP_safe_access2(std::vector v) { @@ -77,7 +82,7 @@ void FP_safe_access3_Good() { } } -void FP_safe_access4(std::vector v) { +void safe_access4(std::vector v) { if (!v.empty()) { v[0] = 1; } @@ -85,10 +90,10 @@ void FP_safe_access4(std::vector v) { void call_safe_access4_Good() { std::vector v; - FP_safe_access4(v); + safe_access4(v); } -void FP_safe_access5(std::vector v) { +void safe_access5(std::vector v) { if (v.empty()) { } else { v[0] = 1; @@ -97,17 +102,17 @@ void FP_safe_access5(std::vector v) { void call_safe_access5_Good() { std::vector v; - FP_safe_access5(v); + safe_access5(v); } -void safe_access6(std::vector v) { +void FP_safe_access6(std::vector v) { std::vector v2(2); v2[v.empty()]; } void call_safe_access6_Good() { std::vector v; - safe_access6(v); + FP_safe_access6(v); } void FP_data_Good() {