[analyzer] Use the common evalBind infrastructure for initializers.

This allows checkers (like the MallocChecker) to process the effects of the
bind. Previously, using a memory-allocating function (like strdup()) in an
initializer would result in a leak warning.

This does bend the expectations of checkBind a bit; since there is no
assignment expression, the statement being used is the initializer value.
In most cases this shouldn't matter because we'll use a PostInitializer
program point (rather than PostStmt) for any checker-generated nodes, though
we /will/ generate a PostStore node referencing the internal statement.
(In theory this could have funny effects if someone actually does an
assignment within an initializer; in practice, that seems like it would be
very rare.)

<rdar://problem/12171711>

llvm-svn: 162637
This commit is contained in:
Jordan Rose 2012-08-25 01:06:23 +00:00
parent cb889d8737
commit 0a0aa84da3
6 changed files with 52 additions and 33 deletions

View File

@ -258,7 +258,7 @@ public:
const ExplodedNodeSet &Src,
SVal location, SVal val,
const Stmt *S, ExprEngine &Eng,
ProgramPoint::Kind PointKind);
const ProgramPoint &PP);
/// \brief Run checkers for end of analysis.
void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,

View File

@ -434,7 +434,8 @@ protected:
/// evalBind - Handle the semantics of binding a value to a specific location.
/// This method is used by evalStore, VisitDeclStmt, and others.
void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred,
SVal location, SVal Val, bool atDeclInit = false);
SVal location, SVal Val, bool atDeclInit = false,
const ProgramPoint *PP = 0);
public:
// FIXME: 'tag' should be removed, and a LocationContext should be used

View File

@ -32,7 +32,11 @@ using namespace ento;
const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) {
// Pattern match for a few useful cases (do something smarter later):
// a[0], p->f, *p
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
const PostStmt *Loc = N->getLocationAs<PostStmt>();
if (!Loc)
return 0;
const Stmt *S = Loc->getStmt();
while (true) {
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) {

View File

@ -314,20 +314,19 @@ namespace {
SVal Val;
const Stmt *S;
ExprEngine &Eng;
ProgramPoint::Kind PointKind;
const ProgramPoint &PP;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckBindContext(const CheckersTy &checkers,
SVal loc, SVal val, const Stmt *s, ExprEngine &eng,
ProgramPoint::Kind PK)
: Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PointKind(PK) {}
const ProgramPoint &pp)
: Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {}
void runChecker(CheckerManager::CheckBindFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, PointKind,
Pred->getLocationContext(), checkFn.Checker);
const ProgramPoint &L = PP.withTag(checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
checkFn(Loc, Val, S, C);
@ -340,8 +339,8 @@ void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SVal location, SVal val,
const Stmt *S, ExprEngine &Eng,
ProgramPoint::Kind PointKind) {
CheckBindContext C(BindCheckers, location, val, S, Eng, PointKind);
const ProgramPoint &PP) {
CheckBindContext C(BindCheckers, location, val, S, Eng, PP);
expandGraphWithCheckers(C, Dst, Src);
}

View File

@ -354,11 +354,6 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
void ExprEngine::ProcessInitializer(const CFGInitializer Init,
ExplodedNode *Pred) {
ExplodedNodeSet Dst;
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
ProgramStateRef State = Pred->getState();
const CXXCtorInitializer *BMI = Init.getInitializer();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
@ -370,13 +365,19 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
cast<StackFrameContext>(Pred->getLocationContext());
const CXXConstructorDecl *decl =
cast<CXXConstructorDecl>(stackFrame->getDecl());
ProgramStateRef State = Pred->getState();
SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame));
PostInitializer PP(BMI, stackFrame);
ExplodedNodeSet Tmp(Pred);
// Evaluate the initializer, if necessary
if (BMI->isAnyMemberInitializer()) {
// Constructors build the object directly in the field,
// but non-objects must be copied in from the initializer.
if (!isa<CXXConstructExpr>(BMI->getInit())) {
const Expr *Init = BMI->getInit();
if (!isa<CXXConstructExpr>(Init)) {
SVal FieldLoc;
if (BMI->isIndirectMemberInitializer())
FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal);
@ -384,19 +385,23 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
FieldLoc = State->getLValue(BMI->getMember(), thisVal);
SVal InitVal = State->getSVal(BMI->getInit(), stackFrame);
State = State->bindLoc(FieldLoc, InitVal);
Tmp.clear();
evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
}
} else {
assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
// We already did all the work when visiting the CXXConstructExpr.
}
// Construct a PostInitializer node whether the state changed or not,
// Construct PostInitializer nodes whether the state changed or not,
// so that the diagnostics don't get confused.
PostInitializer PP(BMI, stackFrame);
// Builder automatically add the generated node to the deferred set,
// which are processed in the builder's dtor.
Bldr.generateNode(PP, State, Pred);
ExplodedNodeSet Dst;
NodeBuilder Bldr(Tmp, Dst, *currBldrCtx);
for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) {
ExplodedNode *N = *I;
Bldr.generateNode(PP, N->getState(), N);
}
// Enqueue the new nodes onto the work list.
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
@ -1541,13 +1546,17 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
ExplodedNode *Pred,
SVal location, SVal Val,
bool atDeclInit) {
bool atDeclInit, const ProgramPoint *PP) {
const LocationContext *LC = Pred->getLocationContext();
PostStmt PS(StoreE, LC);
if (!PP)
PP = &PS;
// Do a previsit of the bind.
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val,
StoreE, *this,
ProgramPoint::PostStmtKind);
StoreE, *this, *PP);
// If the location is not a 'Loc', it will already be handled by
// the checkers. There is nothing left to do.
@ -1559,7 +1568,6 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
ExplodedNodeSet TmpDst;
StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currBldrCtx);
const LocationContext *LC = Pred->getLocationContext();
for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
I!=E; ++I) {
ExplodedNode *PredI = *I;
@ -1575,6 +1583,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location)) {
LocReg = LocRegVal->getRegion();
}
const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0);
Bldr.generateNode(L, state, PredI);
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -cfg-add-implicit-dtors -std=c++11 -verify %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-ipa=inlining -cfg-add-implicit-dtors -std=c++11 -verify %s
// We don't inline constructors unless we have destructors turned on.
@ -57,10 +57,6 @@ void testDelegatingConstructor() {
}
// ------------------------------------
// False negatives
// ------------------------------------
struct RefWrapper {
RefWrapper(int *p) : x(*p) {}
RefWrapper(int &r) : x(r) {}
@ -69,10 +65,20 @@ struct RefWrapper {
void testReferenceMember() {
int *p = 0;
RefWrapper X(p); // should warn in the constructor
RefWrapper X(p); // expected-warning@61 {{Dereference of null pointer}}
}
void testReferenceMember2() {
int *p = 0;
RefWrapper X(*p); // should warn here
// FIXME: We should warn here, since we're creating the reference here.
RefWrapper X(*p); // expected-warning@62 {{Dereference of null pointer}}
}
extern "C" char *strdup(const char *);
class StringWrapper {
char *str;
public:
StringWrapper(const char *input) : str(strdup(input)) {} // no-warning
};