diff --git a/clang/include/clang/Analysis/PathSensitive/BugReporter.h b/clang/include/clang/Analysis/PathSensitive/BugReporter.h index 1434fce811d1..914118cb430d 100644 --- a/clang/include/clang/Analysis/PathSensitive/BugReporter.h +++ b/clang/include/clang/Analysis/PathSensitive/BugReporter.h @@ -19,6 +19,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Analysis/PathSensitive/GRState.h" #include "clang/Analysis/PathSensitive/ExplodedGraph.h" +#include "clang/Analysis/PathSensitive/BugType.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" @@ -183,38 +184,6 @@ public: const_iterator end() const { return const_iterator(Reports.end()); } }; -class BugType { -private: - const std::string Name; - const std::string Category; - llvm::FoldingSet EQClasses; - friend class BugReporter; - bool SuppressonSink; -public: - BugType(const char *name, const char* cat) - : Name(name), Category(cat), SuppressonSink(false) {} - virtual ~BugType(); - - // FIXME: Should these be made strings as well? - const std::string& getName() const { return Name; } - const std::string& getCategory() const { return Category; } - - /// isSuppressOnSink - Returns true if bug reports associated with this bug - /// type should be suppressed if the end node of the report is post-dominated - /// by a sink node. - bool isSuppressOnSink() const { return SuppressonSink; } - void setSuppressOnSink(bool x) { SuppressonSink = x; } - - virtual void FlushReports(BugReporter& BR); - - typedef llvm::FoldingSet::iterator iterator; - iterator begin() { return EQClasses.begin(); } - iterator end() { return EQClasses.end(); } - - typedef llvm::FoldingSet::const_iterator const_iterator; - const_iterator begin() const { return EQClasses.begin(); } - const_iterator end() const { return EQClasses.end(); } -}; //===----------------------------------------------------------------------===// // Specialized subclasses of BugReport. diff --git a/clang/include/clang/Analysis/PathSensitive/BugType.h b/clang/include/clang/Analysis/PathSensitive/BugType.h new file mode 100644 index 000000000000..955622a1ef9c --- /dev/null +++ b/clang/include/clang/Analysis/PathSensitive/BugType.h @@ -0,0 +1,73 @@ +#ifndef LLVM_CLANG_BUGTYPE +#define LLVM_CLANG_BUGTYPE + +#include + +#include + +namespace clang { + +class BugReportEquivClass; +class BugReporter; +class BuiltinBugReport; +class BugReporterContext; +class GRExprEngine; + +class BugType { +private: + const std::string Name; + const std::string Category; + llvm::FoldingSet EQClasses; + friend class BugReporter; + bool SuppressonSink; +public: + BugType(const char *name, const char* cat) + : Name(name), Category(cat), SuppressonSink(false) {} + virtual ~BugType(); + + // FIXME: Should these be made strings as well? + const std::string& getName() const { return Name; } + const std::string& getCategory() const { return Category; } + + /// isSuppressOnSink - Returns true if bug reports associated with this bug + /// type should be suppressed if the end node of the report is post-dominated + /// by a sink node. + bool isSuppressOnSink() const { return SuppressonSink; } + void setSuppressOnSink(bool x) { SuppressonSink = x; } + + virtual void FlushReports(BugReporter& BR); + + typedef llvm::FoldingSet::iterator iterator; + iterator begin() { return EQClasses.begin(); } + iterator end() { return EQClasses.end(); } + + typedef llvm::FoldingSet::const_iterator const_iterator; + const_iterator begin() const { return EQClasses.begin(); } + const_iterator end() const { return EQClasses.end(); } +}; + +class 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 void Emit(BugReporter& BR, ITER I, ITER E); +}; +} // end clang namespace +#endif diff --git a/clang/include/clang/Analysis/PathSensitive/Checker.h b/clang/include/clang/Analysis/PathSensitive/Checker.h index 4e00d69cdba1..59ce71e616c3 100644 --- a/clang/include/clang/Analysis/PathSensitive/Checker.h +++ b/clang/include/clang/Analysis/PathSensitive/Checker.h @@ -104,16 +104,24 @@ private: GRStmtNodeBuilder &Builder, GRExprEngine &Eng, const Stmt *stmt, - ExplodedNode *Pred, bool isPrevisit) { - CheckerContext C(Dst, Builder, Eng, Pred, getTag(), isPrevisit); + ExplodedNode *Pred, void *tag, bool isPrevisit) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, isPrevisit); assert(isPrevisit && "Only previsit supported for now."); _PreVisit(C, stmt); } + + public: virtual ~Checker() {} - virtual void _PreVisit(CheckerContext &C, const Stmt *stmt) = 0; - virtual const void *getTag() = 0; + virtual void _PreVisit(CheckerContext &C, const Stmt *stmt) {} + + // This is a previsit which takes a node returns a node. + virtual ExplodedNode *CheckLocation(const Stmt *S, ExplodedNode *Pred, + const GRState *state, SVal V, + GRExprEngine &Eng) { + return Pred; + } }; } // end clang namespace diff --git a/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h b/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h index e5c61e68c77c..e30b4271f1db 100644 --- a/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h +++ b/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h @@ -75,7 +75,8 @@ class GRExprEngine : public GRSubEngine { Selector RaiseSel; llvm::OwningPtr BatchAuditor; - std::vector Checkers; + + llvm::DenseMap Checkers; /// BR - The BugReporter associated with this engine. It is important that // this object be placed at the very end of member variables so that its @@ -126,18 +127,6 @@ public: // calling a function with the attribute "noreturn". ErrorNodes NoReturnCalls; - /// ImplicitNullDeref - Nodes in the ExplodedGraph that result from - /// taking a dereference on a symbolic pointer that MAY be NULL. - ErrorNodes ImplicitNullDeref; - - /// ExplicitNullDeref - Nodes in the ExplodedGraph that result from - /// taking a dereference on a symbolic pointer that MUST be NULL. - ErrorNodes ExplicitNullDeref; - - /// UndefDeref - Nodes in the ExplodedGraph that result from - /// taking a dereference on an undefined value. - ErrorNodes UndefDeref; - /// ImplicitBadSizedVLA - Nodes in the ExplodedGraph that result from /// constructing a zero-sized VLA where the size may be zero. ErrorNodes ImplicitBadSizedVLA; @@ -158,10 +147,6 @@ public: /// ObjC message expressions where the receiver is undefined (uninitialized). ErrorNodes UndefReceivers; - /// UndefArg - Nodes in the ExplodedGraph resulting from calls to functions - /// where a pass-by-value argument has an undefined value. - UndefArgsTy UndefArgs; - /// MsgExprUndefArgs - Nodes in the ExplodedGraph resulting from /// message expressions where a pass-by-value argument has an undefined /// value. @@ -195,6 +180,8 @@ public: BugReporter& getBugReporter() { return BR; } + GRStmtNodeBuilder &getBuilder() { assert(Builder); return *Builder; } + /// setTransferFunctions void setTransferFunctions(GRTransferFuncs* tf); @@ -217,8 +204,14 @@ public: void RegisterInternalChecks(); + template void registerCheck(Checker *check) { - Checkers.push_back(check); + Checkers[CHECKER::getTag()] = check; + } + + template + CHECKER *getChecker() { + return static_cast(Checkers[CHECKER::getTag()]); } bool isRetStackAddr(const ExplodedNode* N) const { @@ -234,15 +227,15 @@ public: } bool isImplicitNullDeref(const ExplodedNode* N) const { - return N->isSink() && ImplicitNullDeref.count(const_cast(N)) != 0; + return false; } bool isExplicitNullDeref(const ExplodedNode* N) const { - return N->isSink() && ExplicitNullDeref.count(const_cast(N)) != 0; + return false; } bool isUndefDeref(const ExplodedNode* N) const { - return N->isSink() && UndefDeref.count(const_cast(N)) != 0; + return false; } bool isNoReturnCall(const ExplodedNode* N) const { @@ -254,13 +247,11 @@ public: } bool isBadCall(const ExplodedNode* N) const { - return N->isSink() && BadCalls.count(const_cast(N)) != 0; + return false; } bool isUndefArg(const ExplodedNode* N) const { - return N->isSink() && - (UndefArgs.find(const_cast(N)) != UndefArgs.end() || - MsgExprUndefArgs.find(const_cast(N)) != MsgExprUndefArgs.end()); + return false; } bool isUndefReceiver(const ExplodedNode* N) const { @@ -279,17 +270,6 @@ public: undef_branch_iterator undef_branches_begin() { return UndefBranches.begin(); } undef_branch_iterator undef_branches_end() { return UndefBranches.end(); } - typedef ErrorNodes::iterator null_deref_iterator; - null_deref_iterator null_derefs_begin() { return ExplicitNullDeref.begin(); } - null_deref_iterator null_derefs_end() { return ExplicitNullDeref.end(); } - - null_deref_iterator implicit_null_derefs_begin() { - return ImplicitNullDeref.begin(); - } - null_deref_iterator implicit_null_derefs_end() { - return ImplicitNullDeref.end(); - } - typedef ErrorNodes::iterator nil_receiver_struct_ret_iterator; nil_receiver_struct_ret_iterator nil_receiver_struct_ret_begin() { @@ -312,10 +292,6 @@ public: return NilReceiverLargerThanVoidPtrRetExplicit.end(); } - typedef ErrorNodes::iterator undef_deref_iterator; - undef_deref_iterator undef_derefs_begin() { return UndefDeref.begin(); } - undef_deref_iterator undef_derefs_end() { return UndefDeref.end(); } - typedef ErrorNodes::iterator undef_result_iterator; undef_result_iterator undef_results_begin() { return UndefResults.begin(); } undef_result_iterator undef_results_end() { return UndefResults.end(); } @@ -325,9 +301,6 @@ public: bad_calls_iterator bad_calls_end() { return BadCalls.end(); } typedef UndefArgsTy::iterator undef_arg_iterator; - undef_arg_iterator undef_arg_begin() { return UndefArgs.begin(); } - undef_arg_iterator undef_arg_end() { return UndefArgs.end(); } - undef_arg_iterator msg_expr_undef_arg_begin() { return MsgExprUndefArgs.begin(); } @@ -427,7 +400,11 @@ public: protected: /// CheckerVisit - Dispatcher for performing checker-specific logic /// at specific statements. - void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, bool isPrevisit); + void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + bool isPrevisit); + + ExplodedNode *CheckerVisitLocation(Stmt *S, ExplodedNode *Pred, + const GRState *state, SVal V); /// Visit - Transfer function logic for all statements. Dispatches to /// other functions that handle specific kinds of statements. diff --git a/clang/include/clang/Analysis/PathSensitive/NullDerefChecker.h b/clang/include/clang/Analysis/PathSensitive/NullDerefChecker.h new file mode 100644 index 000000000000..05c4b360f882 --- /dev/null +++ b/clang/include/clang/Analysis/PathSensitive/NullDerefChecker.h @@ -0,0 +1,51 @@ +//== NullDerefChecker - Null dereference checker ----------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_NULLDEREFCHECKER +#define LLVM_CLANG_NULLDEREFCHECKER + +#include "clang/Analysis/PathSensitive/Checker.h" +#include "clang/Analysis/PathSensitive/BugType.h" + +namespace clang { + +class ExplodedNode; + +class NullDeref : public BuiltinBug { +public: + NullDeref() + : BuiltinBug(0, "Null dereference", "Dereference of null pointer") {} + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode* N, + BuiltinBugReport *R); +}; + +class NullDerefChecker : public Checker { + NullDeref *BT; + llvm::SmallVector ImplicitNullDerefNodes; + +public: + + NullDerefChecker() : BT(0) {} + ExplodedNode *CheckLocation(const Stmt *S, ExplodedNode *Pred, + const GRState *state, SVal V,GRExprEngine &Eng); + + static void *getTag() { + static int x = 0; + return &x; + } + + typedef llvm::SmallVectorImpl::iterator iterator; + iterator implicit_nodes_begin() { return ImplicitNullDerefNodes.begin(); } + iterator implicit_nodes_end() { return ImplicitNullDerefNodes.end(); } +}; + +} // end clang namespace +#endif diff --git a/clang/lib/Analysis/CheckNSError.cpp b/clang/lib/Analysis/CheckNSError.cpp index 8086da588264..2398285d396e 100644 --- a/clang/lib/Analysis/CheckNSError.cpp +++ b/clang/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 @@ void NSErrorCheck::CheckParamDeref(const VarDecl *Param, 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(); + 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(); diff --git a/clang/lib/Analysis/GRExprEngine.cpp b/clang/lib/Analysis/GRExprEngine.cpp index 9ed5ba5d02ac..c0aed2306e3d 100644 --- a/clang/lib/Analysis/GRExprEngine.cpp +++ b/clang/lib/Analysis/GRExprEngine.cpp @@ -118,17 +118,20 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; - for (std::vector::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { + for (llvm::DenseMap::iterator I = Checkers.begin(), + E = Checkers.end(); I != E; ++I) { - ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst + llvm::DenseMap::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 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, // automatically. } +ExplodedNode *GRExprEngine::CheckerVisitLocation(Stmt *S, ExplodedNode *Pred, + const GRState *state, SVal V) { + if (Checkers.empty()) + return Pred; + + for (llvm::DenseMap::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(AnalysisManager &mgr) GRExprEngine::~GRExprEngine() { BR.FlushReports(); delete [] NSExceptionInstanceRaiseSelectors; - for (std::vector::iterator I=Checkers.begin(), E=Checkers.end(); - I!=E; ++I) - delete *I; + for (llvm::DenseMap::iterator I=Checkers.begin(), + E=Checkers.end(); I!=E; ++I) + delete I->second; } //===----------------------------------------------------------------------===// @@ -1188,58 +1206,11 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred, SaveAndRestore 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(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(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 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred, } } #endif - - // Generate a new node indicating the checks succeed. - return Builder->generateNode(Ex, StNotNull, Pred, - ProgramPoint::PostLocationChecksSucceedKind); } //===----------------------------------------------------------------------===// @@ -2895,7 +2862,8 @@ namespace llvm { template<> struct VISIBILITY_HIDDEN DOTGraphTraits : 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/clang/lib/Analysis/GRExprEngineInternalChecks.cpp b/clang/lib/Analysis/GRExprEngineInternalChecks.cpp index da24192c9d5a..ca38b05df86f 100644 --- a/clang/lib/Analysis/GRExprEngineInternalChecks.cpp +++ b/clang/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 @@ ExplodedNode* GetNode(GRExprEngine::undef_arg_iterator I) { //===----------------------------------------------------------------------===// // 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 @@ public: 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 void Emit(BugReporter& BR, ITER I, ITER E); -}; - +void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode* N) { + static_cast(getBugType()).registerInitialVisitors(BRC, N, this); +} template 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(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 @@ public: } }; + + 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 @@ public: CheckAttrNonNull() : BT(0) {} ~CheckAttrNonNull() {} - const void *getTag() { + static void *getTag() { static int x = 0; return &x; } @@ -676,10 +637,9 @@ public: C.addTransition(C.GenerateNode(CE, state)); } }; -} // end anonymous namespace // Undefined arguments checking. -namespace { + class VISIBILITY_HIDDEN CheckUndefinedArg : public CheckerVisitor { @@ -689,7 +649,7 @@ public: CheckUndefinedArg() : BT(0) {} ~CheckUndefinedArg() {} - const void *getTag() { + static void *getTag() { static int x = 0; return &x; } @@ -721,7 +681,7 @@ public: CheckBadCall() : BT(0) {} ~CheckBadCall() {} - const void *getTag() { + static void *getTag() { static int x = 0; return &x; } @@ -748,7 +708,7 @@ public: CheckDivZero() : BT(0) {} ~CheckDivZero() {} - const void *getTag() { + static void *getTag() { static int x; return &x; } @@ -797,7 +757,85 @@ void CheckDivZero::PreVisitBinaryOperator(CheckerContext &C, 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(&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(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 @@ void GRExprEngine::RegisterInternalChecks() { // 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 @@ void GRExprEngine::RegisterInternalChecks() { // 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(new CheckAttrNonNull()); + registerCheck(new CheckUndefinedArg()); + registerCheck(new CheckBadCall()); + registerCheck(new CheckDivZero()); + registerCheck(new CheckUndefDeref()); + registerCheck(new NullDerefChecker()); }