[cfe-commits] r154434 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/Store.h lib/StaticAnalyzer/Core/ExprEngineC.cpp lib/StaticAnalyzer/Core/RegionStore.cpp test/Analysis/dynamic-cast.cpp
Anna Zaks
ganna at apple.com
Tue Apr 10 13:59:00 PDT 2012
Author: zaks
Date: Tue Apr 10 15:59:00 2012
New Revision: 154434
URL: http://llvm.org/viewvc/llvm-project?rev=154434&view=rev
Log:
[analyzer] Add support for C++ dynamic_cast.
Simulate the C++ dynamic_cast in the analyzer.
Added:
cfe/trunk/test/Analysis/dynamic-cast.cpp
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h?rev=154434&r1=154433&r2=154434&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h Tue Apr 10 15:59:00 2012
@@ -120,9 +120,18 @@
virtual SVal ArrayToPointer(Loc Array) = 0;
/// Evaluates DerivedToBase casts.
- virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType) {
- return UnknownVal();
- }
+ virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType) = 0;
+
+ /// \brief Evaluates C++ dynamic_cast cast.
+ /// The callback may result in the following 3 scenarios:
+ /// - Successful cast (ex: derived is subclass of base).
+ /// - Failed cast (ex: derived is definitely not a subclass of base).
+ /// - We don't know (base is a symbolic region and we don't have
+ /// enough info to determine if the cast will succeed at run time).
+ /// The function returns an SVal representing the derived class; it's
+ /// valid only if Failed flag is set to false.
+ virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType,
+ bool &Failed) = 0;
class CastResult {
ProgramStateRef state;
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp?rev=154434&r1=154433&r2=154434&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp Tue Apr 10 15:59:00 2012
@@ -283,8 +283,50 @@
Bldr.generateNode(CastE, Pred, state);
continue;
}
- // Various C++ casts that are not handled yet.
- case CK_Dynamic:
+ // Handle C++ dyn_cast.
+ case CK_Dynamic: {
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal val = state->getSVal(Ex, LCtx);
+
+ // Compute the type of the result.
+ QualType resultType = CastE->getType();
+ if (CastE->isLValue())
+ resultType = getContext().getPointerType(resultType);
+
+ bool Failed = false;
+
+ // Check if the value being cast evaluates to 0.
+ if (val.isZeroConstant())
+ Failed = true;
+ // Else, evaluate the cast.
+ else
+ val = getStoreManager().evalDynamicCast(val, T, Failed);
+
+ if (Failed) {
+ // If the cast fails, conjure symbol constrained to 0.
+ DefinedOrUnknownSVal NewSym = svalBuilder.getConjuredSymbolVal(NULL,
+ CastE, LCtx, resultType,
+ currentBuilderContext->getCurrentBlockCount());
+ DefinedOrUnknownSVal Constraint = svalBuilder.evalEQ(state,
+ NewSym, svalBuilder.makeZeroVal(resultType));
+ state = state->assume(Constraint, true);
+ state = state->BindExpr(CastE, LCtx, NewSym);
+ } else {
+ // If we don't know if the cast succeeded, conjure a new symbol.
+ if (val.isUnknown()) {
+ DefinedOrUnknownSVal NewSym = svalBuilder.getConjuredSymbolVal(NULL,
+ CastE, LCtx, resultType,
+ currentBuilderContext->getCurrentBlockCount());
+ state = state->BindExpr(CastE, LCtx, NewSym);
+ } else
+ // Else, bind to the derived region value.
+ state = state->BindExpr(CastE, LCtx, val);
+ }
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ // Various C++ casts that are not handled yet.
case CK_ToUnion:
case CK_BaseToDerived:
case CK_NullToMemberPointer:
@@ -300,9 +342,8 @@
if (CastE->isLValue())
resultType = getContext().getPointerType(resultType);
const LocationContext *LCtx = Pred->getLocationContext();
- SVal result =
- svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx, resultType,
- currentBuilderContext->getCurrentBlockCount());
+ SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx,
+ resultType, currentBuilderContext->getCurrentBlockCount());
ProgramStateRef state = Pred->getState()->BindExpr(CastE, LCtx,
result);
Bldr.generateNode(CastE, Pred, state);
Modified: cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp?rev=154434&r1=154433&r2=154434&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/RegionStore.cpp Tue Apr 10 15:59:00 2012
@@ -229,6 +229,16 @@
/// For DerivedToBase casts, create a CXXBaseObjectRegion and return it.
virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType);
+ /// \brief Evaluates C++ dynamic_cast cast.
+ /// The callback may result in the following 3 scenarios:
+ /// - Successful cast (ex: derived is subclass of base).
+ /// - Failed cast (ex: derived is definitely not a subclass of base).
+ /// - We don't know (base is a symbolic region and we don't have
+ /// enough info to determine if the cast will succeed at run time).
+ /// The function returns an SVal representing the derived class; it's
+ /// valid only if Failed flag is set to false.
+ virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType,bool &Failed);
+
StoreRef getInitialStore(const LocationContext *InitLoc) {
return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this);
}
@@ -877,6 +887,79 @@
return loc::MemRegionVal(baseReg);
}
+SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType,
+ bool &Failed) {
+ Failed = false;
+
+ loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base);
+ if (!baseRegVal)
+ return UnknownVal();
+ const MemRegion *BaseRegion = baseRegVal->stripCasts();
+
+ // Assume the derived class is a pointer to a CXX record.
+ // TODO: Note, we do not model reference types: a bad_cast exception is thrown
+ // when a cast of reference fails, but we just return an UnknownVal.
+ if (!derivedType->isPointerType())
+ return UnknownVal();
+ derivedType = derivedType->getPointeeType();
+ assert(!derivedType.isNull());
+ const CXXRecordDecl *DerivedDecl = derivedType->getAsCXXRecordDecl();
+ if (!DerivedDecl && !derivedType->isVoidType())
+ return UnknownVal();
+
+ // Drill down the CXXBaseObject chains, which represent upcasts (casts from
+ // derived to base).
+ const MemRegion *SR = BaseRegion;
+ while (const TypedRegion *TSR = dyn_cast_or_null<TypedRegion>(SR)) {
+ QualType BaseType = TSR->getLocationType()->getPointeeType();
+ assert(!BaseType.isNull());
+ const CXXRecordDecl *SRDecl = BaseType->getAsCXXRecordDecl();
+ if (!SRDecl)
+ return UnknownVal();
+
+ // If found the derived class, the cast succeeds.
+ if (SRDecl == DerivedDecl)
+ return loc::MemRegionVal(TSR);
+
+ // If the region type is a subclass of the derived type.
+ if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) {
+ // This occurs in two cases.
+ // 1) We are processing an upcast.
+ // 2) We are processing a downcast but we jumped directly from the
+ // ancestor to a child of the cast value, so conjure the
+ // appropriate region to represent value (the intermediate node).
+ return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl,
+ BaseRegion));
+ }
+
+ // If super region is not a parent of derived class, the cast definitely
+ // fails.
+ if (!derivedType->isVoidType() &&
+ DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) {
+ Failed = true;
+ return UnknownVal();
+ }
+
+ if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR))
+ // Drill down the chain to get the derived classes.
+ SR = R->getSuperRegion();
+ else {
+ // We reached the bottom of the hierarchy.
+
+ // If this is a cast to void*, return the region.
+ if (derivedType->isVoidType())
+ return loc::MemRegionVal(TSR);
+
+ // We did not find the derived class. We we must be casting the base to
+ // derived, so the cast should fail.
+ Failed = true;
+ return UnknownVal();
+ }
+ }
+
+ return UnknownVal();
+}
+
//===----------------------------------------------------------------------===//
// Loading values from regions.
//===----------------------------------------------------------------------===//
Added: cfe/trunk/test/Analysis/dynamic-cast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/dynamic-cast.cpp?rev=154434&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/dynamic-cast.cpp (added)
+++ cfe/trunk/test/Analysis/dynamic-cast.cpp Tue Apr 10 15:59:00 2012
@@ -0,0 +1,185 @@
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core -verify %s
+
+class A {
+public:
+ virtual void f(){};
+
+};
+class B : public A{
+public:
+ int m;
+};
+class C : public A{};
+
+class BB: public B{};
+
+// A lot of the tests below have the if statement in them, which forces the
+// analyzer to explore both path - when the result is 0 and not. This makes
+// sure that we definitely know that the result is non-0 (as the result of
+// the cast).
+int testDynCastFromRadar() {
+ B aa;
+ A *a = &aa;
+ const int* res = 0;
+ B *b = dynamic_cast<B*>(a);
+ static const int i = 5;
+ if(b) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // no warning
+}
+
+int testBaseToBase1() {
+ B b;
+ B *pb = &b;
+ B *pbb = dynamic_cast<B*>(pb);
+ const int* res = 0;
+ static const int i = 5;
+ if (pbb) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // no warning
+}
+
+int testMultipleLevelsOfSubclassing1() {
+ BB bb;
+ B *pb = &bb;
+ A *pa = pb;
+ B *b = dynamic_cast<B*>(pa);
+ const int* res = 0;
+ static const int i = 5;
+ if (b) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // no warning
+}
+
+int testMultipleLevelsOfSubclassing2() {
+ BB bb;
+ A *pbb = &bb;
+ B *b = dynamic_cast<B*>(pbb);
+ BB *s = dynamic_cast<BB*>(b);
+ const int* res = 0;
+ static const int i = 5;
+ if (s) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // no warning
+}
+
+int testMultipleLevelsOfSubclassing3() {
+ BB bb;
+ A *pbb = &bb;
+ B *b = dynamic_cast<B*>(pbb);
+ return b->m; // no warning
+}
+
+int testLHS() {
+ B aa;
+ A *a = &aa;
+ return (dynamic_cast<B*>(a))->m;
+}
+
+int testLHS2() {
+ B aa;
+ A *a = &aa;
+ return (*dynamic_cast<B*>(a)).m;
+}
+
+int testDynCastUnknown2(class A *a) {
+ B *b = dynamic_cast<B*>(a);
+ return b->m; // no warning
+}
+
+int testDynCastUnknown(class A *a) {
+ B *b = dynamic_cast<B*>(a);
+ const int* res = 0;
+ static const int i = 5;
+ if (b) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // expected-warning {{Dereference of null pointer}}
+}
+
+int testDynCastFail2() {
+ C c;
+ A *pa = &c;
+ B *b = dynamic_cast<B*>(pa);
+ return b->m; // expected-warning {{dereference of a null pointer}}
+}
+
+int testLHSFail() {
+ C c;
+ A *a = &c;
+ return (*dynamic_cast<B*>(a)).m; // expected-warning {{Dereference of null pointer}}
+}
+
+int testBaseToDerivedFail() {
+ A a;
+ B *b = dynamic_cast<B*>(&a);
+ return b->m; // expected-warning {{dereference of a null pointer}}
+}
+
+int testConstZeroFail() {
+ B *b = dynamic_cast<B*>((A *)0);
+ return b->m; // expected-warning {{dereference of a null pointer}}
+}
+
+int testConstZeroFail2() {
+ A *a = 0;
+ B *b = dynamic_cast<B*>(a);
+ return b->m; // expected-warning {{dereference of a null pointer}}
+}
+
+int testUpcast() {
+ B b;
+ A *a = dynamic_cast<A*>(&b);
+ const int* res = 0;
+ static const int i = 5;
+ if (a) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // no warning
+}
+
+int testCastToVoidStar() {
+ A a;
+ void *b = dynamic_cast<void*>(&a);
+ const int* res = 0;
+ static const int i = 5;
+ if (b) {
+ res = &i;
+ } else {
+ res = 0;
+ }
+ return *res; // no warning
+}
+
+int testReference() {
+ A a;
+ B &b = dynamic_cast<B&>(a);
+ return b.m; // no warning
+}
+
+// False negatives.
+
+// Symbolic regions are not typed, so we cannot deduce that the cast will
+// always fail in this case.
+int testDynCastFail1(class C *c) {
+ B *b = 0;
+ b = dynamic_cast<B*>(c);
+ return b->m;
+}
+
More information about the cfe-commits
mailing list