]> granicus.if.org Git - clang/commitdiff
[analyzer] Support modeling no-op BaseToDerived casts in ExprEngine.
authorArtem Dergachev <artem.dergachev@gmail.com>
Wed, 29 Aug 2018 22:43:31 +0000 (22:43 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Wed, 29 Aug 2018 22:43:31 +0000 (22:43 +0000)
Introduce a new MemRegion sub-class, CXXDerivedObjectRegion, which is
the opposite of CXXBaseObjectRegion, to represent such casts. Such region is
a bit weird because it is by design bigger than its super-region.
But it's not harmful when it is put on top of a SymbolicRegion
that has unknown extent anyway.

Offset computation for CXXDerivedObjectRegion and proper modeling of casts
still remains to be implemented.

Differential Revision: https://reviews.llvm.org/D51191

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@340984 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
lib/StaticAnalyzer/Core/MemRegion.cpp
lib/StaticAnalyzer/Core/RegionStore.cpp
lib/StaticAnalyzer/Core/Store.cpp
test/Analysis/casts.cpp

index 3a93aae5a9bc627fea2c6adeab41488b908c16bc..a82559238df599d563ccf5dcab7e105143873ec0 100644 (file)
@@ -122,7 +122,7 @@ public:
   /// Each region is a subregion of itself.
   virtual bool isSubRegionOf(const MemRegion *R) const;
 
-  const MemRegion *StripCasts(bool StripBaseCasts = true) const;
+  const MemRegion *StripCasts(bool StripBaseAndDerivedCasts = true) const;
 
   /// If this is a symbolic region, returns the region. Otherwise,
   /// goes up the base chain looking for the first symbolic base region.
@@ -1176,6 +1176,47 @@ public:
   }
 };
 
+// CXXDerivedObjectRegion represents a derived-class object that surrounds
+// a C++ object. It is identified by the derived class declaration and the
+// region of its parent object. It is a bit counter-intuitive (but not otherwise
+// unseen) that this region represents a larger segment of memory that its
+// super-region.
+class CXXDerivedObjectRegion : public TypedValueRegion {
+  friend class MemRegionManager;
+
+  const CXXRecordDecl *DerivedD;
+
+  CXXDerivedObjectRegion(const CXXRecordDecl *DerivedD, const SubRegion *SReg)
+      : TypedValueRegion(SReg, CXXDerivedObjectRegionKind), DerivedD(DerivedD) {
+    assert(DerivedD);
+    // In case of a concrete region, it should always be possible to model
+    // the base-to-derived cast by undoing a previous derived-to-base cast,
+    // otherwise the cast is most likely ill-formed.
+    assert(SReg->getSymbolicBase() &&
+           "Should have unwrapped a base region instead!");
+  }
+
+  static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD,
+                            const MemRegion *SReg);
+
+public:
+  const CXXRecordDecl *getDecl() const { return DerivedD; }
+
+  QualType getValueType() const override;
+
+  void dumpToStream(raw_ostream &os) const override;
+
+  void Profile(llvm::FoldingSetNodeID &ID) const override;
+
+  bool canPrintPrettyAsExpr() const override;
+
+  void printPrettyAsExpr(raw_ostream &os) const override;
+
+  static bool classof(const MemRegion *region) {
+    return region->getKind() == CXXDerivedObjectRegionKind;
+  }
+};
+
 template<typename RegionTy>
 const RegionTy* MemRegion::getAs() const {
   if (const auto *RT = dyn_cast<RegionTy>(this))
@@ -1326,6 +1367,14 @@ public:
                                   baseReg->isVirtual());
   }
 
+  /// Create a CXXDerivedObjectRegion with the given derived class for region
+  /// \p Super. This should not be used for casting an existing
+  /// CXXBaseObjectRegion back to the derived type; instead, CXXBaseObjectRegion
+  /// should be removed.
+  const CXXDerivedObjectRegion *
+  getCXXDerivedObjectRegion(const CXXRecordDecl *BaseClass,
+                            const SubRegion *Super);
+
   const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD);
   const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD,
                                             CanQualType locTy,
index c84a1ff13f8b6f18b3cd80e0bfcc6a02855ce952..10f89ecc55a5eb26e43d8925cff01172689b34b3 100644 (file)
@@ -68,6 +68,7 @@ ABSTRACT_REGION(SubRegion, MemRegion)
     ABSTRACT_REGION(TypedValueRegion, TypedRegion)
       REGION(CompoundLiteralRegion, TypedValueRegion)
       REGION(CXXBaseObjectRegion, TypedValueRegion)
