[analyzer] Fix a crash when destroying a non-region.
Add defensive check that prevents a crash when we try to evaluate a destructor whose this-value is a concrete integer that isn't a null. Differential Revision: https://reviews.llvm.org/D65349 llvm-svn: 369450
This commit is contained in:
parent
d3971fe97b
commit
8eb7a74b78
|
@ -530,7 +530,7 @@ public:
|
||||||
void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest,
|
void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest,
|
||||||
const Stmt *S, bool IsBaseDtor,
|
const Stmt *S, bool IsBaseDtor,
|
||||||
ExplodedNode *Pred, ExplodedNodeSet &Dst,
|
ExplodedNode *Pred, ExplodedNodeSet &Dst,
|
||||||
const EvalCallOptions &Options);
|
EvalCallOptions &Options);
|
||||||
|
|
||||||
void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
|
void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
|
|
|
@ -977,8 +977,8 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
|
||||||
Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
|
Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
|
||||||
CallOpts.IsArrayCtorOrDtor).getAsRegion();
|
CallOpts.IsArrayCtorOrDtor).getAsRegion();
|
||||||
|
|
||||||
VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false,
|
VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
|
||||||
Pred, Dst, CallOpts);
|
/*IsBase=*/false, Pred, Dst, CallOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
|
void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
|
||||||
|
@ -1036,8 +1036,9 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
|
||||||
SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy,
|
SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy,
|
||||||
Base->isVirtual());
|
Base->isVirtual());
|
||||||
|
|
||||||
VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(),
|
EvalCallOptions CallOpts;
|
||||||
CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {});
|
VisitCXXDestructor(BaseTy, BaseVal.getAsRegion(), CurDtor->getBody(),
|
||||||
|
/*IsBase=*/true, Pred, Dst, CallOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
|
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
|
||||||
|
@ -1048,10 +1049,10 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
|
|
||||||
const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
|
const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
|
||||||
Loc ThisVal = getSValBuilder().getCXXThis(CurDtor,
|
Loc ThisStorageLoc =
|
||||||
LCtx->getStackFrame());
|
getSValBuilder().getCXXThis(CurDtor, LCtx->getStackFrame());
|
||||||
SVal FieldVal =
|
Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>();
|
||||||
State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>());
|
SVal FieldVal = State->getLValue(Member, ThisLoc);
|
||||||
|
|
||||||
// FIXME: We need to run the same destructor on every element of the array.
|
// FIXME: We need to run the same destructor on every element of the array.
|
||||||
// This workaround will just run the first destructor (which will still
|
// This workaround will just run the first destructor (which will still
|
||||||
|
@ -1060,8 +1061,8 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
|
||||||
FieldVal = makeZeroElementRegion(State, FieldVal, T,
|
FieldVal = makeZeroElementRegion(State, FieldVal, T,
|
||||||
CallOpts.IsArrayCtorOrDtor);
|
CallOpts.IsArrayCtorOrDtor);
|
||||||
|
|
||||||
VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(),
|
VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
|
||||||
CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts);
|
/*IsBase=*/false, Pred, Dst, CallOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
|
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
|
||||||
|
@ -1109,8 +1110,6 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
|
||||||
EvalCallOptions CallOpts;
|
EvalCallOptions CallOpts;
|
||||||
CallOpts.IsTemporaryCtorOrDtor = true;
|
CallOpts.IsTemporaryCtorOrDtor = true;
|
||||||
if (!MR) {
|
if (!MR) {
|
||||||
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
|
|
||||||
|
|
||||||
// If we have no MR, we still need to unwrap the array to avoid destroying
|
// If we have no MR, we still need to unwrap the array to avoid destroying
|
||||||
// the whole array at once. Regardless, we'd eventually need to model array
|
// the whole array at once. Regardless, we'd eventually need to model array
|
||||||
// destructors properly, element-by-element.
|
// destructors properly, element-by-element.
|
||||||
|
|
|
@ -604,7 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
|
||||||
bool IsBaseDtor,
|
bool IsBaseDtor,
|
||||||
ExplodedNode *Pred,
|
ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst,
|
ExplodedNodeSet &Dst,
|
||||||
const EvalCallOptions &CallOpts) {
|
EvalCallOptions &CallOpts) {
|
||||||
assert(S && "A destructor without a trigger!");
|
assert(S && "A destructor without a trigger!");
|
||||||
const LocationContext *LCtx = Pred->getLocationContext();
|
const LocationContext *LCtx = Pred->getLocationContext();
|
||||||
ProgramStateRef State = Pred->getState();
|
ProgramStateRef State = Pred->getState();
|
||||||
|
@ -612,7 +612,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
|
||||||
const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
|
const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
|
||||||
assert(RecordDecl && "Only CXXRecordDecls should have destructors");
|
assert(RecordDecl && "Only CXXRecordDecls should have destructors");
|
||||||
const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
|
const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
|
||||||
|
|
||||||
// FIXME: There should always be a Decl, otherwise the destructor call
|
// FIXME: There should always be a Decl, otherwise the destructor call
|
||||||
// shouldn't have been added to the CFG in the first place.
|
// shouldn't have been added to the CFG in the first place.
|
||||||
if (!DtorDecl) {
|
if (!DtorDecl) {
|
||||||
|
@ -626,9 +625,27 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Dest) {
|
||||||
|
// We're trying to destroy something that is not a region. This may happen
|
||||||
|
// for a variety of reasons (unknown target region, concrete integer instead
|
||||||
|
// of target region, etc.). The current code makes an attempt to recover.
|
||||||
|
// FIXME: We probably don't really need to recover when we're dealing
|
||||||
|
// with concrete integers specifically.
|
||||||
|
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
|
||||||
|
if (const Expr *E = dyn_cast_or_null<Expr>(S)) {
|
||||||
|
Dest = MRMgr.getCXXTempObjectRegion(E, Pred->getLocationContext());
|
||||||
|
} else {
|
||||||
|
static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor");
|
||||||
|
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
|
||||||
|
Bldr.generateSink(Pred->getLocation().withTag(&T),
|
||||||
|
Pred->getState(), Pred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CallEventManager &CEMgr = getStateManager().getCallEventManager();
|
CallEventManager &CEMgr = getStateManager().getCallEventManager();
|
||||||
CallEventRef<CXXDestructorCall> Call =
|
CallEventRef<CXXDestructorCall> Call =
|
||||||
CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
|
CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
|
||||||
|
|
||||||
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
|
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
|
||||||
Call->getSourceRange().getBegin(),
|
Call->getSourceRange().getBegin(),
|
||||||
|
|
|
@ -540,3 +540,33 @@ void f() {
|
||||||
clang_analyzer_eval(__alignof(NonTrivial) > 0); // expected-warning{{TRUE}}
|
clang_analyzer_eval(__alignof(NonTrivial) > 0); // expected-warning{{TRUE}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace dtor_over_loc_concrete_int {
|
||||||
|
struct A {
|
||||||
|
~A() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
A a;
|
||||||
|
~B() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct C : A {
|
||||||
|
~C() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void testB() {
|
||||||
|
B *b = (B *)-1;
|
||||||
|
b->~B(); // no-crash
|
||||||
|
}
|
||||||
|
|
||||||
|
void testC() {
|
||||||
|
C *c = (C *)-1;
|
||||||
|
c->~C(); // no-crash
|
||||||
|
}
|
||||||
|
|
||||||
|
void testAutoDtor() {
|
||||||
|
const A &a = *(A *)-1;
|
||||||
|
// no-crash
|
||||||
|
}
|
||||||
|
} // namespace dtor_over_loc_concrete_int
|
||||||
|
|
Loading…
Reference in New Issue