Skip to content

Commit a987185

Browse files
authored
Merge pull request #3730 from haoNoQ/static-analyzer-cherrypicks-25
Static analyzer cherrypicks 25
2 parents 982c765 + e84f9ea commit a987185

File tree

3 files changed

+168
-58
lines changed

3 files changed

+168
-58
lines changed

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 105 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,10 @@ class RegionStoreManager : public StoreManager {
437437

438438
RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
439439
const SubRegion *R);
440+
Optional<SVal> getConstantValFromConstArrayInitializer(
441+
RegionBindingsConstRef B, const VarRegion *VR, const ElementRegion *R);
442+
Optional<SVal> getSValFromInitListExpr(const InitListExpr *ILE,
443+
uint64_t Offset, QualType ElemT);
440444

441445
public: // Part of public interface to class.
442446

@@ -1625,6 +1629,105 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
16251629
return Result;
16261630
}
16271631

1632+
Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
1633+
RegionBindingsConstRef B, const VarRegion *VR, const ElementRegion *R) {
1634+
assert(R && VR && "Regions should not be null");
1635+
1636+
// Check if the containing array has an initialized value that we can trust.
1637+
// We can trust a const value or a value of a global initializer in main().
1638+
const VarDecl *VD = VR->getDecl();
1639+
if (!VD->getType().isConstQualified() &&
1640+
!R->getElementType().isConstQualified() &&
1641+
(!B.isMainAnalysis() || !VD->hasGlobalStorage()))
1642+
return None;
1643+
1644+
// Array's declaration should have `ConstantArrayType` type, because only this
1645+
// type contains an array extent. It may happen that array type can be of
1646+
// `IncompleteArrayType` type. To get the declaration of `ConstantArrayType`
1647+
// type, we should find the declaration in the redeclarations chain that has
1648+
// the initialization expression.
1649+
// NOTE: `getAnyInitializer` has an out-parameter, which returns a new `VD`
1650+
// from which an initializer is obtained. We replace current `VD` with the new
1651+
// `VD`. If the return value of the function is null than `VD` won't be
1652+
// replaced.
1653+
const Expr *Init = VD->getAnyInitializer(VD);
1654+
// NOTE: If `Init` is non-null, then a new `VD` is non-null for sure. So check
1655+
// `Init` for null only and don't worry about the replaced `VD`.
1656+
if (!Init)
1657+
return None;
1658+
1659+
// Array's declaration should have ConstantArrayType type, because only this
1660+
// type contains an array extent.
1661+
const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType());
1662+
if (!CAT)
1663+
return None;
1664+
1665+
// Array should be one-dimensional.
1666+
// TODO: Support multidimensional array.
1667+
if (isa<ConstantArrayType>(CAT->getElementType())) // is multidimensional
1668+
return None;
1669+
1670+
// Array's offset should be a concrete value.
1671+
// Return Unknown value if symbolic index presented.
1672+
// FIXME: We also need to take ElementRegions with symbolic
1673+
// indexes into account.
1674+
const auto OffsetVal = R->getIndex().getAs<nonloc::ConcreteInt>();
1675+
if (!OffsetVal.hasValue())
1676+
return UnknownVal();
1677+
1678+
// Check offset for being out of bounds.
1679+
// C++20 [expr.add] 7.6.6.4 (excerpt):
1680+
// If P points to an array element i of an array object x with n
1681+
// elements, where i < 0 or i > n, the behavior is undefined.
1682+
// Dereferencing is not allowed on the "one past the last
1683+
// element", when i == n.
1684+
// Example:
1685+
// const int arr[4] = {1, 2};
1686+
// const int *ptr = arr;
1687+
// int x0 = ptr[0]; // 1
1688+
// int x1 = ptr[1]; // 2
1689+
// int x2 = ptr[2]; // 0
1690+
// int x3 = ptr[3]; // 0
1691+
// int x4 = ptr[4]; // UB
1692+
// int x5 = ptr[-1]; // UB
1693+
const llvm::APSInt &OffsetInt = OffsetVal->getValue();
1694+
const auto Offset = static_cast<uint64_t>(OffsetInt.getExtValue());
1695+
// Use `getZExtValue` because array extent can not be negative.
1696+
const uint64_t Extent = CAT->getSize().getZExtValue();
1697+
// Check for `OffsetInt < 0` but NOT for `Offset < 0`, because `OffsetInt`
1698+
// CAN be negative, but `Offset` can NOT, because `Offset` is an uint64_t.
1699+
if (OffsetInt < 0 || Offset >= Extent)
1700+
return UndefinedVal();
1701+
// From here `Offset` is in the bounds.
1702+
1703+
// Handle InitListExpr.
1704+
if (const auto *ILE = dyn_cast<InitListExpr>(Init))
1705+
return getSValFromInitListExpr(ILE, Offset, R->getElementType());
1706+
1707+
// FIXME: Handle StringLiteral.
1708+
1709+
// FIXME: Handle CompoundLiteralExpr.
1710+
1711+
return None;
1712+
}
1713+
1714+
Optional<SVal>
1715+
RegionStoreManager::getSValFromInitListExpr(const InitListExpr *ILE,
1716+
uint64_t Offset, QualType ElemT) {
1717+
assert(ILE && "InitListExpr should not be null");
1718+
1719+
// C++20 [expr.add] 9.4.17.5 (excerpt):
1720+
// i-th array element is value-initialized for each k < i ≤ n,
1721+
// where k is an expression-list size and n is an array extent.
1722+
if (Offset >= ILE->getNumInits())
1723+
return svalBuilder.makeZeroVal(ElemT);
1724+
1725+
// Return a constant value, if it is presented.
1726+
// FIXME: Support other SVals.
1727+
const Expr *E = ILE->getInit(Offset);
1728+
return svalBuilder.getConstantVal(E);
1729+
}
1730+
16281731
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
16291732
const ElementRegion* R) {
16301733
// Check if the region has a binding.
@@ -1658,64 +1761,8 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
16581761
return svalBuilder.makeIntVal(c, T);
16591762
}
16601763
} else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
1661-
// Check if the containing array has an initialized value that we can trust.
1662-
// We can trust a const value or a value of a global initializer in main().
1663-
const VarDecl *VD = VR->getDecl();
1664-
if (VD->getType().isConstQualified() ||
1665-
R->getElementType().isConstQualified() ||
1666-
(B.isMainAnalysis() && VD->hasGlobalStorage())) {
1667-
if (const Expr *Init = VD->getAnyInitializer()) {
1668-
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
1669-
// The array index has to be known.
1670-
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
1671-
// If it is not an array, return Undef.
1672-
QualType T = VD->getType();
1673-
const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T);
1674-
if (!CAT)
1675-
return UndefinedVal();
1676-
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)
1701-
return UndefinedVal();
1702-
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());
1708-
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))
1713-
return *V;
1714-
}
1715-
}
1716-
}
1717-
}
1718-
}
1764+
if (Optional<SVal> V = getConstantValFromConstArrayInitializer(B, VR, R))
1765+
return *V;
17191766
}
17201767

