ASDenysPetrov created this revision.
ASDenysPetrov added reviewers: NoQ, xazax.hun, r.stahl, chrish_ericsson_atx.
ASDenysPetrov added a project: clang.
Herald added subscribers: manas, steakhal, martong, dkrupp, donat.nagy,
Szelethus, mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware.
ASDenysPetrov requested review of this revision.
Herald added a subscriber: cfe-commits.
Retrieve a value from list initialization of constant multi-dimensional array
which resides in a global scope. Calculate raw offset and traverse through list
initialization to get a value for specified position. This patch is a
consistent continuation of D104285 <https://reviews.llvm.org/D104285> revision.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D106681
Files:
clang/lib/StaticAnalyzer/Core/MemRegion.cpp
clang/lib/StaticAnalyzer/Core/RegionStore.cpp
clang/test/Analysis/initialization.c
clang/test/Analysis/initialization.cpp
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.uninitialized.Assign,core.builtin,debug.ExprInspection,core.uninitialized.UndefReturn -verify %s
+// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection -verify %s
void clang_analyzer_eval(int);
@@ -15,8 +15,7 @@
int const arr[2][2] = {};
void arr2init() {
int i = 1;
- // FIXME: Should recognize that it is 0.
- clang_analyzer_eval(arr[i][0]); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(arr[i][0]); // expected-warning{{FALSE}}
}
void direct_index1() {
@@ -124,8 +123,86 @@
auto buf_p = rvp[4]; // no-warning (garbage or undefined)
}
-const float floats[] = {
- 0.0000, 0.0235, 0.0470, 0.0706, 0.0941, 0.1176};
-float no_warn_garbage_value() {
- return floats[0]; // no-warning (garbage or undefined)
+const int glob_arr1[2][2][3] = {{{1, 2}}, {{7}}};
+void glob_arr_index1() {
+ clang_analyzer_eval(glob_arr1[0][0][0] == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][0][1] == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][0][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][1][0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][1][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][1][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][0][0] == 7); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][0][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][0][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][1][0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][1][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][1][2] == 0); // expected-warning{{TRUE}}
+}
+
+int const glob_arr2[2][2][3] = {{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+void glob_arr_index2() {
+ clang_analyzer_eval(glob_arr2[0][0][0] == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[0][0][1] == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[0][0][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[0][1][0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[0][1][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[0][1][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[1][0][0] == 7); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[1][0][1] == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[1][0][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[1][1][0] == 10); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[1][1][1] == 11); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr2[1][1][2] == 12); // expected-warning{{TRUE}}
+}
+
+const int glob_arr3[4][2] = {{}, {3}, {}, {7}};
+void glob_arr_index3() {
+ clang_analyzer_eval(glob_arr3[0][0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[0][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[1][0] == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[1][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[2][0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[2][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[3][0] == 7); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr3[3][1] == 0); // expected-warning{{TRUE}}
+}
+
+void negative_index1() {
+ int x = 2, y = -2;
+ clang_analyzer_eval(glob_arr3[x][y] == 3); // expected-warning{{TRUE}}
+ x = 4;
+ y = -2;
+ clang_analyzer_eval(glob_arr3[x][y] == 7); // expected-warning{{TRUE}}
+}
+
+void out_of_bound_index1() {
+ int x = -3, y = 2;
+ int res = glob_arr3[x][y]; // expected-warning{{garbage or undefined}}
+}
+
+void out_of_bound_index2() {
+ int x = 3, y = 2;
+ int res = glob_arr3[x][y]; // expected-warning{{garbage or undefined}}
+}
+
+const int glob_arr4[8] = {1, 2, 3, 4};
+void glob_arr_index4() {
+ clang_analyzer_eval(glob_arr4[0] == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[1] == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[2] == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[3] == 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[4] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[5] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[6] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr4[7] == 0); // expected-warning{{TRUE}}
+}
+
+void out_of_bound_index3() {
+ int x = -42;
+ int res = glob_arr4[x]; // expected-warning{{garbage or undefined}}
+}
+
+void out_of_bound_index4() {
+ int x = 42;
+ int res = glob_arr4[x]; // expected-warning{{garbage or undefined}}
}
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-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection -verify %s
void clang_analyzer_eval(int);
@@ -26,3 +26,34 @@
clang_analyzer_eval(sm.a == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(sm.b == 0); // expected-warning{{TRUE}}
}
+
+const int glob_arr1[3][3] = {[0][0] = 1, [1][1] = 5, [2][0] = 7};
+void glob_arr_index1() {
+ clang_analyzer_eval(glob_arr1[0][0] == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[0][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][1] == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[1][2] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[2][0] == 7); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[2][1] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(glob_arr1[2][2] == 0); // expected-warning{{TRUE}}
+}
+
+void negative_index1() {
+ int x = 2, y = -2;
+ clang_analyzer_eval(glob_arr1[x][y] == 5); // expected-warning{{TRUE}}
+ x = 3;
+ y = -3;
+ clang_analyzer_eval(glob_arr1[x][y] == 7); // expected-warning{{TRUE}}
+}
+
+void out_of_bound_index1() {
+ int x = -1, y = -1;
+ int res = glob_arr1[x][y]; // expected-warning{{garbage or undefined}}
+}
+
+void out_of_bound_index2() {
+ int x = 3, y = 2;
+ int res = glob_arr1[x][y]; // expected-warning{{garbage or undefined}}
+}
Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -437,6 +437,10 @@
RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
const SubRegion *R);
+ Optional<SVal>
+ getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
+ QualType ElemT, const VarRegion *VR,
+ const llvm::APSInt &Idx);
public: // Part of public interface to class.
@@ -1624,6 +1628,39 @@
return Result;
}
+Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
+ RegionBindingsConstRef B, QualType ElemT, const VarRegion *VR,
+ const llvm::APSInt &Idx) {
+ // Array should constant a const value or a value of a global initializer in
+ // main().
+ const VarDecl *VD = VR->getDecl();
+ if (!VD->getType().isConstQualified() && !ElemT.isConstQualified() &&
+ (!B.isMainAnalysis() || !VD->hasGlobalStorage()))
+ return None;
+ // Array should have an initialization list.
+ const Expr *Init = VD->getAnyInitializer();
+ // FIXME: Support StringLiteral as an initializer as well.
+ const auto *InitList = dyn_cast_or_null<InitListExpr>(Init);
+ if (!InitList)
+ return None;
+ // Choose a correct function depending on index signedness.
+ const int64_t I = Idx.getExtValue();
+ const Expr *E =
+ Idx.isSigned()
+ ? InitList->getExprForConstArrayByRawIndex(I)
+ : InitList->getExprForConstArrayByRawIndex(static_cast<uint64_t>(I));
+ // If E is null, then the index is out of bounds. Return Undef.
+ if (!E)
+ return UndefinedVal();
+ // If E is the same as InitList, then there is no value specified
+ // in the list and we shall return a zero value.
+ if (E == InitList)
+ return svalBuilder.makeZeroVal(ElemT);
+ // Return a constant value.
+ if (Optional<SVal> V = svalBuilder.getConstantVal(E))
+ return *V;
+ return None;
+}
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
@@ -1657,40 +1694,32 @@
char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
return svalBuilder.makeIntVal(c, T);
}
- } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
- // Check if the containing array has an initialized value that we can trust.
- // We can trust a const value or a value of a global initializer in main().
- const VarDecl *VD = VR->getDecl();
- if (VD->getType().isConstQualified() ||
- R->getElementType().isConstQualified() ||
- (B.isMainAnalysis() && VD->hasGlobalStorage())) {
- if (const Expr *Init = VD->getAnyInitializer()) {
- if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
- // The array index has to be known.
- if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
- const llvm::APSInt &Int = CI->getValue();
- int64_t Idx = Int.getExtValue();
- const Expr *E = Int.isSigned()
- ? InitList->getExprForConstArrayByRawIndex(Idx)
- : InitList->getExprForConstArrayByRawIndex(
- static_cast<uint64_t>(Idx));
-
- // If E is null, then the index is out of bounds. Return Undef.
- if (!E)
- return UndefinedVal();
-
- // If E is the same as InitList, then there is no value specified
- // in the list and we shall return a zero value.
- if (E == InitList)
- return svalBuilder.makeZeroVal(R->getElementType());
-
- // Return a constant value.
- if (Optional<SVal> V = svalBuilder.getConstantVal(E))
- return *V;
- }
- }
+ } else if (isa<ElementRegion>(superR) || isa<VarRegion>(superR)) {
+ const VarRegion *VR = nullptr;
+ // Prepare APSInt as int64_t.
+ llvm::APSInt Idx(64, false);
+ if (isa<ElementRegion>(superR)) {
+ // Check if it is a mutli-dimensional array.
+ const RegionRawOffset &RRO = R->getAsArrayOffset();
+ VR = dyn_cast_or_null<VarRegion>(RRO.getRegion());
+ if (VR) {
+ // FIXME: We are limited here due to RegionRawOffset only contains
+ // offset as signed integer. Adjust the code when
+ // ElementRegion::getAsArrayOffset supports unsigned offsets.
+ Idx = RRO.getOffset().getQuantity() /
+ Ctx.getTypeSizeInChars(R->getElementType()).getQuantity();
}
+ } else if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
+ // Check if it is a single-dimensional array.
+ VR = cast<VarRegion>(superR);
+ const llvm::APSInt Int = CI->getValue();
+ Idx = Int.getExtValue();
+ Idx.setIsUnsigned(Int.isUnsigned());
}
+ if (VR)
+ if (Optional<SVal> V = getConstantValFromConstArrayInitializer(
+ B, R->getElementType(), VR, Idx))
+ return *V;
}
// Check for loads from a code text region. For such loads, just give up.
@@ -1709,8 +1738,7 @@
if (!O.getRegion())
return UnknownVal();
- if (const TypedValueRegion *baseR =
- dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
+ if (const auto *baseR = dyn_cast<TypedValueRegion>(O.getRegion())) {
QualType baseT = baseR->getValueType();
if (baseT->isScalarType()) {
QualType elemT = R->getElementType();
Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1334,6 +1334,7 @@
return nullptr;
}
+// FIXME: Support unsigned offset as well.
RegionRawOffset ElementRegion::getAsArrayOffset() const {
int64_t offset = 0;
const ElementRegion *ER = this;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits