[analyzer] Support modeling no-op BaseToDerived casts in ExprEngine.

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

llvm-svn: 340984
This commit is contained in:
Artem Dergachev 2018-08-29 22:43:31 +00:00
parent 1e4498869d
commit 4e864b8329
6 changed files with 151 additions and 13 deletions

View 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,

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

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

View 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:

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

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