]> granicus.if.org Git - clang/commitdiff
[analyzer] Don't crash when dynamic type of a variable is set via placement new.
authorArtem Dergachev <artem.dergachev@gmail.com>
Tue, 27 Feb 2018 20:54:40 +0000 (20:54 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Tue, 27 Feb 2018 20:54:40 +0000 (20:54 +0000)
If a variable or an otherwise a concrete typed-value region is being
placement-new'ed into, its dynamic type may change in arbitrary manners. And
when the region is used, there may be a third type that's different from both
the static and the dynamic type. It cannot be *completely* different from the
dynamic type, but it may be a base class of the dynamic type - and in this case
there isn't (and shouldn't be) any indication anywhere in the AST that there is
a derived-to-base cast from the dynamic type to the third type.

Perform a generic cast (evalCast()) from the third type to the dynamic type
in this case. From the point of view of the SVal hierarchy, this would have
produced non-canonical SVals if we used such generic cast in the normal case,
but in this case there doesn't seem to be a better option.

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

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

lib/StaticAnalyzer/Core/CallEvent.cpp
test/Analysis/new-dynamic-types.cpp [new file with mode: 0644]

index cb41f65e988979195a157df7a94d289d98ca6429..47a10bf23c4b1d1754fa06f1a8637f1ed11fcf2c 100644 (file)
@@ -587,7 +587,15 @@ void CXXInstanceCall::getInitialStackFrameContents(
       // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
       bool Failed;
       ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed);
-      assert(!Failed && "Calling an incorrectly devirtualized method");
+      if (Failed) {
+        // We might have suffered some sort of placement new earlier, so
+        // we're constructing in a completely unexpected storage.
+        // Fall back to a generic pointer cast for this-value.
+        const CXXMethodDecl *StaticMD = cast<CXXMethodDecl>(getDecl());
+        const CXXRecordDecl *StaticClass = StaticMD->getParent();
+        QualType StaticTy = Ctx.getPointerType(Ctx.getRecordType(StaticClass));
+        ThisVal = SVB.evalCast(ThisVal, Ty, StaticTy);
+      }
     }
 
     if (!ThisVal.isUnknown())
diff --git a/test/Analysis/new-dynamic-types.cpp b/test/Analysis/new-dynamic-types.cpp
new file mode 100644 (file)
index 0000000..e0a460e
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core -std=c++11 -verify %s
+
+// expected-no-diagnostics
+
+typedef __typeof(sizeof(int)) size_t;
+
+void *operator new(size_t size, void *ptr);
+
+struct B {
+  virtual void foo();
+};
+
+struct D : public B {
+  virtual void foo() override {}
+};
+
+void test_ub() {
+  // FIXME: Potentially warn because this code is pretty weird.
+  B b;
+  new (&b) D;
+  b.foo(); // no-crash
+}
+
+void test_non_ub() {
+  char c[sizeof(D)]; // Should be enough storage.
+  new (c) D;
+  ((B *)c)->foo(); // no-crash
+}