Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1668,23 +1668,51 @@ if (const auto *InitList = dyn_cast(Init)) { // The array index has to be known. if (auto CI = R->getIndex().getAs()) { - int64_t i = CI->getValue().getSExtValue(); - // If it is known that the index is out of bounds, we can return - // an undefined value. - if (i < 0) + // If it is not an array, return Undef. + QualType T = VD->getType(); + const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T); + if (!CAT) return UndefinedVal(); - if (auto CAT = Ctx.getAsConstantArrayType(VD->getType())) - if (CAT->getSize().sle(i)) + // Support one-dimensional array. + // C++20 [expr.add] 7.6.6.4 (excerpt): + // If P points to an array element i of an array object x with n + // elements, where i < 0 or i > n, the behavior is undefined. + // Dereferencing is not allowed on the "one past the last + // element", when i == n. + // Example: + // const int arr[4] = {1, 2}; + // const int *ptr = arr; + // int x0 = ptr[0]; // 1 + // int x1 = ptr[1]; // 2 + // int x2 = ptr[2]; // 0 + // int x3 = ptr[3]; // 0 + // int x4 = ptr[4]; // UB + // TODO: Support multidimensional array. + const bool IsOneDimensionalArray = + !isa(CAT->getElementType()); + if (IsOneDimensionalArray) { + const llvm::APSInt &Idx = CI->getValue(); + const uint64_t I = static_cast(Idx.getExtValue()); + // Use `getZExtValue` because array extent can not be negative. + const uint64_t Extent = CAT->getSize().getZExtValue(); + // Check for `Idx < 0`, NOT for `I < 0`, because `Idx` CAN be + // negative, but `I` can NOT. + if (Idx < 0 || I >= Extent) return UndefinedVal(); - // If there is a list, but no init, it must be zero. - if (i >= InitList->getNumInits()) - return svalBuilder.makeZeroVal(R->getElementType()); + // C++20 [expr.add] 9.4.17.5 (excerpt): + // i-th array element is value-initialized for each k < i ≤ n, + // where k is an expression-list size and n is an array extent. + if (I >= InitList->getNumInits()) + return svalBuilder.makeZeroVal(R->getElementType()); - if (const Expr *ElemInit = InitList->getInit(i)) - if (Optional V = svalBuilder.getConstantVal(ElemInit)) + // Return a constant value, if it is presented. + // FIXME: Support other SVals. + const Expr *E = InitList->getInit(I); + if (Optional V = svalBuilder.getConstantVal(E)) return *V; + } } } } Index: clang/test/Analysis/initialization.c =================================================================== --- clang/test/Analysis/initialization.c +++ clang/test/Analysis/initialization.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.builtin,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-config eagerly-assume=false -analyzer-checker=core.uninitialized.Assign,debug.ExprInspection -verify %s void clang_analyzer_eval(int); @@ -26,3 +26,74 @@ clang_analyzer_eval(sm.a == 1); // expected-warning{{TRUE}} clang_analyzer_eval(sm.b == 0); // expected-warning{{TRUE}} } + +const int glob_arr1[6] = {[2] = 3, [0] = 1, [1] = 2, [3] = 4}; +void glob_array_index1() { + clang_analyzer_eval(glob_arr1[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[2] == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[3] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[4] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[5] == 0); // expected-warning{{TRUE}} +} + +void glob_array_index2() { + const int *ptr = glob_arr1; + clang_analyzer_eval(ptr[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[2] == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[3] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[4] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[5] == 0); // expected-warning{{TRUE}} +} + +void glob_invalid_index1() { + int x = -42; + int res = glob_arr1[x]; // expected-warning{{garbage or undefined}} +} + +void glob_invalid_index2() { + const int *ptr = glob_arr1; + int x = 42; + int res = ptr[x]; // expected-warning{{garbage or undefined}} +} + +// TODO: Support multidimensional array. +const int glob_arr2[3][3] = {[0][0] = 1, [1][1] = 5, [2][0] = 7}; +void glob_arr_index3() { + // FIXME: These all should be TRUE. + clang_analyzer_eval(glob_arr2[0][0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[0][1] == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[0][2] == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[1][0] == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[1][1] == 5); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[1][2] == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[2][0] == 7); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[2][1] == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(glob_arr2[2][2] == 0); // expected-warning{{UNKNOWN}} +} + +// TODO: Support multidimensional array. +void negative_index() { + int x = 2, y = -2; + // FIXME: Should be UNDEFINED. + clang_analyzer_eval(glob_arr2[x][y] == 5); // expected-warning{{UNKNOWN}} + x = 3; + y = -3; + // FIXME: Should be UNDEFINED. + clang_analyzer_eval(glob_arr2[x][y] == 7); // expected-warning{{UNKNOWN}} +} + +// TODO: Support multidimensional array. +void glob_invalid_index3() { + int x = -1, y = -1; + // FIXME: Should warn {{garbage or undefined}}. + int res = glob_arr2[x][y]; // no-warning +} + +// TODO: Support multidimensional array. +void glob_invalid_index4() { + int x = 3, y = 2; + // FIXME: Should warn {{garbage or undefined}}. + int res = glob_arr2[x][y]; // no-warning +} Index: clang/test/Analysis/initialization.cpp =================================================================== --- clang/test/Analysis/initialization.cpp +++ clang/test/Analysis/initialization.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.builtin,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-config eagerly-assume=false -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection,core.uninitialized.UndefReturn -verify %s void clang_analyzer_eval(int); @@ -18,3 +18,113 @@ // FIXME: Should recognize that it is 0. clang_analyzer_eval(arr[i][0]); // expected-warning{{UNKNOWN}} } + +int const glob_arr1[3] = {}; +void glob_array_index1() { + clang_analyzer_eval(glob_arr1[0] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[2] == 0); // expected-warning{{TRUE}} +} + +void glob_invalid_index1() { + const int *ptr = glob_arr1; + int idx = -42; + auto x = ptr[idx]; // expected-warning{{garbage or undefined}} +} + +int const glob_arr2[4] = {1, 2}; +void glob_ptr_index1() { + int const *ptr = glob_arr2; + clang_analyzer_eval(ptr[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[2] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[3] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[4] == 0); // expected-warning{{UNDEFINED}} +} + +void glob_invalid_index2() { + const int *ptr = glob_arr2; + int idx = 42; + auto x = ptr[idx]; // expected-warning{{garbage or undefined}} +} + +const float glob_arr3[] = { + 0.0000, 0.0235, 0.0470, 0.0706, 0.0941, 0.1176}; +float no_warn_garbage_value() { + return glob_arr3[0]; // no-warning (garbage or undefined) +} + +// TODO: Support multidimensional array. +int const glob_arr4[4][2] = {}; +void glob_array_index2() { + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr4[1][0] == 0); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr4[1][1] == 0); // expected-warning{{UNKNOWN}} +} + +// TODO: Support multidimensional array. +void glob_invalid_index3() { + int idx = -42; + // FIXME: Should warn {{garbage or undefined}}. + auto x = glob_arr4[1][idx]; // no-warning +} + +// TODO: Support multidimensional array. +void glob_invalid_index4() { + const int *ptr = glob_arr4[1]; + int idx = -42; + // FIXME: Should warn {{garbage or undefined}}. + auto x = ptr[idx]; // no-warning +} + +// TODO: Support multidimensional array. +int const glob_arr5[4][2] = {{1}, 3, 4, 5}; +void glob_array_index3() { + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[0][0] == 1); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[0][1] == 0); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[1][0] == 3); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[1][1] == 4); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[2][0] == 5); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[2][1] == 0); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[3][0] == 0); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(glob_arr5[3][1] == 0); // expected-warning{{UNKNOWN}} +} + +// TODO: Support multidimensional array. +void glob_ptr_index2() { + int const *ptr = glob_arr5[1]; + // FIXME: Should be TRUE. + clang_analyzer_eval(ptr[0] == 3); // expected-warning{{UNKNOWN}} + // FIXME: Should be TRUE. + clang_analyzer_eval(ptr[1] == 4); // expected-warning{{UNKNOWN}} + // FIXME: Should be UNDEFINED. + clang_analyzer_eval(ptr[2] == 5); // expected-warning{{UNKNOWN}} + // FIXME: Should be UNDEFINED. + clang_analyzer_eval(ptr[3] == 0); // expected-warning{{UNKNOWN}} + // FIXME: Should be UNDEFINED. + clang_analyzer_eval(ptr[4] == 0); // expected-warning{{UNKNOWN}} +} + +// TODO: Support multidimensional array. +void glob_invalid_index5() { + int idx = -42; + // FIXME: Should warn {{garbage or undefined}}. + auto x = glob_arr5[1][idx]; // no-warning +} + +// TODO: Support multidimensional array. +void glob_invalid_index6() { + int const *ptr = &glob_arr5[1][0]; + int idx = 42; + // FIXME: Should warn {{garbage or undefined}}. + auto x = ptr[idx]; // // no-warning +}