Move NullDeref and UndefDeref into their own checker.
Add a CheckLocation() interface to Checker.
Now ImplicitNullDeref nodes are cached in NullDerefChecker.
More cleanups follow.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85471 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/CheckNSError.cpp b/lib/Analysis/CheckNSError.cpp
index 8086da5..2398285 100644
--- a/lib/Analysis/CheckNSError.cpp
+++ b/lib/Analysis/CheckNSError.cpp
@@ -18,6 +18,7 @@
#include "clang/Analysis/LocalCheckers.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
+#include "clang/Analysis/PathSensitive/NullDerefChecker.h"
#include "BasicObjCFoundationChecks.h"
#include "llvm/Support/Compiler.h"
#include "clang/AST/DeclObjC.h"
@@ -208,8 +209,9 @@
return;
// Iterate over the implicit-null dereferences.
- for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(),
- E=Eng.implicit_null_derefs_end(); I!=E; ++I) {
+ NullDerefChecker *Checker = Eng.getChecker<NullDerefChecker>();
+ for (NullDerefChecker::iterator I = Checker->implicit_nodes_begin(),
+ E = Checker->implicit_nodes_end(); I != E; ++I) {
const GRState *state = (*I)->getState();
const SVal* X = state->get<GRState::NullDerefTag>();
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp
index 9ed5ba5..c0aed23 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -118,17 +118,20 @@
ExplodedNodeSet Tmp;
ExplodedNodeSet *PrevSet = &Src;
- for (std::vector<Checker*>::iterator I = Checkers.begin(), E = Checkers.end();
- I != E; ++I) {
+ for (llvm::DenseMap<void*, Checker*>::iterator I = Checkers.begin(),
+ E = Checkers.end(); I != E; ++I) {
- ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
+ llvm::DenseMap<void*, Checker*>::iterator X = I;
+
+ ExplodedNodeSet *CurrSet = (++X == E) ? &Dst
: (PrevSet == &Tmp) ? &Src : &Tmp;
CurrSet->clear();
- Checker *checker = *I;
+ void *tag = I->first;
+ Checker *checker = I->second;
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
NI != NE; ++NI)
- checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, isPrevisit);
+ checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit);
// Update which NodeSet is the current one.
PrevSet = CurrSet;
@@ -138,6 +141,21 @@
// automatically.
}
+ExplodedNode *GRExprEngine::CheckerVisitLocation(Stmt *S, ExplodedNode *Pred,
+ const GRState *state, SVal V) {
+ if (Checkers.empty())
+ return Pred;
+
+ for (llvm::DenseMap<void*, Checker*>::iterator I = Checkers.begin(),
+ E = Checkers.end(); I != E; ++I) {
+ Pred = I->second->CheckLocation(S, Pred, state, V, *this);
+ if (!Pred)
+ break;
+ }
+
+ return Pred;
+}
+
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -166,9 +184,9 @@
GRExprEngine::~GRExprEngine() {
BR.FlushReports();
delete [] NSExceptionInstanceRaiseSelectors;
- for (std::vector<Checker*>::iterator I=Checkers.begin(), E=Checkers.end();
- I!=E; ++I)
- delete *I;
+ for (llvm::DenseMap<void*, Checker*>::iterator I=Checkers.begin(),
+ E=Checkers.end(); I!=E; ++I)
+ delete I->second;
}
//===----------------------------------------------------------------------===//
@@ -1188,58 +1206,11 @@
SaveAndRestore<const void*> OldTag(Builder->Tag);
Builder->Tag = tag;
- // Check for loads/stores from/to undefined values.
- if (location.isUndef()) {
- ExplodedNode* N =
- Builder->generateNode(Ex, state, Pred,
- ProgramPoint::PostUndefLocationCheckFailedKind);
-
- if (N) {
- N->markAsSink();
- UndefDeref.insert(N);
- }
-
- return 0;
- }
-
- // Check for loads/stores from/to unknown locations. Treat as No-Ops.
if (location.isUnknown())
return Pred;
- // During a load, one of two possible situations arise:
- // (1) A crash, because the location (pointer) was NULL.
- // (2) The location (pointer) is not NULL, and the dereference works.
- //
- // We add these assumptions.
+ return CheckerVisitLocation(Ex, Pred, state, location);
- Loc LV = cast<Loc>(location);
-
- // "Assume" that the pointer is not NULL.
- const GRState *StNotNull = state->Assume(LV, true);
-
- // "Assume" that the pointer is NULL.
- const GRState *StNull = state->Assume(LV, false);
-
- if (StNull) {
- // Use the Generic Data Map to mark in the state what lval was null.
- const SVal* PersistentLV = getBasicVals().getPersistentSVal(LV);
- StNull = StNull->set<GRState::NullDerefTag>(PersistentLV);
-
- // We don't use "MakeNode" here because the node will be a sink
- // and we have no intention of processing it later.
- ExplodedNode* NullNode =
- Builder->generateNode(Ex, StNull, Pred,
- ProgramPoint::PostNullCheckFailedKind);
-
- if (NullNode) {
- NullNode->markAsSink();
- if (StNotNull) ImplicitNullDeref.insert(NullNode);
- else ExplicitNullDeref.insert(NullNode);
- }
- }
-
- if (!StNotNull)
- return NULL;
// FIXME: Temporarily disable out-of-bounds checking until we make
// the logic reflect recent changes to CastRegion and friends.
@@ -1282,10 +1253,6 @@
}
}
#endif
-
- // Generate a new node indicating the checks succeed.
- return Builder->generateNode(Ex, StNotNull, Pred,
- ProgramPoint::PostLocationChecksSucceedKind);
}
//===----------------------------------------------------------------------===//
@@ -2895,7 +2862,8 @@
template<>
struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> :
public DefaultDOTGraphTraits {
-
+ // FIXME: Since we do not cache error nodes in GRExprEngine now, this does not
+ // work.
static std::string getNodeAttributes(const ExplodedNode* N, void*) {
if (GraphPrintCheckerState->isImplicitNullDeref(N) ||
diff --git a/lib/Analysis/GRExprEngineInternalChecks.cpp b/lib/Analysis/GRExprEngineInternalChecks.cpp
index da24192..ca38b05 100644
--- a/lib/Analysis/GRExprEngineInternalChecks.cpp
+++ b/lib/Analysis/GRExprEngineInternalChecks.cpp
@@ -15,6 +15,7 @@
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
+#include "clang/Analysis/PathSensitive/NullDerefChecker.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/Compiler.h"
@@ -40,10 +41,8 @@
//===----------------------------------------------------------------------===//
// Bug Descriptions.
//===----------------------------------------------------------------------===//
-
-namespace {
-
-class VISIBILITY_HIDDEN BuiltinBugReport : public RangedBugReport {
+namespace clang {
+class BuiltinBugReport : public RangedBugReport {
public:
BuiltinBugReport(BugType& bt, const char* desc,
ExplodedNode *n)
@@ -57,58 +56,22 @@
const ExplodedNode* N);
};
-class VISIBILITY_HIDDEN BuiltinBug : public BugType {
- GRExprEngine &Eng;
-protected:
- const std::string desc;
-public:
- BuiltinBug(GRExprEngine *eng, const char* n, const char* d)
- : BugType(n, "Logic errors"), Eng(*eng), desc(d) {}
-
- BuiltinBug(GRExprEngine *eng, const char* n)
- : BugType(n, "Logic errors"), Eng(*eng), desc(n) {}
-
- const std::string &getDescription() const { return desc; }
-
- virtual void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {}
-
- void FlushReports(BugReporter& BR) { FlushReportsImpl(BR, Eng); }
-
- virtual void registerInitialVisitors(BugReporterContext& BRC,
- const ExplodedNode* N,
- BuiltinBugReport *R) {}
-
- template <typename ITER> void Emit(BugReporter& BR, ITER I, ITER E);
-};
-
+void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC,
+ const ExplodedNode* N) {
+ static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this);
+}
template <typename ITER>
void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) {
for (; I != E; ++I) BR.EmitReport(new BuiltinBugReport(*this, desc.c_str(),
GetNode(I)));
}
-
-void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC,
- const ExplodedNode* N) {
- static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this);
+void NullDeref::registerInitialVisitors(BugReporterContext& BRC,
+ const ExplodedNode* N,
+ BuiltinBugReport *R) {
+ registerTrackNullOrUndefValue(BRC, bugreporter::GetDerefExpr(N), N);
}
-class VISIBILITY_HIDDEN NullDeref : public BuiltinBug {
-public:
- NullDeref(GRExprEngine* eng)
- : BuiltinBug(eng,"Null dereference", "Dereference of null pointer") {}
-
- void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {
- Emit(BR, Eng.null_derefs_begin(), Eng.null_derefs_end());
- }
-
- void registerInitialVisitors(BugReporterContext& BRC,
- const ExplodedNode* N,
- BuiltinBugReport *R) {
- registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N);
- }
-};
-
class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug {
public:
NilReceiverStructRet(GRExprEngine* eng) :
@@ -175,14 +138,12 @@
}
};
+
+
class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug {
public:
- UndefinedDeref(GRExprEngine* eng)
- : BuiltinBug(eng,"Dereference of undefined pointer value") {}
-
- void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {
- Emit(BR, Eng.undef_derefs_begin(), Eng.undef_derefs_end());
- }
+ UndefinedDeref()
+ : BuiltinBug(0, "Dereference of undefined pointer value") {}
void registerInitialVisitors(BugReporterContext& BRC,
const ExplodedNode* N,
@@ -595,7 +556,7 @@
CheckAttrNonNull() : BT(0) {}
~CheckAttrNonNull() {}
- const void *getTag() {
+ static void *getTag() {
static int x = 0;
return &x;
}
@@ -676,10 +637,9 @@
C.addTransition(C.GenerateNode(CE, state));
}
};
-} // end anonymous namespace
// Undefined arguments checking.
-namespace {
+
class VISIBILITY_HIDDEN CheckUndefinedArg
: public CheckerVisitor<CheckUndefinedArg> {
@@ -689,7 +649,7 @@
CheckUndefinedArg() : BT(0) {}
~CheckUndefinedArg() {}
- const void *getTag() {
+ static void *getTag() {
static int x = 0;
return &x;
}
@@ -721,7 +681,7 @@
CheckBadCall() : BT(0) {}
~CheckBadCall() {}
- const void *getTag() {
+ static void *getTag() {
static int x = 0;
return &x;
}
@@ -748,7 +708,7 @@
CheckDivZero() : BT(0) {}
~CheckDivZero() {}
- const void *getTag() {
+ static void *getTag() {
static int x;
return &x;
}
@@ -797,7 +757,85 @@
if (stateNotZero != C.getState())
C.addTransition(C.GenerateNode(B, stateNotZero));
}
+
+class VISIBILITY_HIDDEN CheckUndefDeref : public Checker {
+ UndefinedDeref *BT;
+public:
+ CheckUndefDeref() : BT(0) {}
+
+ ExplodedNode *CheckSVal(const Stmt *S, ExplodedNode *Pred,
+ const GRState *state, SVal V, GRExprEngine &Eng);
+
+ static void *getTag() {
+ static int x = 0;
+ return &x;
+ }
+};
+
+ExplodedNode *CheckUndefDeref::CheckSVal(const Stmt *S, ExplodedNode *Pred,
+ const GRState *state, SVal V,
+ GRExprEngine &Eng) {
+ GRStmtNodeBuilder &Builder = Eng.getBuilder();
+ BugReporter &BR = Eng.getBugReporter();
+
+ if (V.isUndef()) {
+ ExplodedNode *N = Builder.generateNode(S, state, Pred,
+ ProgramPoint::PostUndefLocationCheckFailedKind);
+ if (N) {
+ if (!BT)
+ BT = new UndefinedDeref();
+
+ N->markAsSink();
+ BR.EmitReport(new BuiltinBugReport(*BT, BT->getDescription().c_str(), N));
+ }
+ return 0;
+ }
+
+ return Pred;
}
+
+ExplodedNode *NullDerefChecker::CheckLocation(const Stmt *S, ExplodedNode *Pred,
+ const GRState *state, SVal V,
+ GRExprEngine &Eng) {
+ Loc *LV = dyn_cast<Loc>(&V);
+
+ // If the value is not a location, don't touch the node.
+ if (!LV)
+ return Pred;
+
+ const GRState *NotNullState = state->Assume(*LV, true);
+ const GRState *NullState = state->Assume(*LV, false);
+
+ GRStmtNodeBuilder &Builder = Eng.getBuilder();
+ BugReporter &BR = Eng.getBugReporter();
+
+ // The explicit NULL case.
+ if (NullState) {
+ // Use the GDM to mark in the state what lval was null.
+ const SVal *PersistentLV = Eng.getBasicVals().getPersistentSVal(*LV);
+ NullState = NullState->set<GRState::NullDerefTag>(PersistentLV);
+
+ ExplodedNode *N = Builder.generateNode(S, NullState, Pred,
+ ProgramPoint::PostNullCheckFailedKind);
+ if (N) {
+ N->markAsSink();
+
+ if (!NotNullState) { // Explicit null case.
+ if (!BT)
+ BT = new NullDeref();
+ BR.EmitReport(new BuiltinBugReport(*BT,BT->getDescription().c_str(),N));
+ return 0;
+ } else // Implicit null case.
+ ImplicitNullDerefNodes.push_back(N);
+ }
+ }
+
+ if (!NotNullState)
+ return 0;
+ return Builder.generateNode(S, NotNullState, Pred,
+ ProgramPoint::PostLocationChecksSucceedKind);
+}
+} // end clang namespace
//===----------------------------------------------------------------------===//
// Check registration.
//===----------------------------------------------------------------------===//
@@ -808,8 +846,6 @@
// create BugReports on-the-fly but instead wait until GRExprEngine finishes
// analyzing a function. Generation of BugReport objects is done via a call
// to 'FlushReports' from BugReporter.
- BR.Register(new NullDeref(this));
- BR.Register(new UndefinedDeref(this));
BR.Register(new UndefBranch(this));
BR.Register(new UndefResult(this));
BR.Register(new RetStack(this));
@@ -826,8 +862,10 @@
// their associated BugType will get registered with the BugReporter
// automatically. Note that the check itself is owned by the GRExprEngine
// object.
- registerCheck(new CheckAttrNonNull());
- registerCheck(new CheckUndefinedArg());
- registerCheck(new CheckBadCall());
- registerCheck(new CheckDivZero());
+ registerCheck<CheckAttrNonNull>(new CheckAttrNonNull());
+ registerCheck<CheckUndefinedArg>(new CheckUndefinedArg());
+ registerCheck<CheckBadCall>(new CheckBadCall());
+ registerCheck<CheckDivZero>(new CheckDivZero());
+ registerCheck<CheckUndefDeref>(new CheckUndefDeref());
+ registerCheck<NullDerefChecker>(new NullDerefChecker());
}