+      REGION(CXXDerivedObjectRegion, TypedValueRegion)
       REGION(CXXTempObjectRegion, TypedValueRegion)
       REGION(CXXThisRegion, TypedValueRegion)
       ABSTRACT_REGION(DeclRegion, TypedValueRegion)
index cb2122c7749ef1642e89d079e4e2f894d369d1cb..a3d3eb26615fd0f5ebf287de772fe87b15d5ccee 100644 (file)
@@ -225,6 +225,10 @@ QualType CXXBaseObjectRegion::getValueType() const {
   return QualType(getDecl()->getTypeForDecl(), 0);
 }
 
+QualType CXXDerivedObjectRegion::getValueType() const {
+  return QualType(getDecl()->getTypeForDecl(), 0);
+}
+
 //===----------------------------------------------------------------------===//
 // FoldingSet profiling.
 //===----------------------------------------------------------------------===//
@@ -404,6 +408,17 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
   ProfileRegion(ID, getDecl(), isVirtual(), superRegion);
 }
 
+void CXXDerivedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+                                           const CXXRecordDecl *RD,
+                                           const MemRegion *SReg) {
+  ID.AddPointer(RD);
+  ID.AddPointer(SReg);
+}
+
+void CXXDerivedObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+  ProfileRegion(ID, getDecl(), superRegion);
+}
+
 //===----------------------------------------------------------------------===//
 // Region anchors.
 //===----------------------------------------------------------------------===//
@@ -475,7 +490,11 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
 }
 
 void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
-  os << "base{" << superRegion << ',' << getDecl()->getName() << '}';
+  os << "Base{" << superRegion << ',' << getDecl()->getName() << '}';
+}
+
+void CXXDerivedObjectRegion::dumpToStream(raw_ostream &os) const {
+  os << "Derived{" << superRegion << ',' << getDecl()->getName() << '}';
 }
 
 void CXXThisRegion::dumpToStream(raw_ostream &os) const {
@@ -483,7 +502,7 @@ void CXXThisRegion::dumpToStream(raw_ostream &os) const {
 }
 
 void ElementRegion::dumpToStream(raw_ostream &os) const {
-  os << "element{" << superRegion << ','
+  os << "Element{" << superRegion << ','
      << Index << ',' << getElementType().getAsString() << '}';
 }
 
@@ -492,7 +511,7 @@ void FieldRegion::dumpToStream(raw_ostream &os) const {
 }
 
 void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
-  os << "ivar{" << superRegion << ',' << *getDecl() << '}';
+  os << "Ivar{" << superRegion << ',' << *getDecl() << '}';
 }
 
 void StringRegion::dumpToStream(raw_ostream &os) const {
@@ -630,6 +649,14 @@ void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
   superRegion->printPrettyAsExpr(os);
 }
 
+bool CXXDerivedObjectRegion::canPrintPrettyAsExpr() const {
+  return superRegion->canPrintPrettyAsExpr();
+}
+
+void CXXDerivedObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
+  superRegion->printPrettyAsExpr(os);
+}
+
 std::string MemRegion::getDescriptiveName(bool UseQuotes) const {
   std::string VariableName;
   std::string ArrayIndices;
@@ -1061,6 +1088,12 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
   return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super);
 }
 
+const CXXDerivedObjectRegion *
+MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD,
+                                            const SubRegion *Super) {
+  return getSubRegion<CXXDerivedObjectRegion>(RD, Super);
+}
+
 const CXXThisRegion*
 MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
                                    const LocationContext *LC) {
@@ -1131,6 +1164,7 @@ const MemRegion *MemRegion::getBaseRegion() const {
       case MemRegion::FieldRegionKind:
       case MemRegion::ObjCIvarRegionKind:
       case MemRegion::CXXBaseObjectRegionKind:
+      case MemRegion::CXXDerivedObjectRegionKind:
         R = cast<SubRegion>(R)->getSuperRegion();
         continue;
       default:
@@ -1149,7 +1183,7 @@ bool MemRegion::isSubRegionOf(const MemRegion *R) const {
 // View handling.
 //===----------------------------------------------------------------------===//
 
-const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
+const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const {
   const MemRegion *R = this;
   while (true) {
     switch (R->getKind()) {
@@ -1161,9 +1195,10 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
       break;
     }
     case CXXBaseObjectRegionKind:
-      if (!StripBaseCasts)
+    case CXXDerivedObjectRegionKind:
+      if (!StripBaseAndDerivedCasts)
         return R;
-      R = cast<CXXBaseObjectRegion>(R)->getSuperRegion();
+      R = cast<TypedValueRegion>(R)->getSuperRegion();
       break;
     default:
       return R;
@@ -1344,6 +1379,12 @@ static RegionOffset calculateOffset(const MemRegion *R) {
       Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth();
       break;
     }
+
+    case MemRegion::CXXDerivedObjectRegionKind: {
+      // TODO: Store the base type in the CXXDerivedObjectRegion and use it.
+      goto Finish;
+    }
+
     case MemRegion::ElementRegionKind: {
       const auto *ER = cast<ElementRegion>(R);
       R = ER->getSuperRegion();
index a0fae69f6a0af6162ced95241f4f47687761f5e2..02062e7b1632cbac622b72ca9a99f3be1f302a1f 100644 (file)
@@ -62,7 +62,9 @@ private:
     : P(r, k), Data(offset) {
     assert(r && "Must have known regions.");
     assert(getOffset() == offset && "Failed to store offset");
-    assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r)) && "Not a base");
+    assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r) ||
+            isa <CXXDerivedObjectRegion>(r)) &&
+           "Not a base");
   }
 public:
 
