[analyzer] Don't crash when dynamic type of a variable is set via placement new.

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

llvm-svn: 326245
This commit is contained in:
Artem Dergachev 2018-02-27 20:54:40 +00:00
parent 3a76492108
commit f01831ebe9
2 changed files with 37 additions and 1 deletions

View 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())

View File

@ -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
}