[analyzer] Retrieve a value from list initialization of constant array declaration in a global scope.

Summary: Fix the point that we didn't take into account array's dimension. Retrieve a value of global constant array by iterating through its initializer list.

Differential Revision: https://reviews.llvm.org/D104285

Fixes: https://bugs.llvm.org/show_bug.cgi?id=50604
This commit is contained in:
Denys Petrov 2021-09-21 14:34:37 +03:00
parent 5efafc3e65
commit 98a95d4844
3 changed files with 221 additions and 13 deletions

View File

@ -1668,23 +1668,50 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
// The array index has to be known.
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
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.
if (!isa<ConstantArrayType>(CAT->getElementType())) {
// One-dimensional array.
const llvm::APSInt &Idx = CI->getValue();
const auto I = static_cast<uint64_t>(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<SVal> V = svalBuilder.getConstantVal(ElemInit))
// Return a constant value, if it is presented.
// FIXME: Support other SVals.
const Expr *E = InitList->getInit(I);
if (Optional<SVal> V = svalBuilder.getConstantVal(E))
return *V;
}
}
}
}

View File

@ -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 @@ void multinit() {
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
}

View File

@ -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 @@ void arr2init() {
// 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
}