17211768
// Check for loads from a code text region. For such loads, just give up.

clang/test/Analysis/initialization.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,48 @@ void glob_invalid_index4() {
9797
// FIXME: Should warn {{garbage or undefined}}.
9898
int res = glob_arr2[x][y]; // no-warning
9999
}
100+
101+
const int glob_arr_no_init[10];
102+
void glob_arr_index4() {
103+
// FIXME: Should warn {{FALSE}}, since the array has a static storage.
104+
clang_analyzer_eval(glob_arr_no_init[2]); // expected-warning{{UNKNOWN}}
105+
}
106+
107+
const int glob_arr3[]; // IncompleteArrayType
108+
const int glob_arr3[4] = {1, 2, 3}; // ConstantArrayType
109+
void glob_arr_index5() {
110+
clang_analyzer_eval(glob_arr3[0] == 1); // expected-warning{{TRUE}}
111+
clang_analyzer_eval(glob_arr3[1] == 2); // expected-warning{{TRUE}}
112+
clang_analyzer_eval(glob_arr3[2] == 3); // expected-warning{{TRUE}}
113+
clang_analyzer_eval(glob_arr3[3] == 0); // expected-warning{{TRUE}}
114+
}
115+
116+
void glob_invalid_index5() {
117+
int x = 42;
118+
int res = glob_arr3[x]; // expected-warning{{garbage or undefined}}
119+
}
120+
121+
void glob_invalid_index6() {
122+
int x = -42;
123+
int res = glob_arr3[x]; // expected-warning{{garbage or undefined}}
124+
}
125+
126+
const int glob_arr4[]; // IncompleteArrayType
127+
const int glob_arr4[4] = {1, 2, 3}; // ConstantArrayType
128+
const int glob_arr4[]; // ConstantArrayType (according to AST)
129+
void glob_arr_index6() {
130+
clang_analyzer_eval(glob_arr4[0] == 1); // expected-warning{{TRUE}}
131+
clang_analyzer_eval(glob_arr4[1] == 2); // expected-warning{{TRUE}}
132+
clang_analyzer_eval(glob_arr4[2] == 3); // expected-warning{{TRUE}}
133+
clang_analyzer_eval(glob_arr4[3] == 0); // expected-warning{{TRUE}}
134+
}
135+
136+
void glob_invalid_index7() {
137+
int x = 42;
138+
int res = glob_arr4[x]; // expected-warning{{garbage or undefined}}
139+
}
140+
141+
void glob_invalid_index8() {
142+
int x = -42;
143+
int res = glob_arr4[x]; // expected-warning{{garbage or undefined}}
144+
}

clang/test/Analysis/initialization.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// 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

3+
template <typename T>
4+
void clang_analyzer_dump(T x);
35
void clang_analyzer_eval(int);
46

57
struct S {
@@ -32,6 +34,10 @@ void glob_invalid_index1() {
3234
auto x = ptr[idx]; // expected-warning{{garbage or undefined}}
3335
}
3436

37+
void glob_symbolic_index1(int idx) {
38+
clang_analyzer_dump(glob_arr1[idx]); // expected-warning{{Unknown}}
39+
}
40+
3541
int const glob_arr2[4] = {1, 2};
3642
void glob_ptr_index1() {
3743
int const *ptr = glob_arr2;
@@ -128,3 +134,15 @@ void glob_invalid_index6() {
128134
// FIXME: Should warn {{garbage or undefined}}.
129135
auto x = ptr[idx]; // // no-warning
130136
}
137+
138+
extern const int glob_arr_no_init[10];
139+
void glob_array_index4() {
140+
clang_analyzer_eval(glob_arr_no_init[2]); // expected-warning{{UNKNOWN}}
141+
}
142+
143+
struct S2 {
144+
static const int arr_no_init[10];
145+
};
146+
void struct_arr_index1() {
147+
clang_analyzer_eval(S2::arr_no_init[2]); // expected-warning{{UNKNOWN}}
148+
}

0 commit comments

Comments
 (0)