Skip to content

Commit 98a95d4

Browse files
committed
[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
1 parent 5efafc3 commit 98a95d4

File tree

3 files changed

+221
-13
lines changed

3 files changed

+221
-13
lines changed

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,23 +1668,50 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
16681668
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
16691669
// The array index has to be known.
16701670
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
1671-
int64_t i = CI->getValue().getSExtValue();
1672-
// If it is known that the index is out of bounds, we can return
1673-
// an undefined value.
1674-
if (i < 0)
1671+
// If it is not an array, return Undef.
1672+
QualType T = VD->getType();
1673+
const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T);
1674+
if (!CAT)
16751675
return UndefinedVal();
16761676

1677-
if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
1678-
if (CAT->getSize().sle(i))
1677+
// Support one-dimensional array.
1678+
// C++20 [expr.add] 7.6.6.4 (excerpt):
1679+
// If P points to an array element i of an array object x with n
1680+
// elements, where i < 0 or i > n, the behavior is undefined.
1681+
// Dereferencing is not allowed on the "one past the last
1682+
// element", when i == n.
1683+
// Example:
1684+
// const int arr[4] = {1, 2};
1685+
// const int *ptr = arr;
1686+
// int x0 = ptr[0]; // 1
1687+
// int x1 = ptr[1]; // 2
1688+
// int x2 = ptr[2]; // 0
1689+
// int x3 = ptr[3]; // 0
1690+
// int x4 = ptr[4]; // UB
1691+
// TODO: Support multidimensional array.
1692+
if (!isa<ConstantArrayType>(CAT->getElementType())) {
1693+
// One-dimensional array.
1694+
const llvm::APSInt &Idx = CI->getValue();
1695+
const auto I = static_cast<uint64_t>(Idx.getExtValue());
1696+
// Use `getZExtValue` because array extent can not be negative.
1697+
const uint64_t Extent = CAT->getSize().getZExtValue();
1698+
// Check for `Idx < 0`, NOT for `I < 0`, because `Idx` CAN be
1699+
// negative, but `I` can NOT.
1700+
if (Idx < 0 || I >= Extent)
16791701
return UndefinedVal();
16801702

1681-
// If there is a list, but no init, it must be zero.
1682-
if (i >= InitList->getNumInits())
1683-
return svalBuilder.makeZeroVal(R->getElementType());
1703+
// C++20 [expr.add] 9.4.17.5 (excerpt):
1704+
// i-th array element is value-initialized for each k < i ≤ n,
1705+
// where k is an expression-list size and n is an array extent.
1706+
if (I >= InitList->getNumInits())
1707+
return svalBuilder.makeZeroVal(R->getElementType());
16841708

1685-
if (const Expr *ElemInit = InitList->getInit(i))
1686-
if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
1709+
// Return a constant value, if it is presented.
1710+
// FIXME: Support other SVals.
1711+
const Expr *E = InitList->getInit(I);
1712+
if (Optional<SVal> V = svalBuilder.getConstantVal(E))
16871713
return *V;
1714+
}
16881715
}
16891716
}
16901717
}

clang/test/Analysis/initialization.c

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.builtin,debug.ExprInspection -verify %s
1+
// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-config eagerly-assume=false -analyzer-checker=core.uninitialized.Assign,debug.ExprInspection -verify %s
22

33
void clang_analyzer_eval(int);
44

