]> granicus.if.org Git - clang/commitdiff
[analyzer] CastValueChecker: Correctly model results of based-to-derived casts.
authorArtem Dergachev <artem.dergachev@gmail.com>
Fri, 23 Aug 2019 03:24:04 +0000 (03:24 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Fri, 23 Aug 2019 03:24:04 +0000 (03:24 +0000)
Our SVal hierarchy doesn't allow modeling pointer casts as no-op. The
pointer type is instead encoded into the pointer object. Defer to our
usual pointer casting facility, SValBuilder::evalBinOp().

Fixes a crash.

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

lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
test/Analysis/cast-value-logic.cpp
test/Analysis/cast-value-notes.cpp

index a0cebd6ab856292cb7d3e52632df2c671a11425b..cc1c9a66b90e26a00ccbdde6b10e5e20f7ba8e92 100644 (file)
@@ -138,6 +138,20 @@ static const NoteTag *getNoteTag(CheckerContext &C,
 // Main logic to evaluate a cast.
 //===----------------------------------------------------------------------===//
 
+static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,
+                                    ASTContext &ACtx) {
+  if (alignTowards->isLValueReferenceType() &&
+      alignTowards.isConstQualified()) {
+    toAlign.addConst();
+    return ACtx.getLValueReferenceType(toAlign);
+  } else if (alignTowards->isLValueReferenceType())
+    return ACtx.getLValueReferenceType(toAlign);
+  else if (alignTowards->isRValueReferenceType())
+    return ACtx.getRValueReferenceType(toAlign);
+
+  llvm_unreachable("Must align towards a reference type!");
+}
+
 static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
                               CheckerContext &C, bool IsNonNullParam,
                               bool IsNonNullReturn,
@@ -156,6 +170,15 @@ static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
   } else {
     Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
     CastFromTy = Object->getType();
+    if (CastToTy->isPointerType()) {
+      if (!CastFromTy->isPointerType())
+        return;
+    } else {
+      if (!CastFromTy->isReferenceType())
+        return;
+
+      CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());
+    }
   }
 
   const MemRegion *MR = DV.getAsRegion();
@@ -183,7 +206,8 @@ static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
     State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
                                       CastSucceeds);
 
-  SVal V = CastSucceeds ? DV : C.getSValBuilder().makeNull();
+  SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
+                        : C.getSValBuilder().makeNull();
   C.addTransition(
       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
       getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
@@ -198,14 +222,8 @@ static void addInstanceOfTransition(const CallEvent &Call,
   QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType();
   if (CastFromTy->isPointerType())
     CastToTy = C.getASTContext().getPointerType(CastToTy);
-  else if (CastFromTy->isLValueReferenceType() &&
-           CastFromTy.isConstQualified()) {
-    CastToTy.addConst();
-    CastToTy = C.getASTContext().getLValueReferenceType(CastToTy);
-  } else if (CastFromTy->isLValueReferenceType())
-    CastToTy = C.getASTContext().getLValueReferenceType(CastToTy);
-  else if (CastFromTy->isRValueReferenceType())
-    CastToTy = C.getASTContext().getRValueReferenceType(CastToTy);
+  else if (CastFromTy->isReferenceType())
+    CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
   else
     return;
 
index 531772825f97074ca6bbb845a63aad32dac20fe9..221ae7f9ae38bca4a6785322c379c51401187cd4 100644 (file)
@@ -19,7 +19,11 @@ struct Shape {
   virtual double area();
 };
 class Triangle : public Shape {};
-class Circle : public Shape {};
+class Circle : public Shape {
+public:
+  ~Circle();
+};
+class SuspiciouslySpecificCircle : public Circle {};
 } // namespace clang
 
 using namespace llvm;
@@ -149,4 +153,10 @@ double test_virtual_method_after_call(Shape *S) {
     return S->area();
   return S->area() / 2;
 }
+
+void test_delete_crash() {
+  extern Circle *makeCircle();
+  Shape *S = makeCircle();
+  delete cast<SuspiciouslySpecificCircle>(S);
+}
 } // namespace crashes
index f92ba90336a7a6df6e1bc9566c44e7a34ec15dfb..a0eaeae8ba483f8e5ff1ab7fb4b8e74978f4a74c 100644 (file)
@@ -123,8 +123,7 @@ void evalZeroParamNonNullReturnPointer(const Shape *S) {
 
 void evalZeroParamNonNullReturn(const Shape &S) {
   const auto *C = S.castAs<Circle>();
-  // expected-note@-1 {{'S' is a 'Circle'}}
-  // expected-note@-2 {{'C' initialized here}}
+  // expected-note@-1 {{'C' initialized here}}
 
   (void)(1 / !C);
   // expected-note@-1 {{'C' is non-null}}