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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D106681: [analyzer] R... Denys Petrov via Phabricator via cfe-commits

Reply via email to