ASDenysPetrov updated this revision to Diff 376823.
ASDenysPetrov added a comment.

Fixed a comment.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D110927/new/

https://reviews.llvm.org/D110927

Files:
  clang/lib/StaticAnalyzer/Core/RegionStore.cpp
  clang/test/Analysis/initialization.cpp

Index: clang/test/Analysis/initialization.cpp
===================================================================
--- clang/test/Analysis/initialization.cpp
+++ clang/test/Analysis/initialization.cpp
@@ -2,6 +2,10 @@
 
 void clang_analyzer_eval(int);
 
+namespace std {
+enum class byte : unsigned char {};
+};
+
 struct S {
   int a = 3;
 };
@@ -48,6 +52,46 @@
   auto x = ptr[idx]; // expected-warning{{garbage or undefined}}
 }
 
+void glob_cast_same() {
+  auto *ptr = (int *)glob_arr2;
+  auto x1 = ptr[0]; // no-warning
+  auto x2 = ptr[1]; // no-warning
+}
+
+void glob_cast_char() {
+  const auto *ptr = (char *)glob_arr2;
+  auto x1 = ptr[0]; // no-warning
+  auto x2 = ptr[1]; // expected-warning{{garbage or undefined}}
+}
+
+void glob_cast_uchar() {
+  auto *ptr = (unsigned char *)glob_arr2;
+  auto x1 = ptr[0]; // no-warning
+  auto x2 = ptr[1]; // expected-warning{{garbage or undefined}}
+}
+
+void glob_cast_byte() {
+  auto *ptr = (const std::byte *)glob_arr2;
+  auto x1 = ptr[0]; // no-warning
+  auto x2 = ptr[1]; // expected-warning{{garbage or undefined}}
+}
+
+void glob_cast_opposite_sign() {
+  auto *ptr = (unsigned int *)glob_arr2;
+  auto x1 = ptr[0]; // no-warning
+  auto x2 = ptr[1]; // expected-warning{{garbage or undefined}}
+}
+
+void glob_cast_invalid1() {
+  auto *ptr = (signed char *)glob_arr2;
+  auto x = ptr[0]; // expected-warning{{garbage or undefined}}
+}
+
+void glob_cast_invalid2() {
+  using T = short *;
+  auto x = ((T)glob_arr2)[0]; // 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() {
Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -437,6 +437,8 @@
 
   RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
                                             const SubRegion *R);
+  bool canAccessStoredValue(QualType OrigT, QualType ThroughT,
+                            uint64_t Index) const;
 
 public: // Part of public interface to class.
 
@@ -1625,6 +1627,56 @@
   return Result;
 }
 
+/// Returns true if the stored value can be accessed through the pointer to
+/// another type:
+///  const int arr[42] = {};
+///  auto* pchar     = (char*)arr;
+///  auto* punsigned = (unsigned int*)arr;
+///  auto* pshort    = (short*)arr;
+///  auto x1 = pchar[0];     // valid
+///  auto x2 = pchar[1];     // invalid
+///  auto x3 = punsigned[0]; // valid
+///  auto x4 = pshort[0];    // invalid
+bool RegionStoreManager::canAccessStoredValue(QualType OrigT, QualType ThroughT,
+                                              uint64_t Index) const {
+  // Remove cv-qualifiers.
+  OrigT = OrigT->getCanonicalTypeUnqualified();
+  ThroughT = ThroughT->getCanonicalTypeUnqualified();
+
+  // C++20 7.2.1.11 [basic.lval] (excerpt):
+  //  A program can access the stored value of an object through:
+  //  - the same type of the object;
+  //  - a signed or unsigned type corresponding to the type of the
+  //    object;
+  //  - a char, unsigned char, std::byte. (NOTE:
+  //  Otherwise, the behavior is undefined.
+  return
+      // - is same
+      (OrigT == ThroughT) ||
+      // - is another sign
+      (((OrigT == Ctx.CharTy && ThroughT == Ctx.UnsignedCharTy) ||
+        (OrigT == Ctx.SignedCharTy && ThroughT == Ctx.UnsignedCharTy) ||
+        (OrigT == Ctx.ShortTy && ThroughT == Ctx.UnsignedShortTy) ||
+        (OrigT == Ctx.IntTy && ThroughT == Ctx.UnsignedIntTy) ||
+        (OrigT == Ctx.LongTy && ThroughT == Ctx.UnsignedLongTy) ||
+        (OrigT == Ctx.LongLongTy && ThroughT == Ctx.UnsignedLongLongTy) ||
+        (ThroughT == Ctx.CharTy && OrigT == Ctx.UnsignedCharTy) ||
+        (ThroughT == Ctx.SignedCharTy && OrigT == Ctx.UnsignedCharTy) ||
+        (ThroughT == Ctx.ShortTy && OrigT == Ctx.UnsignedShortTy) ||
+        (ThroughT == Ctx.IntTy && OrigT == Ctx.UnsignedIntTy) ||
+        (ThroughT == Ctx.LongTy && OrigT == Ctx.UnsignedLongTy) ||
+        (ThroughT == Ctx.LongLongTy && OrigT == Ctx.UnsignedLongLongTy) ||
+        // - is char, uchar, std::byte
+        (ThroughT == Ctx.CharTy) || (ThroughT == Ctx.UnsignedCharTy) ||
+        ThroughT->isStdByteType()) &&
+       // NOTE: C++20 6.8.2(3.4) [basic.compound]:
+       //  An object of type T that is not an array element is considered to
+       //  belong to an array with one element of type T.
+       // Hence, the first element can be retrieved only. At least untill a
+       // paper P1839R0 be considered by the committee.
+       (Index == 0));
+}
+
 SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
                                               const ElementRegion* R) {
   // Check if the region has a binding.
@@ -1689,7 +1741,8 @@
             //   int x3 = ptr[3]; // 0
             //   int x4 = ptr[4]; // UB
             // TODO: Support multidimensional array.
-            if (!isa<ConstantArrayType>(CAT->getElementType())) {
+            QualType ArrT = Ctx.getCanonicalType(CAT->getElementType());
+            if (!isa<ConstantArrayType>(ArrT)) {
               // One-dimensional array.
               const llvm::APSInt &Idx = CI->getValue();
               const auto I = static_cast<uint64_t>(Idx.getExtValue());
@@ -1700,11 +1753,17 @@
               if (Idx < 0 || I >= Extent)
                 return UndefinedVal();
 
+              // Check whether a program can access the stored value of another
+              // type.
+              QualType ElemT = Ctx.getCanonicalType(R->getElementType());
+              if (!canAccessStoredValue(ArrT, ElemT, I))
+                return UndefinedVal();
+
               // 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());
+                return svalBuilder.makeZeroVal(ElemT);
 
               // Return a constant value, if it is presented.
               // FIXME: Support other SVals.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to