@@ -26,3 +26,74 @@ void multinit() {
2626
clang_analyzer_eval(sm.a == 1); // expected-warning{{TRUE}}
2727
clang_analyzer_eval(sm.b == 0); // expected-warning{{TRUE}}
2828
}
29+
30+
const int glob_arr1[6] = {[2] = 3, [0] = 1, [1] = 2, [3] = 4};
31+
void glob_array_index1() {
32+
clang_analyzer_eval(glob_arr1[0] == 1); // expected-warning{{TRUE}}
33+
clang_analyzer_eval(glob_arr1[1] == 2); // expected-warning{{TRUE}}
34+
clang_analyzer_eval(glob_arr1[2] == 3); // expected-warning{{TRUE}}
35+
clang_analyzer_eval(glob_arr1[3] == 4); // expected-warning{{TRUE}}
36+
clang_analyzer_eval(glob_arr1[4] == 0); // expected-warning{{TRUE}}
37+
clang_analyzer_eval(glob_arr1[5] == 0); // expected-warning{{TRUE}}
38+
}
39+
40+
void glob_array_index2() {
41+
const int *ptr = glob_arr1;
42+
clang_analyzer_eval(ptr[0] == 1); // expected-warning{{TRUE}}
43+
clang_analyzer_eval(ptr[1] == 2); // expected-warning{{TRUE}}
44+
clang_analyzer_eval(ptr[2] == 3); // expected-warning{{TRUE}}
45+
clang_analyzer_eval(ptr[3] == 4); // expected-warning{{TRUE}}
46+
clang_analyzer_eval(ptr[4] == 0); // expected-warning{{TRUE}}
47+
clang_analyzer_eval(ptr[5] == 0); // expected-warning{{TRUE}}
48+
}
49+
50+
void glob_invalid_index1() {
51+
int x = -42;
52+
int res = glob_arr1[x]; // expected-warning{{garbage or undefined}}
53+
}
54+
55+
void glob_invalid_index2() {
56+
const int *ptr = glob_arr1;
57+
int x = 42;
58+
int res = ptr[x]; // expected-warning{{garbage or undefined}}
59+
}
60+
61+
// TODO: Support multidimensional array.
62+
const int glob_arr2[3][3] = {[0][0] = 1, [1][1] = 5, [2][0] = 7};
63+
void glob_arr_index3() {
64+
// FIXME: These all should be TRUE.
65+
clang_analyzer_eval(glob_arr2[0][0] == 1); // expected-warning{{UNKNOWN}}
66+
clang_analyzer_eval(glob_arr2[0][1] == 0); // expected-warning{{UNKNOWN}}
67+
clang_analyzer_eval(glob_arr2[0][2] == 0); // expected-warning{{UNKNOWN}}
68+
clang_analyzer_eval(glob_arr2[1][0] == 0); // expected-warning{{UNKNOWN}}
69+
clang_analyzer_eval(glob_arr2[1][1] == 5); // expected-warning{{UNKNOWN}}
70+
clang_analyzer_eval(glob_arr2[1][2] == 0); // expected-warning{{UNKNOWN}}
71+
clang_analyzer_eval(glob_arr2[2][0] == 7); // expected-warning{{UNKNOWN}}
72+
clang_analyzer_eval(glob_arr2[2][1] == 0); // expected-warning{{UNKNOWN}}
73+
clang_analyzer_eval(glob_arr2[2][2] == 0); // expected-warning{{UNKNOWN}}
74+
}
75+
76+
// TODO: Support multidimensional array.
77+
void negative_index() {
78+
int x = 2, y = -2;
79+
// FIXME: Should be UNDEFINED.
80+
clang_analyzer_eval(glob_arr2[x][y] == 5); // expected-warning{{UNKNOWN}}
81+
x = 3;
82+
y = -3;
83+
// FIXME: Should be UNDEFINED.
84+
clang_analyzer_eval(glob_arr2[x][y] == 7); // expected-warning{{UNKNOWN}}
85+
}
86+
87+
// TODO: Support multidimensional array.
88+
void glob_invalid_index3() {
89+
int x = -1, y = -1;
90+
// FIXME: Should warn {{garbage or undefined}}.
91+
int res = glob_arr2[x][y]; // no-warning
92+
}
93+
94+
// TODO: Support multidimensional array.
95+
void glob_invalid_index4() {
96+
int x = 3, y = 2;
97+
// FIXME: Should warn {{garbage or undefined}}.
98+
int res = glob_arr2[x][y]; // no-warning
99+
}

clang/test/Analysis/initialization.cpp

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.builtin,debug.ExprInspection -verify %s
1+
// 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
22

33
void clang_analyzer_eval(int);
44

