================
@@ -0,0 +1,163 @@
+//===- ContainerOfModeling.h ------------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/ContainerOfModeling.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
+
+namespace clang::ento {
+
+static QualType getRegionObjectType(const MemRegion *Region) {
+  if (const auto *TVR = dyn_cast<TypedValueRegion>(Region))
+    return TVR->getValueType();
+  if (const auto *SR = dyn_cast<SymbolicRegion>(Region))
+    return SR->getPointeeStaticType();
+  return {};
+}
+
+/// Return true when the region containing \p ContainerRegion has type
+/// \p ContainerType. ElementRegion represents both array elements and casts,
+/// so the type of ContainerRegion itself is not sufficient evidence.
+static bool hasContainerTypeProvenance(const SubRegion *ContainerRegion,
+                                       QualType ContainerType,
+                                       ASTContext &Ctx) {
+  const MemRegion *StorageRegion = ContainerRegion;
+  if (const auto *ER = dyn_cast<ElementRegion>(ContainerRegion)) {
+    if (!ASTContext::hasSameUnqualifiedType(ER->getElementType(),
+                                            ContainerType))
+      return false;
+    StorageRegion = ER->getSuperRegion();
+  }
+
+  QualType StorageType = getRegionObjectType(StorageRegion);
+  if (StorageType.isNull())
+    return false;
+
+  if (const ArrayType *AT = Ctx.getAsArrayType(StorageType))
+    StorageType = AT->getElementType();
+
+  return ASTContext::hasSameUnqualifiedType(StorageType, ContainerType);
+}
+
+/// Return whether the concrete storage containing \p ContainerRegion is large
+/// enough to contain an object of \p ContainerType at that region's offset.
+/// Return std::nullopt when either the offset or the extent is symbolic.
+static std::optional<bool>
+hasSufficientContainerExtent(ProgramStateRef State,
+                             const SubRegion *ContainerRegion,
+                             QualType ContainerType, SValBuilder &SVB) {
+  ASTContext &Ctx = SVB.getContext();
+  RegionOffset Offset = ContainerRegion->getAsOffset();
+  if (!Offset.isValid() || Offset.hasSymbolicOffset())
+    return std::nullopt;
+
+  const int64_t OffsetBits = Offset.getOffset();
+  const uint64_t CharWidth = Ctx.getCharWidth();
+  if (OffsetBits < 0 || static_cast<uint64_t>(OffsetBits) % CharWidth != 0)
+    return false;
+
+  const MemRegion *BaseRegion = Offset.getRegion();
+  const auto BaseExtent =
+      getDynamicExtent(State, BaseRegion, SVB).getAs<nonloc::ConcreteInt>();
+  if (!BaseExtent)
+    return std::nullopt;
+
+  const int64_t ContainerSize =
+      Ctx.getTypeSizeInChars(ContainerType).getQuantity();
+  if (ContainerSize < 0)
+    return false;
+
+  const uint64_t OffsetChars = static_cast<uint64_t>(OffsetBits) / CharWidth;
+  const uint64_t ContainerSizeChars = static_cast<uint64_t>(ContainerSize);
+  if (OffsetChars > std::numeric_limits<uint64_t>::max() - ContainerSizeChars)
+    return false;
+
+  const uint64_t RequiredExtent = OffsetChars + ContainerSizeChars;
+  const llvm::APSInt RequiredExtentValue =
+      llvm::APSInt::getUnsigned(RequiredExtent);
+  return llvm::APSInt::compareValues(*BaseExtent->getValue(),
+                                     RequiredExtentValue) >= 0;
+}
+
+const SubRegion *getContainerOfParentRegion(const ElementRegion *ContainerER,
+                                            ProgramStateRef State,
+                                            SValBuilder &SVB) {
+  ASTContext &Ctx = SVB.getContext();
+  const MemRegion *SuperRegion = ContainerER->getSuperRegion();
+  const FieldRegion *FieldR = nullptr;
+  int64_t CharacterIndex = 0;
+
+  if (const auto *CharacterER = dyn_cast<ElementRegion>(SuperRegion)) {
+    QualType CharacterType = CharacterER->getElementType();
+    if (!CharacterType->isCharType() ||
+        Ctx.getTypeSizeInChars(CharacterType).getQuantity() != 1)
+      return nullptr;
+
+    const auto ConcreteIndex =
+        CharacterER->getIndex().getAs<nonloc::ConcreteInt>();
+    if (!ConcreteIndex)
+      return nullptr;
+
+    std::optional<int64_t> Index = ConcreteIndex->getValue()->tryExtValue();
+    if (!Index)
+      return nullptr;
+    CharacterIndex = *Index;
+
+    FieldR = dyn_cast<FieldRegion>(CharacterER->getSuperRegion());
+  } else {
+    // SValBuilder folds an adjustment of zero, so a first field is represented
+    // without an intermediate character ElementRegion.
+    FieldR = dyn_cast<FieldRegion>(SuperRegion);
+  }
+
+  if (!FieldR)
+    return nullptr;
+
+  const FieldDecl *Field = FieldR->getDecl();
+  if (Field->isBitField())
+    return nullptr;
+
+  QualType ContainerType =
+      ContainerER->getElementType().getCanonicalType().getUnqualifiedType();
+  const auto *ContainerRT = ContainerType->getAs<RecordType>();
+  if (!ContainerRT)
+    return nullptr;
+
+  const RecordDecl *FieldParent = Field->getParent();
+  if (!FieldParent || !FieldParent->isCompleteDefinition() ||
+      ContainerRT->getDecl()->getCanonicalDecl() !=
+          FieldParent->getCanonicalDecl())
+    return nullptr;
+
+  const uint64_t FieldOffsetBits = Ctx.getFieldOffset(Field);
+  const uint64_t CharWidth = Ctx.getCharWidth();
+  if (FieldOffsetBits % CharWidth != 0 || CharacterIndex > 0)
+    return nullptr;
+
+  // Avoid negating INT64_MIN while comparing the signed character index with
+  // the unsigned ABI field offset.
+  const uint64_t BackwardOffset =
+      static_cast<uint64_t>(-(CharacterIndex + 1)) + 1;
+  if (BackwardOffset != FieldOffsetBits / CharWidth)
+    return nullptr;
----------------
NagyDonat wrote:

Perhaps it is easier to check this with an approach like
```c++
if (CharacterIndex * CharWidth != - FieldOffsetBits)
  return nullptr;
```
because multiplication is simpler than division.

You may want to harden my proposal to ensure proper behavior against overflows 
(I didn't consider those corner case), but I don't think that overflow is 
actually possible here because the compiler would never generate struct layouts 
where the offsets are close to overflowing a 64-bit integer.

--------

By the way, the `ArrayBound` checker strongly relies on the fact that memory 
offsets are "small" and operations on them don't overflow `size_t`. Using this 
lets us provide more accurate reports. (This is just a fun fact, not something 
that's directly actionable here.)

https://github.com/llvm/llvm-project/pull/214140
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to