index 94188a9ef698f0da8c23a19a727fb758338a57c4..c3b706d90f6496b093a4d465260b434d37383518 100644 (file)
@@ -138,6 +138,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
     case MemRegion::VarRegionKind:
     case MemRegion::CXXTempObjectRegionKind:
     case MemRegion::CXXBaseObjectRegionKind:
+    case MemRegion::CXXDerivedObjectRegionKind:
       return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
 
     case MemRegion::ElementRegionKind: {
@@ -272,9 +273,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) {
 
 SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
                                      bool IsVirtual) {
-  Optional<loc::MemRegionVal> DerivedRegVal =
-      Derived.getAs<loc::MemRegionVal>();
-  if (!DerivedRegVal)
+  const MemRegion *DerivedReg = Derived.getAsRegion();
+  if (!DerivedReg)
     return Derived;
 
   const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl();
@@ -282,8 +282,18 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
     BaseDecl = BaseType->getAsCXXRecordDecl();
   assert(BaseDecl && "not a C++ object?");
 
+  if (const auto *AlreadyDerivedReg =
+          dyn_cast<CXXDerivedObjectRegion>(DerivedReg)) {
+    if (const auto *SR =
+            dyn_cast<SymbolicRegion>(AlreadyDerivedReg->getSuperRegion()))
+      if (SR->getSymbol()->getType()->getPointeeCXXRecordDecl() == BaseDecl)
+        return loc::MemRegionVal(SR);
+
+    DerivedReg = AlreadyDerivedReg->getSuperRegion();
+  }
+
   const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion(
-      BaseDecl, cast<SubRegion>(DerivedRegVal->getRegion()), IsVirtual);
+      BaseDecl, cast<SubRegion>(DerivedReg), IsVirtual);
 
   return loc::MemRegionVal(BaseReg);
 }
@@ -365,6 +375,10 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
     MR = Uncasted;
   }
 
+  if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
+    return loc::MemRegionVal(MRMgr.getCXXDerivedObjectRegion(TargetClass, SR));
+  }
+
   // We failed if the region we ended up with has perfect type info.
   Failed = isa<TypedValueRegion>(MR);
   return UnknownVal();
index 757eb6da6623682a5c21fea801beb90bdfcd03d5..9b3e294c7792f52235de41f6a115dcadbf8ba3a5 100644 (file)
@@ -1,4 +1,6 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-store=region -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify %s
+
+void clang_analyzer_eval(bool);
 
 bool PR14634(int x) {
   double y = (double)x;
@@ -41,3 +43,32 @@ bool retrievePointerFromBoolean(int *p) {
   *reinterpret_cast<int **>(&q) = p;
   return q;
 }
+
+namespace base_to_derived {
+struct A {};
+struct B : public A{};
+
+void foo(A* a) {
+  B* b = (B* ) a;
+  A* a2 = (A *) b;
+  clang_analyzer_eval(a2 == a); // expected-warning{{TRUE}}
+}
+}
+
+namespace base_to_derived_double_inheritance {
+struct A {
+  int x;
+};
+struct B {
+  int y;
+};
+struct C : A, B {};
+
+void foo(B *b) {
+  C *c = (C *)b;
+  b->y = 1;
+  clang_analyzer_eval(c->x); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(c->y); // expected-warning{{TRUE}}
+}
+}
+