@@ -18,3 +18,113 @@ void arr2init() {
1818
// FIXME: Should recognize that it is 0.
1919
clang_analyzer_eval(arr[i][0]); // expected-warning{{UNKNOWN}}
2020
}
21+
22+
int const glob_arr1[3] = {};
23+
void glob_array_index1() {
24+
clang_analyzer_eval(glob_arr1[0] == 0); // expected-warning{{TRUE}}
25+
clang_analyzer_eval(glob_arr1[1] == 0); // expected-warning{{TRUE}}
26+
clang_analyzer_eval(glob_arr1[2] == 0); // expected-warning{{TRUE}}
27+
}
28+
29+
void glob_invalid_index1() {
30+
const int *ptr = glob_arr1;
31+
int idx = -42;
32+
auto x = ptr[idx]; // expected-warning{{garbage or undefined}}
33+
}
34+
35+
int const glob_arr2[4] = {1, 2};
36+
void glob_ptr_index1() {
37+
int const *ptr = glob_arr2;
38+
clang_analyzer_eval(ptr[0] == 1); // expected-warning{{TRUE}}
39+
clang_analyzer_eval(ptr[1] == 2); // expected-warning{{TRUE}}
40+
clang_analyzer_eval(ptr[2] == 0); // expected-warning{{TRUE}}
41+
clang_analyzer_eval(ptr[3] == 0); // expected-warning{{TRUE}}
42+
clang_analyzer_eval(ptr[4] == 0); // expected-warning{{UNDEFINED}}
43+
}
44+
45+
void glob_invalid_index2() {
46+
const int *ptr = glob_arr2;
47+
int idx = 42;
48+
auto x = ptr[idx]; // expected-warning{{garbage or undefined}}
49+
}
50+
51+
const float glob_arr3[] = {
52+
0.0000, 0.0235, 0.0470, 0.0706, 0.0941, 0.1176};
53+
float no_warn_garbage_value() {
54+
return glob_arr3[0]; // no-warning (garbage or undefined)
55+
}
56+
57+
// TODO: Support multidimensional array.
58+
int const glob_arr4[4][2] = {};
59+
void glob_array_index2() {
60+
// FIXME: Should be TRUE.
61+
clang_analyzer_eval(glob_arr4[1][0] == 0); // expected-warning{{UNKNOWN}}
62+
// FIXME: Should be TRUE.
63+
clang_analyzer_eval(glob_arr4[1][1] == 0); // expected-warning{{UNKNOWN}}
64+
}
65+
66+
// TODO: Support multidimensional array.
67+
void glob_invalid_index3() {
68+
int idx = -42;
69+
// FIXME: Should warn {{garbage or undefined}}.
70+
auto x = glob_arr4[1][idx]; // no-warning
71+
}
72+
73+
// TODO: Support multidimensional array.
74+
void glob_invalid_index4() {
75+
const int *ptr = glob_arr4[1];
76+
int idx = -42;
77+
// FIXME: Should warn {{garbage or undefined}}.
78+
auto x = ptr[idx]; // no-warning
79+
}
80+
81+
// TODO: Support multidimensional array.
82+
int const glob_arr5[4][2] = {{1}, 3, 4, 5};
83+
void glob_array_index3() {
84+
// FIXME: Should be TRUE.
85+
clang_analyzer_eval(glob_arr5[0][0] == 1); // expected-warning{{UNKNOWN}}
86+
// FIXME: Should be TRUE.
87+
clang_analyzer_eval(glob_arr5[0][1] == 0); // expected-warning{{UNKNOWN}}
88+
// FIXME: Should be TRUE.
89+
clang_analyzer_eval(glob_arr5[1][0] == 3); // expected-warning{{UNKNOWN}}
90+
// FIXME: Should be TRUE.
91+
clang_analyzer_eval(glob_arr5[1][1] == 4); // expected-warning{{UNKNOWN}}
92+
// FIXME: Should be TRUE.
93+
clang_analyzer_eval(glob_arr5[2][0] == 5); // expected-warning{{UNKNOWN}}
94+
// FIXME: Should be TRUE.
95+
clang_analyzer_eval(glob_arr5[2][1] == 0); // expected-warning{{UNKNOWN}}
96+
// FIXME: Should be TRUE.
97+
clang_analyzer_eval(glob_arr5[3][0] == 0); // expected-warning{{UNKNOWN}}
98+
// FIXME: Should be TRUE.
99+
clang_analyzer_eval(glob_arr5[3][1] == 0); // expected-warning{{UNKNOWN}}
100+
}
101+
102+
// TODO: Support multidimensional array.
103+
void glob_ptr_index2() {
104+
int const *ptr = glob_arr5[1];
105+
// FIXME: Should be TRUE.
106+
clang_analyzer_eval(ptr[0] == 3); // expected-warning{{UNKNOWN}}
107+
// FIXME: Should be TRUE.
108+
clang_analyzer_eval(ptr[1] == 4); // expected-warning{{UNKNOWN}}
109+
// FIXME: Should be UNDEFINED.
110+
clang_analyzer_eval(ptr[2] == 5); // expected-warning{{UNKNOWN}}
111+
// FIXME: Should be UNDEFINED.
112+
clang_analyzer_eval(ptr[3] == 0); // expected-warning{{UNKNOWN}}
113+
// FIXME: Should be UNDEFINED.
114+
clang_analyzer_eval(ptr[4] == 0); // expected-warning{{UNKNOWN}}
115+
}
116+
117+
// TODO: Support multidimensional array.
118+
void glob_invalid_index5() {
119+
int idx = -42;
120+
// FIXME: Should warn {{garbage or undefined}}.
121+
auto x = glob_arr5[1][idx]; // no-warning
122+
}
123+
124+
// TODO: Support multidimensional array.
125+
void glob_invalid_index6() {
126+
int const *ptr = &glob_arr5[1][0];
127+
int idx = 42;
128+
// FIXME: Should warn {{garbage or undefined}}.
129+
auto x = ptr[idx]; // // no-warning
130+
}

0 commit comments

Comments
 (0)