diff options
Diffstat (limited to 'lib/Analysis')
58 files changed, 1497 insertions, 1100 deletions
diff --git a/lib/Analysis/AnalysisContext.cpp b/lib/Analysis/AnalysisContext.cpp index 640912a..339e2c9 100644 --- a/lib/Analysis/AnalysisContext.cpp +++ b/lib/Analysis/AnalysisContext.cpp @@ -18,21 +18,12 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/Support/BumpVector.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; -AnalysisContext::~AnalysisContext() { - delete cfg; - delete liveness; - delete PM; -} - -AnalysisContextManager::~AnalysisContextManager() { - for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) - delete I->second; -} - void AnalysisContextManager::clear() { for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) delete I->second; @@ -73,7 +64,7 @@ LiveVariables *AnalysisContext::getLiveVariables() { if (!c) return 0; - liveness = new LiveVariables(D->getASTContext(), *c); + liveness = new LiveVariables(*this); liveness->runOnCFG(*c); liveness->runOnAllBlocks(*c, 0, true); } @@ -157,3 +148,75 @@ ScopeContext *LocationContextManager::getScope(AnalysisContext *ctx, } return scope; } + +//===----------------------------------------------------------------------===// +// Lazily generated map to query the external variables referenced by a Block. +//===----------------------------------------------------------------------===// + +namespace { +class FindBlockDeclRefExprsVals : public StmtVisitor<FindBlockDeclRefExprsVals>{ + BumpVector<const VarDecl*> &BEVals; + BumpVectorContext &BC; +public: + FindBlockDeclRefExprsVals(BumpVector<const VarDecl*> &bevals, + BumpVectorContext &bc) + : BEVals(bevals), BC(bc) {} + + void VisitStmt(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end();I!=E;++I) + if (Stmt *child = *I) + Visit(child); + } + + void VisitBlockDeclRefExpr(BlockDeclRefExpr *DR) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + BEVals.push_back(VD, BC); + } +}; +} // end anonymous namespace + +typedef BumpVector<const VarDecl*> DeclVec; + +static DeclVec* LazyInitializeReferencedDecls(const BlockDecl *BD, + void *&Vec, + llvm::BumpPtrAllocator &A) { + if (Vec) + return (DeclVec*) Vec; + + BumpVectorContext BC(A); + DeclVec *BV = (DeclVec*) A.Allocate<DeclVec>(); + new (BV) DeclVec(BC, 10); + + // Find the referenced variables. + FindBlockDeclRefExprsVals F(*BV, BC); + F.Visit(BD->getBody()); + + Vec = BV; + return BV; +} + +std::pair<AnalysisContext::referenced_decls_iterator, + AnalysisContext::referenced_decls_iterator> +AnalysisContext::getReferencedBlockVars(const BlockDecl *BD) { + if (!ReferencedBlockVars) + ReferencedBlockVars = new llvm::DenseMap<const BlockDecl*,void*>(); + + DeclVec *V = LazyInitializeReferencedDecls(BD, (*ReferencedBlockVars)[BD], A); + return std::make_pair(V->begin(), V->end()); +} + +//===----------------------------------------------------------------------===// +// Cleanup. +//===----------------------------------------------------------------------===// + +AnalysisContext::~AnalysisContext() { + delete cfg; + delete liveness; + delete PM; + delete ReferencedBlockVars; +} + +AnalysisContextManager::~AnalysisContextManager() { + for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) + delete I->second; +} diff --git a/lib/Analysis/ArrayBoundChecker.cpp b/lib/Analysis/ArrayBoundChecker.cpp index 549a22b..3d95ab1 100644 --- a/lib/Analysis/ArrayBoundChecker.cpp +++ b/lib/Analysis/ArrayBoundChecker.cpp @@ -20,7 +20,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN ArrayBoundChecker : +class ArrayBoundChecker : public CheckerVisitor<ArrayBoundChecker> { BuiltinBug *BT; public: @@ -62,8 +62,7 @@ void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.GenerateNode(S, StOutBound, true); - + ExplodedNode *N = C.GenerateSink(StOutBound); if (!N) return; @@ -80,7 +79,12 @@ void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ new RangedBugReport(*BT, BT->getDescription(), N); report->addRange(S->getSourceRange()); - C.EmitReport(report); + return; } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + assert(StInBound); + C.addTransition(StInBound); } diff --git a/lib/Analysis/AttrNonNullChecker.cpp b/lib/Analysis/AttrNonNullChecker.cpp index 01e1a1f..aa21700 100644 --- a/lib/Analysis/AttrNonNullChecker.cpp +++ b/lib/Analysis/AttrNonNullChecker.cpp @@ -19,7 +19,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN AttrNonNullChecker +class AttrNonNullChecker : public CheckerVisitor<AttrNonNullChecker> { BugType *BT; public: @@ -39,7 +39,6 @@ void clang::RegisterAttrNonNullChecker(GRExprEngine &Eng) { void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); - const GRState *originalState = state; // Check if the callee has a 'nonnull' attribute. SVal X = state->getSVal(CE->getCallee()); @@ -74,7 +73,7 @@ void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, if (stateNull && !stateNotNull) { // Generate an error node. Check for a null node in case // we cache out. - if (ExplodedNode *errorNode = C.GenerateNode(CE, stateNull, true)) { + if (ExplodedNode *errorNode = C.GenerateSink(stateNull)) { // Lazily allocate the BugType object if it hasn't already been // created. Ownership is transferred to the BugReporter object once @@ -109,6 +108,5 @@ void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, // If we reach here all of the arguments passed the nonnull check. // If 'state' has been updated generated a new node. - if (state != originalState) - C.addTransition(C.GenerateNode(CE, state)); + C.addTransition(state); } diff --git a/lib/Analysis/BadCallChecker.cpp b/lib/Analysis/BadCallChecker.cpp deleted file mode 100644 index 7a7ea18..0000000 --- a/lib/Analysis/BadCallChecker.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//===--- BadCallChecker.h - Bad call checker --------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines BadCallChecker, a builtin check in GRExprEngine that performs -// checks for bad callee at call sites. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class VISIBILITY_HIDDEN BadCallChecker : public CheckerVisitor<BadCallChecker> { - BuiltinBug *BT; -public: - BadCallChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); -}; -} // end anonymous namespace - -void clang::RegisterBadCallChecker(GRExprEngine &Eng) { - Eng.registerCheck(new BadCallChecker()); -} - -void BadCallChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { - const Expr *Callee = CE->getCallee()->IgnoreParens(); - SVal L = C.getState()->getSVal(Callee); - - if (L.isUndef() || isa<loc::ConcreteInt>(L)) { - if (ExplodedNode *N = C.GenerateNode(CE, true)) { - if (!BT) - BT = new BuiltinBug("Invalid function call", - "Called function pointer is a null or undefined pointer value"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription(), N); - - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetCalleeExpr(N)); - - C.EmitReport(R); - } - } -} diff --git a/lib/Analysis/BasicConstraintManager.cpp b/lib/Analysis/BasicConstraintManager.cpp index d0b8289..6c3f7b2 100644 --- a/lib/Analysis/BasicConstraintManager.cpp +++ b/lib/Analysis/BasicConstraintManager.cpp @@ -16,14 +16,13 @@ #include "clang/Analysis/PathSensitive/GRState.h" #include "clang/Analysis/PathSensitive/GRStateTrait.h" #include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" using namespace clang; -namespace { class VISIBILITY_HIDDEN ConstNotEq {}; } -namespace { class VISIBILITY_HIDDEN ConstEq {}; } +namespace { class ConstNotEq {}; } +namespace { class ConstEq {}; } typedef llvm::ImmutableMap<SymbolRef,GRState::IntSetTy> ConstNotEqTy; typedef llvm::ImmutableMap<SymbolRef,const llvm::APSInt*> ConstEqTy; @@ -46,7 +45,7 @@ struct GRStateTrait<ConstEq> : public GRStatePartialTrait<ConstEqTy> { namespace { // BasicConstraintManager only tracks equality and inequality constraints of // constants and integer variables. -class VISIBILITY_HIDDEN BasicConstraintManager +class BasicConstraintManager : public SimpleConstraintManager { GRState::IntSetTy::Factory ISetFactory; public: diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp index c2ecfa1..c913779 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -22,12 +22,12 @@ #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/MemRegion.h" #include "clang/Analysis/PathDiagnostic.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/LocalCheckers.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ASTContext.h" -#include "llvm/Support/Compiler.h" using namespace clang; @@ -52,12 +52,12 @@ static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { namespace { -class VISIBILITY_HIDDEN APIMisuse : public BugType { +class APIMisuse : public BugType { public: APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} }; -class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck { +class BasicObjCFoundationChecks : public GRSimpleAPICheck { APIMisuse *BT; BugReporter& BR; ASTContext &Ctx; @@ -87,7 +87,7 @@ private: // by the BugReporter object 'BR' once we call BR.EmitWarning. if (!BT) BT = new APIMisuse("nil argument"); - RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N); + RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); R->addRange(ME->getArg(Arg)->getSourceRange()); BR.EmitReport(R); } @@ -228,7 +228,7 @@ bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, namespace { -class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck { +class AuditCFNumberCreate : public GRSimpleAPICheck { APIMisuse* BT; // FIXME: Either this should be refactored into GRSimpleAPICheck, or @@ -435,7 +435,7 @@ void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex, // Lazily create the BugType object. This will be owned // by the BugReporter object 'BR' once we call BR.EmitWarning. if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); - RangedBugReport *report = new RangedBugReport(*BT, os.str().c_str(), N); + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(Ex->getSourceRange()); BR.EmitReport(report); } @@ -450,7 +450,7 @@ clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) { //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck { +class AuditCFRetainRelease : public GRSimpleAPICheck { APIMisuse *BT; // FIXME: Either this should be refactored into GRSimpleAPICheck, or @@ -522,6 +522,64 @@ clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { } //===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class ClassReleaseChecker : + public CheckerVisitor<ClassReleaseChecker> { + Selector releaseS; + Selector retainS; + Selector autoreleaseS; + Selector drainS; + BugType *BT; +public: + ClassReleaseChecker(ASTContext &Ctx) + : releaseS(GetNullarySelector("release", Ctx)), + retainS(GetNullarySelector("retain", Ctx)), + autoreleaseS(GetNullarySelector("autorelease", Ctx)), + drainS(GetNullarySelector("drain", Ctx)), + BT(0) {} + + static void *getTag() { static int x = 0; return &x; } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; +} + +void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const IdentifierInfo *ClsName = ME->getClassName(); + if (!ClsName) + return; + + Selector S = ME->getSelector(); + if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) + return; + + if (!BT) + BT = new APIMisuse("message incorrectly sent to class instead of class " + "instance"); + + ExplodedNode *N = C.GenerateNode(); + + if (!N) + return; + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "The '" << S.getAsString() << "' message should be sent to instances " + "of class '" << ClsName->getName() + << "' and not the class directly"; + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(ME->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// @@ -536,4 +594,5 @@ void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { RegisterNSErrorChecks(BR, Eng, D); RegisterNSAutoreleasePoolChecks(Eng); + Eng.registerCheck(new ClassReleaseChecker(Ctx)); } diff --git a/lib/Analysis/BasicObjCFoundationChecks.h b/lib/Analysis/BasicObjCFoundationChecks.h index ea4d3ec..679c6dc 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.h +++ b/lib/Analysis/BasicObjCFoundationChecks.h @@ -13,24 +13,16 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/ExplodedGraph.h" -#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ASTContext.h" -#include "llvm/Support/Compiler.h" - #ifndef LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS #define LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS namespace clang { -class GRSimpleAPICheck; class ASTContext; -class GRStateManager; class BugReporter; +class Decl; class GRExprEngine; +class GRSimpleAPICheck; GRSimpleAPICheck *CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR); diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp index 800a76f..45fc11a 100644 --- a/lib/Analysis/BasicStore.cpp +++ b/lib/Analysis/BasicStore.cpp @@ -15,7 +15,6 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/PathSensitive/AnalysisContext.h" #include "clang/Analysis/PathSensitive/GRState.h" -#include "llvm/Support/Compiler.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; @@ -24,7 +23,7 @@ typedef llvm::ImmutableMap<const MemRegion*,SVal> BindingsTy; namespace { -class VISIBILITY_HIDDEN BasicStoreSubRegionMap : public SubRegionMap { +class BasicStoreSubRegionMap : public SubRegionMap { public: BasicStoreSubRegionMap() {} @@ -33,7 +32,7 @@ public: } }; -class VISIBILITY_HIDDEN BasicStoreManager : public StoreManager { +class BasicStoreManager : public StoreManager { BindingsTy::Factory VBFactory; public: BasicStoreManager(GRStateManager& mgr) diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp index 8235f4a..c26a60a 100644 --- a/lib/Analysis/BugReporter.cpp +++ b/lib/Analysis/BugReporter.cpp @@ -119,7 +119,7 @@ typedef llvm::DenseMap<const ExplodedNode*, const ExplodedNode*> NodeBackMap; namespace { -class VISIBILITY_HIDDEN NodeMapClosure : public BugReport::NodeResolver { +class NodeMapClosure : public BugReport::NodeResolver { NodeBackMap& M; public: NodeMapClosure(NodeBackMap *m) : M(*m) {} @@ -131,7 +131,7 @@ public: } }; -class VISIBILITY_HIDDEN PathDiagnosticBuilder : public BugReporterContext { +class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; PathDiagnosticClient *PDC; llvm::OwningPtr<ParentMap> PM; @@ -358,7 +358,7 @@ GetMostRecentVarDeclBinding(const ExplodedNode* N, } namespace { -class VISIBILITY_HIDDEN NotableSymbolHandler +class NotableSymbolHandler : public StoreManager::BindingsHandler { SymbolRef Sym; @@ -458,7 +458,7 @@ static void HandleNotableSymbol(const ExplodedNode* N, } namespace { -class VISIBILITY_HIDDEN ScanNotableSymbols +class ScanNotableSymbols : public StoreManager::BindingsHandler { llvm::SmallSet<SymbolRef, 10> AlreadyProcessed; @@ -802,7 +802,7 @@ static bool IsControlFlowExpr(const Stmt *S) { } namespace { -class VISIBILITY_HIDDEN ContextLocation : public PathDiagnosticLocation { +class ContextLocation : public PathDiagnosticLocation { bool IsDead; public: ContextLocation(const PathDiagnosticLocation &L, bool isdead = false) @@ -812,7 +812,7 @@ public: bool isDead() const { return IsDead; } }; -class VISIBILITY_HIDDEN EdgeBuilder { +class EdgeBuilder { std::vector<ContextLocation> CLocs; typedef std::vector<ContextLocation>::iterator iterator; PathDiagnostic &PD; @@ -1645,7 +1645,7 @@ void BugReporter::EmitReport(BugReport* R) { //===----------------------------------------------------------------------===// namespace { -struct VISIBILITY_HIDDEN FRIEC_WLItem { +struct FRIEC_WLItem { const ExplodedNode *N; ExplodedNode::const_succ_iterator I, E; @@ -1738,7 +1738,7 @@ static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) { // uses global state, which eventually should go elsewhere. //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN DiagCacheItem : public llvm::FoldingSetNode { +class DiagCacheItem : public llvm::FoldingSetNode { llvm::FoldingSetNodeID ID; public: DiagCacheItem(BugReport *R, PathDiagnostic *PD) { @@ -1835,14 +1835,15 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { PD->HandlePathDiagnostic(D.take()); } -void BugReporter::EmitBasicReport(const char* name, const char* str, +void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, SourceLocation Loc, SourceRange* RBeg, unsigned NumRanges) { EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); } -void BugReporter::EmitBasicReport(const char* name, const char* category, - const char* str, SourceLocation Loc, +void BugReporter::EmitBasicReport(llvm::StringRef name, + llvm::StringRef category, + llvm::StringRef str, SourceLocation Loc, SourceRange* RBeg, unsigned NumRanges) { // 'BT' will be owned by BugReporter as soon as we call 'EmitReport'. diff --git a/lib/Analysis/BugReporterVisitors.cpp b/lib/Analysis/BugReporterVisitors.cpp index 89c9ca1..87de30a 100644 --- a/lib/Analysis/BugReporterVisitors.cpp +++ b/lib/Analysis/BugReporterVisitors.cpp @@ -83,7 +83,7 @@ clang::bugreporter::GetRetValExpr(const ExplodedNode *N) { //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN FindLastStoreBRVisitor : public BugReporterVisitor { +class FindLastStoreBRVisitor : public BugReporterVisitor { const MemRegion *R; SVal V; bool satisfied; @@ -231,7 +231,7 @@ static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); } -class VISIBILITY_HIDDEN TrackConstraintBRVisitor : public BugReporterVisitor { +class TrackConstraintBRVisitor : public BugReporterVisitor { DefinedSVal Constraint; const bool Assumption; bool isSatisfied; diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 3141759..c97692f 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -17,7 +17,6 @@ #include "clang/AST/StmtVisitor.h" #include "clang/AST/PrettyPrinter.h" #include "llvm/Support/GraphWriter.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Format.h" #include "llvm/ADT/DenseMap.h" @@ -50,7 +49,7 @@ static SourceLocation GetEndLoc(Decl* D) { /// constructed prior to its predecessor. This allows us to nicely capture /// implicit fall-throughs without extra basic blocks. /// -class VISIBILITY_HIDDEN CFGBuilder { +class CFGBuilder { ASTContext *Context; llvm::OwningPtr<CFG> cfg; @@ -461,9 +460,12 @@ CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, bool alwaysAdd) { return VisitStmt(B, alwaysAdd); } -CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr* E, bool alwaysAdd) { - // FIXME - return NYS(); +CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, bool alwaysAdd) { + if (alwaysAdd) { + autoCreateBlock(); + AppendStmt(Block, E); + } + return Block; } CFGBlock *CFGBuilder::VisitBlockDeclRefExpr(BlockDeclRefExpr* E, @@ -1624,7 +1626,7 @@ CFG::~CFG() { namespace { -class VISIBILITY_HIDDEN StmtPrinterHelper : public PrinterHelper { +class StmtPrinterHelper : public PrinterHelper { typedef llvm::DenseMap<Stmt*,std::pair<unsigned,unsigned> > StmtMapTy; StmtMapTy StmtMap; @@ -1668,7 +1670,7 @@ public: namespace { -class VISIBILITY_HIDDEN CFGBlockTerminatorPrint +class CFGBlockTerminatorPrint : public StmtVisitor<CFGBlockTerminatorPrint,void> { llvm::raw_ostream& OS; @@ -2047,8 +2049,10 @@ void CFG::viewCFG(const LangOptions &LO) const { namespace llvm { template<> struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits { - static std::string getNodeLabel(const CFGBlock* Node, const CFG* Graph, - bool ShortNames) { + + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + + static std::string getNodeLabel(const CFGBlock* Node, const CFG* Graph) { #ifndef NDEBUG std::string OutSStr; diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp index 55e5f17..b95f981 100644 --- a/lib/Analysis/CFRefCount.cpp +++ b/lib/Analysis/CFRefCount.cpp @@ -22,13 +22,14 @@ #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/SymbolManager.h" #include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtVisitor.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Compiler.h" #include "llvm/ADT/STLExtras.h" #include <stdarg.h> @@ -168,7 +169,7 @@ ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { } namespace { -class VISIBILITY_HIDDEN GenericNodeBuilder { +class GenericNodeBuilder { GRStmtNodeBuilder *SNB; Stmt *S; const void *tag; @@ -246,7 +247,7 @@ namespace { /// RetEffect is used to summarize a function/method call's behavior with /// respect to its return value. -class VISIBILITY_HIDDEN RetEffect { +class RetEffect { public: enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, @@ -312,7 +313,7 @@ public: // Reference-counting logic (typestate + counts). //===----------------------------------------------------------------------===// -class VISIBILITY_HIDDEN RefVal { +class RefVal { public: enum Kind { Owned = 0, // Owning reference. @@ -536,7 +537,7 @@ namespace clang { //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN RetainSummary { +class RetainSummary { /// Args - an ordered vector of (index, ArgEffect) pairs, where index /// specifies the argument (starting from 0). This can be sparsely /// populated; arguments with no entry in Args use 'DefaultArgEffect'. @@ -627,7 +628,7 @@ public: //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN ObjCSummaryKey { +class ObjCSummaryKey { IdentifierInfo* II; Selector S; public: @@ -682,7 +683,7 @@ template <> struct DenseMapInfo<ObjCSummaryKey> { } // end llvm namespace namespace { -class VISIBILITY_HIDDEN ObjCSummaryCache { +class ObjCSummaryCache { typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy; MapTy M; public: @@ -776,7 +777,7 @@ public: //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN RetainSummaryManager { +class RetainSummaryManager { //==-----------------------------------------------------------------==// // Typedefs. @@ -1865,8 +1866,8 @@ typedef llvm::ImmutableList<SymbolRef> ARStack; static int AutoRCIndex = 0; static int AutoRBIndex = 0; -namespace { class VISIBILITY_HIDDEN AutoreleasePoolContents {}; } -namespace { class VISIBILITY_HIDDEN AutoreleaseStack {}; } +namespace { class AutoreleasePoolContents {}; } +namespace { class AutoreleaseStack {}; } namespace clang { template<> struct GRStateTrait<AutoreleaseStack> @@ -1908,7 +1909,7 @@ static const GRState * SendAutorelease(const GRState *state, namespace { -class VISIBILITY_HIDDEN CFRefCount : public GRTransferFuncs { +class CFRefCount : public GRTransferFuncs { public: class BindingsPrinter : public GRState::Printer { public: @@ -2093,11 +2094,11 @@ namespace { // Bug Descriptions. // //===-------------===// - class VISIBILITY_HIDDEN CFRefBug : public BugType { + class CFRefBug : public BugType { protected: CFRefCount& TF; - CFRefBug(CFRefCount* tf, const char* name) + CFRefBug(CFRefCount* tf, llvm::StringRef name) : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} public: @@ -2110,7 +2111,7 @@ namespace { virtual bool isLeak() const { return false; } }; - class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug { + class UseAfterRelease : public CFRefBug { public: UseAfterRelease(CFRefCount* tf) : CFRefBug(tf, "Use-after-release") {} @@ -2120,7 +2121,7 @@ namespace { } }; - class VISIBILITY_HIDDEN BadRelease : public CFRefBug { + class BadRelease : public CFRefBug { public: BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {} @@ -2130,7 +2131,7 @@ namespace { } }; - class VISIBILITY_HIDDEN DeallocGC : public CFRefBug { + class DeallocGC : public CFRefBug { public: DeallocGC(CFRefCount *tf) : CFRefBug(tf, "-dealloc called while using garbage collection") {} @@ -2140,7 +2141,7 @@ namespace { } }; - class VISIBILITY_HIDDEN DeallocNotOwned : public CFRefBug { + class DeallocNotOwned : public CFRefBug { public: DeallocNotOwned(CFRefCount *tf) : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} @@ -2150,7 +2151,7 @@ namespace { } }; - class VISIBILITY_HIDDEN OverAutorelease : public CFRefBug { + class OverAutorelease : public CFRefBug { public: OverAutorelease(CFRefCount *tf) : CFRefBug(tf, "Object sent -autorelease too many times") {} @@ -2160,7 +2161,7 @@ namespace { } }; - class VISIBILITY_HIDDEN ReturnedNotOwnedForOwned : public CFRefBug { + class ReturnedNotOwnedForOwned : public CFRefBug { public: ReturnedNotOwnedForOwned(CFRefCount *tf) : CFRefBug(tf, "Method should return an owned object") {} @@ -2171,10 +2172,10 @@ namespace { } }; - class VISIBILITY_HIDDEN Leak : public CFRefBug { + class Leak : public CFRefBug { const bool isReturn; protected: - Leak(CFRefCount* tf, const char* name, bool isRet) + Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) : CFRefBug(tf, name), isReturn(isRet) {} public: @@ -2183,15 +2184,15 @@ namespace { bool isLeak() const { return true; } }; - class VISIBILITY_HIDDEN LeakAtReturn : public Leak { + class LeakAtReturn : public Leak { public: - LeakAtReturn(CFRefCount* tf, const char* name) + LeakAtReturn(CFRefCount* tf, llvm::StringRef name) : Leak(tf, name, true) {} }; - class VISIBILITY_HIDDEN LeakWithinFunction : public Leak { + class LeakWithinFunction : public Leak { public: - LeakWithinFunction(CFRefCount* tf, const char* name) + LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) : Leak(tf, name, false) {} }; @@ -2199,7 +2200,7 @@ namespace { // Bug Reports. // //===---------===// - class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport { + class CFRefReport : public RangedBugReport { protected: SymbolRef Sym; const CFRefCount &TF; @@ -2209,7 +2210,7 @@ namespace { : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, const char* endText) + ExplodedNode *n, SymbolRef sym, llvm::StringRef endText) : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {} virtual ~CFRefReport() {} @@ -2240,7 +2241,7 @@ namespace { BugReporterContext& BRC); }; - class VISIBILITY_HIDDEN CFRefLeakReport : public CFRefReport { + class CFRefLeakReport : public CFRefReport { SourceLocation AllocSite; const MemRegion* AllocBinding; public: @@ -2255,64 +2256,7 @@ namespace { }; } // end anonymous namespace -void CFRefCount::RegisterChecks(GRExprEngine& Eng) { - BugReporter &BR = Eng.getBugReporter(); - - useAfterRelease = new UseAfterRelease(this); - BR.Register(useAfterRelease); - - releaseNotOwned = new BadRelease(this); - BR.Register(releaseNotOwned); - - deallocGC = new DeallocGC(this); - BR.Register(deallocGC); - - deallocNotOwned = new DeallocNotOwned(this); - BR.Register(deallocNotOwned); - - overAutorelease = new OverAutorelease(this); - BR.Register(overAutorelease); - - returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); - BR.Register(returnNotOwnedForOwned); - - // First register "return" leaks. - const char* name = 0; - - if (isGCEnabled()) - name = "Leak of returned object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of returned object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak of returned object"; - } - - // Leaks should not be reported if they are post-dominated by a sink. - leakAtReturn = new LeakAtReturn(this, name); - leakAtReturn->setSuppressOnSink(true); - BR.Register(leakAtReturn); - - // Second, register leaks within a function/method. - if (isGCEnabled()) - name = "Leak of object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak"; - } - - // Leaks should not be reported if they are post-dominated by sinks. - leakWithinFunction = new LeakWithinFunction(this, name); - leakWithinFunction->setSuppressOnSink(true); - BR.Register(leakWithinFunction); - // Save the reference to the BugReporter. - this->BR = &BR; -} static const char* Msgs[] = { // GC only @@ -2603,7 +2547,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, } namespace { - class VISIBILITY_HIDDEN FindUniqueBinding : + class FindUniqueBinding : public StoreManager::BindingsHandler { SymbolRef Sym; const MemRegion* Binding; @@ -3052,9 +2996,20 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, CallExpr* CE, SVal L, ExplodedNode* Pred) { - const FunctionDecl* FD = L.getAsFunctionDecl(); - RetainSummary* Summ = !FD ? Summaries.getDefaultSummary() - : Summaries.getSummary(const_cast<FunctionDecl*>(FD)); + + RetainSummary *Summ = 0; + + // FIXME: Better support for blocks. For now we stop tracking anything + // that is passed to blocks. + // FIXME: Need to handle variables that are "captured" by the block. + if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { + Summ = Summaries.getPersistentStopSummary(); + } + else { + const FunctionDecl* FD = L.getAsFunctionDecl(); + Summ = !FD ? Summaries.getDefaultSummary() : + Summaries.getSummary(const_cast<FunctionDecl*>(FD)); + } assert(Summ); EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, @@ -3066,6 +3021,16 @@ void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, ExplodedNode* Pred) { + // FIXME: Since we moved the nil check into a checker, we could get nil + // receiver here. Need a better way to check such case. + if (Expr* Receiver = ME->getReceiver()) { + const GRState *state = Pred->getState(); + DefinedOrUnknownSVal L=cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); + if (!state->Assume(L, true)) { + Dst.Add(Pred); + return; + } + } RetainSummary *Summ = ME->getReceiver() @@ -3079,7 +3044,7 @@ void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, } namespace { -class VISIBILITY_HIDDEN StopTrackingCallback : public SymbolVisitor { +class StopTrackingCallback : public SymbolVisitor { const GRState *state; public: StopTrackingCallback(const GRState *st) : state(st) {} @@ -3501,7 +3466,7 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd CFRefReport *report = new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), - *this, N, Sym, os.str().c_str()); + *this, N, Sym, os.str()); BR->EmitReport(report); } @@ -3670,9 +3635,114 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, } //===----------------------------------------------------------------------===// +// Pieces of the retain/release checker implemented using a CheckerVisitor. +// More pieces of the retain/release checker will be migrated to this interface +// (ideally, all of it some day). +//===----------------------------------------------------------------------===// + +namespace { +class RetainReleaseChecker + : public CheckerVisitor<RetainReleaseChecker> { + CFRefCount *TF; +public: + RetainReleaseChecker(CFRefCount *tf) : TF(tf) {} + static void* getTag() { static int x = 0; return &x; } + + void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); +}; +} // end anonymous namespace + + +void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C, + const BlockExpr *BE) { + + // Scan the BlockDecRefExprs for any object the retain/release checker + // may be tracking. + if (!BE->hasBlockDeclRefExprs()) + return; + + const GRState *state = C.getState(); + const BlockDataRegion *R = + cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + if (I == E) + return; + + state = state->scanReachableSymbols<StopTrackingCallback>(I, E).getState(); + C.addTransition(state); +} + +//===----------------------------------------------------------------------===// // Transfer function creation for external clients. //===----------------------------------------------------------------------===// +void CFRefCount::RegisterChecks(GRExprEngine& Eng) { + BugReporter &BR = Eng.getBugReporter(); + + useAfterRelease = new UseAfterRelease(this); + BR.Register(useAfterRelease); + + releaseNotOwned = new BadRelease(this); + BR.Register(releaseNotOwned); + + deallocGC = new DeallocGC(this); + BR.Register(deallocGC); + + deallocNotOwned = new DeallocNotOwned(this); + BR.Register(deallocNotOwned); + + overAutorelease = new OverAutorelease(this); + BR.Register(overAutorelease); + + returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); + BR.Register(returnNotOwnedForOwned); + + // First register "return" leaks. + const char* name = 0; + + if (isGCEnabled()) + name = "Leak of returned object when using garbage collection"; + else if (getLangOptions().getGCMode() == LangOptions::HybridGC) + name = "Leak of returned object when not using garbage collection (GC) in " + "dual GC/non-GC code"; + else { + assert(getLangOptions().getGCMode() == LangOptions::NonGC); + name = "Leak of returned object"; + } + + // Leaks should not be reported if they are post-dominated by a sink. + leakAtReturn = new LeakAtReturn(this, name); + leakAtReturn->setSuppressOnSink(true); + BR.Register(leakAtReturn); + + // Second, register leaks within a function/method. + if (isGCEnabled()) + name = "Leak of object when using garbage collection"; + else if (getLangOptions().getGCMode() == LangOptions::HybridGC) + name = "Leak of object when not using garbage collection (GC) in " + "dual GC/non-GC code"; + else { + assert(getLangOptions().getGCMode() == LangOptions::NonGC); + name = "Leak"; + } + + // Leaks should not be reported if they are post-dominated by sinks. + leakWithinFunction = new LeakWithinFunction(this, name); + leakWithinFunction->setSuppressOnSink(true); + BR.Register(leakWithinFunction); + + // Save the reference to the BugReporter. + this->BR = &BR; + + // Register the RetainReleaseChecker with the GRExprEngine object. + // Functionality in CFRefCount will be migrated to RetainReleaseChecker + // over time. + Eng.registerCheck(new RetainReleaseChecker(this)); +} + GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, const LangOptions& lopts) { return new CFRefCount(Ctx, GCEnabled, lopts); diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 8e8c1e7..409292d 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -4,7 +4,6 @@ add_clang_library(clangAnalysis AnalysisContext.cpp ArrayBoundChecker.cpp AttrNonNullChecker.cpp - BadCallChecker.cpp BasicConstraintManager.cpp BasicObjCFoundationChecks.cpp BasicStore.cpp @@ -13,6 +12,7 @@ add_clang_library(clangAnalysis BugReporterVisitors.cpp CFG.cpp CFRefCount.cpp + CallAndMessageChecker.cpp CallGraph.cpp CallInliner.cpp CastToStructChecker.cpp @@ -22,6 +22,7 @@ add_clang_library(clangAnalysis CheckObjCUnusedIVars.cpp CheckSecuritySyntaxOnly.cpp CheckSizeofPointer.cpp + Checker.cpp DereferenceChecker.cpp DivZeroChecker.cpp Environment.cpp @@ -31,7 +32,6 @@ add_clang_library(clangAnalysis GRCoreEngine.cpp GRExprEngine.cpp GRExprEngineExperimentalChecks.cpp - GRExprEngineInternalChecks.cpp GRState.cpp LiveVariables.cpp MallocChecker.cpp @@ -54,7 +54,8 @@ add_clang_library(clangAnalysis SimpleSValuator.cpp Store.cpp SymbolManager.cpp - UndefinedArgChecker.cpp + UndefBranchChecker.cpp + UndefResultChecker.cpp UndefinedArraySubscriptChecker.cpp UndefinedAssignmentChecker.cpp UninitializedValues.cpp diff --git a/lib/Analysis/CallAndMessageChecker.cpp b/lib/Analysis/CallAndMessageChecker.cpp new file mode 100644 index 0000000..d8dd16c --- /dev/null +++ b/lib/Analysis/CallAndMessageChecker.cpp @@ -0,0 +1,267 @@ +//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CallAndMessageChecker, a builtin checker that checks for various +// errors of call and objc message expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/AST/ParentMap.h" +#include "GRExprEngineInternalChecks.h" + +using namespace clang; + +namespace { +class CallAndMessageChecker + : public CheckerVisitor<CallAndMessageChecker> { + BugType *BT_call_null; + BugType *BT_call_undef; + BugType *BT_call_arg; + BugType *BT_msg_undef; + BugType *BT_msg_arg; + BugType *BT_msg_ret; +public: + CallAndMessageChecker() : + BT_call_null(0), BT_call_undef(0), BT_call_arg(0), + BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + +private: + void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); + void EmitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME, + ExplodedNode *N); + + void HandleNilReceiver(CheckerContext &C, const GRState *state, + const ObjCMessageExpr *ME); +}; +} // end anonymous namespace + +void clang::RegisterCallAndMessageChecker(GRExprEngine &Eng) { + Eng.registerCheck(new CallAndMessageChecker()); +} + +void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, + const CallExpr *CE) { + ExplodedNode *N = C.GenerateSink(); + if (!N) + return; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetCalleeExpr(N)); + C.EmitReport(R); +} + +void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE){ + + const Expr *Callee = CE->getCallee()->IgnoreParens(); + SVal L = C.getState()->getSVal(Callee); + + if (L.isUndef()) { + if (!BT_call_undef) + BT_call_undef = + new BuiltinBug("Called function pointer is an undefined pointer value"); + EmitBadCall(BT_call_undef, C, CE); + return; + } + + if (isa<loc::ConcreteInt>(L)) { + if (!BT_call_null) + BT_call_null = + new BuiltinBug("Called function pointer is null (null dereference)"); + EmitBadCall(BT_call_null, C, CE); + } + + for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) { + if (C.getState()->getSVal(*I).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_call_arg) + BT_call_arg = new BuiltinBug("Pass-by-value argument in function call" + " is undefined"); + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT_call_arg, + BT_call_arg->getName(), N); + R->addRange((*I)->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); + C.EmitReport(R); + return; + } + } + } +} + +void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const GRState *state = C.getState(); + + if (const Expr *receiver = ME->getReceiver()) + if (state->getSVal(receiver).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_msg_undef) + BT_msg_undef = + new BuiltinBug("Receiver in message expression is a garbage value"); + EnhancedBugReport *R = + new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); + R->addRange(receiver->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + C.EmitReport(R); + } + return; + } + + // Check for any arguments that are uninitialized/undefined. + for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), + E = ME->arg_end(); I != E; ++I) { + if (state->getSVal(*I).isUndef()) { + if (ExplodedNode *N = C.GenerateSink()) { + if (!BT_msg_arg) + BT_msg_arg = + new BuiltinBug("Pass-by-value argument in message expression" + " is undefined"); + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT_msg_arg, + BT_msg_arg->getName(), N); + R->addRange((*I)->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); + C.EmitReport(R); + return; + } + } + } + + // Check if the receiver was nil and then returns a value that may + // be garbage. + if (const Expr *Receiver = ME->getReceiver()) { + DefinedOrUnknownSVal receiverVal = + cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); + + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->Assume(receiverVal); + + if (nullState && !notNullState) { + HandleNilReceiver(C, nullState, ME); + C.setDoneEvaluating(); // FIXME: eventually remove. + return; + } + + assert(notNullState); + state = notNullState; + } + + // Add a state transition if the state has changed. + C.addTransition(state); +} + +void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C, + const ObjCMessageExpr *ME, + ExplodedNode *N) { + + if (!BT_msg_ret) + BT_msg_ret = + new BuiltinBug("Receiver in message expression is " + "'nil' and returns a garbage value"); + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "The receiver of message '" << ME->getSelector().getAsString() + << "' is nil and returns a value of type '" + << ME->getType().getAsString() << "' that will be garbage"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); + const Expr *receiver = ME->getReceiver(); + report->addRange(receiver->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + C.EmitReport(report); +} + +static bool SupportsNilWithFloatRet(const llvm::Triple &triple) { + return triple.getVendor() == llvm::Triple::Apple && + triple.getDarwinMajorNumber() >= 9; +} + +void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, + const GRState *state, + const ObjCMessageExpr *ME) { + + // Check the return type of the message expression. A message to nil will + // return different values depending on the return type and the architecture. + QualType RetTy = ME->getType(); + + ASTContext &Ctx = C.getASTContext(); + CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); + + if (CanRetTy->isStructureType()) { + // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead + // have the "use of undefined value" be smarter about where the + // undefined value came from. + if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + if (ExplodedNode* N = C.GenerateSink(state)) + EmitNilReceiverBug(C, ME, N); + return; + } + + // The result is not consumed by a surrounding expression. Just propagate + // the current state. + C.addTransition(state); + return; + } + + // Other cases: check if the return type is smaller than void*. + if (CanRetTy != Ctx.VoidTy && + C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + // Compute: sizeof(void *) and sizeof(return type) + const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); + const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); + + if (voidPtrSize < returnTypeSize && + !(SupportsNilWithFloatRet(Ctx.Target.getTriple()) && + (Ctx.FloatTy == CanRetTy || + Ctx.DoubleTy == CanRetTy || + Ctx.LongDoubleTy == CanRetTy || + Ctx.LongLongTy == CanRetTy))) { + if (ExplodedNode* N = C.GenerateSink(state)) + EmitNilReceiverBug(C, ME, N); + return; + } + + // Handle the safe cases where the return value is 0 if the + // receiver is nil. + // + // FIXME: For now take the conservative approach that we only + // return null values if we *know* that the receiver is nil. + // This is because we can have surprises like: + // + // ... = [[NSScreens screens] objectAtIndex:0]; + // + // What can happen is that [... screens] could return nil, but + // it most likely isn't nil. We should assume the semantics + // of this case unless we have *a lot* more knowledge. + // + SVal V = C.getValueManager().makeZeroVal(ME->getType()); + C.GenerateNode(state->BindExpr(ME, V)); + return; + } + + C.addTransition(state); +} diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp index 06e3317..c1040f0 100644 --- a/lib/Analysis/CallGraph.cpp +++ b/lib/Analysis/CallGraph.cpp @@ -137,8 +137,10 @@ namespace llvm { template <> struct DOTGraphTraits<CallGraph> : public DefaultDOTGraphTraits { + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + static std::string getNodeLabel(const CallGraphNode *Node, - const CallGraph &CG, bool ShortNames) { + const CallGraph &CG) { return Node->getName(); } diff --git a/lib/Analysis/CallInliner.cpp b/lib/Analysis/CallInliner.cpp index cca8584..43523c2 100644 --- a/lib/Analysis/CallInliner.cpp +++ b/lib/Analysis/CallInliner.cpp @@ -18,7 +18,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN CallInliner : public GRTransferFuncs { +class CallInliner : public GRTransferFuncs { ASTContext &Ctx; public: CallInliner(ASTContext &ctx) : Ctx(ctx) {} diff --git a/lib/Analysis/CastToStructChecker.cpp b/lib/Analysis/CastToStructChecker.cpp index ccd4a33..a366342 100644 --- a/lib/Analysis/CastToStructChecker.cpp +++ b/lib/Analysis/CastToStructChecker.cpp @@ -19,7 +19,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN CastToStructChecker +class CastToStructChecker : public CheckerVisitor<CastToStructChecker> { BuiltinBug *BT; public: @@ -59,7 +59,7 @@ void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, // Now the cast-to-type is struct pointer, the original type is not void*. if (!OrigPointeeTy->isRecordType()) { - if (ExplodedNode *N = C.GenerateNode(CE)) { + if (ExplodedNode *N = C.GenerateNode()) { if (!BT) BT = new BuiltinBug("Cast from non-struct type to struct type", "Casting a non-structure type to a structure type " diff --git a/lib/Analysis/CheckDeadStores.cpp b/lib/Analysis/CheckDeadStores.cpp index d5cb7ca..ad63eb4 100644 --- a/lib/Analysis/CheckDeadStores.cpp +++ b/lib/Analysis/CheckDeadStores.cpp @@ -22,13 +22,12 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ParentMap.h" #include "llvm/ADT/SmallPtrSet.h" -#include "llvm/Support/Compiler.h" using namespace clang; namespace { -class VISIBILITY_HIDDEN DeadStoreObs : public LiveVariables::ObserverTy { +class DeadStoreObs : public LiveVariables::ObserverTy { ASTContext &Ctx; BugReporter& BR; ParentMap& Parents; @@ -77,7 +76,7 @@ public: break; } - BR.EmitBasicReport(BugType, "Dead store", msg.c_str(), L, R); + BR.EmitBasicReport(BugType, "Dead store", msg, L, R); } void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, @@ -134,16 +133,15 @@ public: if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS())) if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - Expr* RHS = B->getRHS()->IgnoreParenCasts(); - // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. if (VD->getType()->isPointerType()) { - if (IntegerLiteral* L = dyn_cast<IntegerLiteral>(RHS)) - // FIXME: Probably should have an Expr::isNullPointerConstant. - if (L->getValue() == 0) - return; + if (B->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return; } + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); // Special case: self-assignments. These are often used to shut up // "unused variable" compiler warnings. if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS)) @@ -226,7 +224,7 @@ public: //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{ +class FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{ CFG *cfg; public: FindEscaped(CFG *c) : cfg(c) {} diff --git a/lib/Analysis/CheckObjCDealloc.cpp b/lib/Analysis/CheckObjCDealloc.cpp index 92e3e11..87c1f27 100644 --- a/lib/Analysis/CheckObjCDealloc.cpp +++ b/lib/Analysis/CheckObjCDealloc.cpp @@ -169,7 +169,7 @@ void clang::CheckObjCDealloc(const ObjCImplementationDecl* D, os << "Objective-C class '" << D->getNameAsString() << "' lacks a 'dealloc' instance method"; - BR.EmitBasicReport(name, os.str().c_str(), D->getLocStart()); + BR.EmitBasicReport(name, os.str(), D->getLocStart()); return; } @@ -187,7 +187,7 @@ void clang::CheckObjCDealloc(const ObjCImplementationDecl* D, << "' does not send a 'dealloc' message to its super class" " (missing [super dealloc])"; - BR.EmitBasicReport(name, os.str().c_str(), D->getLocStart()); + BR.EmitBasicReport(name, os.str(), D->getLocStart()); return; } @@ -251,8 +251,7 @@ void clang::CheckObjCDealloc(const ObjCImplementationDecl* D, "but was released in 'dealloc'"; } - BR.EmitBasicReport(name, category, - os.str().c_str(), (*I)->getLocation()); + BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); } } } diff --git a/lib/Analysis/CheckObjCInstMethSignature.cpp b/lib/Analysis/CheckObjCInstMethSignature.cpp index 8c0d396..10ba896 100644 --- a/lib/Analysis/CheckObjCInstMethSignature.cpp +++ b/lib/Analysis/CheckObjCInstMethSignature.cpp @@ -65,7 +65,7 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, "behavior for clients of these classes."; BR.EmitBasicReport("Incompatible instance method return type", - os.str().c_str(), MethDerived->getLocStart()); + os.str(), MethDerived->getLocStart()); } } diff --git a/lib/Analysis/CheckObjCUnusedIVars.cpp b/lib/Analysis/CheckObjCUnusedIVars.cpp index 2d9b531..d4067c9 100644 --- a/lib/Analysis/CheckObjCUnusedIVars.cpp +++ b/lib/Analysis/CheckObjCUnusedIVars.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" using namespace clang; @@ -85,6 +86,17 @@ static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { } } +static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, + SourceManager &SM) { + for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); + I!=E; ++I) + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { + SourceLocation L = FD->getLocStart(); + if (SM.getFileID(L) == FID) + Scan(M, FD->getBody()); + } +} + void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR) { @@ -110,10 +122,30 @@ void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, if (M.empty()) return; - + // Now scan the implementation declaration. Scan(M, D); + + // Any potentially unused ivars? + bool hasUnused = false; + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + hasUnused = true; + break; + } + + if (!hasUnused) + return; + + // We found some potentially unused ivars. Scan the entire translation unit + // for functions inside the @implementation that reference these ivars. + // FIXME: In the future hopefully we can just use the lexical DeclContext + // to go from the ObjCImplementationDecl to the lexically "nested" + // C functions. + SourceManager &SM = BR.getSourceManager(); + Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); + // Find ivars that are unused. for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) if (I->second == Unused) { @@ -125,6 +157,6 @@ void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, "(although it may be used by category methods)."; BR.EmitBasicReport("Unused instance variable", "Optimization", - os.str().c_str(), I->first->getLocation()); + os.str(), I->first->getLocation()); } } diff --git a/lib/Analysis/CheckSecuritySyntaxOnly.cpp b/lib/Analysis/CheckSecuritySyntaxOnly.cpp index f1b9c21..e6ab17a 100644 --- a/lib/Analysis/CheckSecuritySyntaxOnly.cpp +++ b/lib/Analysis/CheckSecuritySyntaxOnly.cpp @@ -14,13 +14,12 @@ #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/LocalCheckers.h" #include "clang/AST/StmtVisitor.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" using namespace clang; namespace { -class VISIBILITY_HIDDEN WalkAST : public StmtVisitor<WalkAST> { +class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; IdentifierInfo *II_gets; IdentifierInfo *II_getpw; @@ -210,7 +209,7 @@ void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { ranges.push_back(drInc->getSourceRange()); const char *bugType = "Floating point variable used as loop counter"; - BR.EmitBasicReport(bugType, "Security", os.str().c_str(), + BR.EmitBasicReport(bugType, "Security", os.str(), FS->getLocStart(), ranges.data(), ranges.size()); } @@ -347,7 +346,7 @@ void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport(os1.str().c_str(), "Security", os2.str().c_str(), + BR.EmitBasicReport(os1.str(), "Security", os2.str(), CE->getLocStart(), &R, 1); } @@ -437,7 +436,7 @@ void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport(os1.str().c_str(), "Security", os2.str().c_str(), + BR.EmitBasicReport(os1.str(), "Security", os2.str(), CE->getLocStart(), &R, 1); } diff --git a/lib/Analysis/CheckSizeofPointer.cpp b/lib/Analysis/CheckSizeofPointer.cpp index 174beef..4f5da9f 100644 --- a/lib/Analysis/CheckSizeofPointer.cpp +++ b/lib/Analysis/CheckSizeofPointer.cpp @@ -15,12 +15,11 @@ #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/LocalCheckers.h" -#include "llvm/Support/Compiler.h" using namespace clang; namespace { -class VISIBILITY_HIDDEN WalkAST : public StmtVisitor<WalkAST> { +class WalkAST : public StmtVisitor<WalkAST> { BugReporter &BR; public: diff --git a/lib/Analysis/Checker.cpp b/lib/Analysis/Checker.cpp new file mode 100644 index 0000000..0d907e5 --- /dev/null +++ b/lib/Analysis/Checker.cpp @@ -0,0 +1,35 @@ +//== Checker.h - Abstract interface for checkers -----------------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker and CheckerVisitor, classes used for creating +// domain-specific checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checker.h" +using namespace clang; + +Checker::~Checker() {} + +CheckerContext::~CheckerContext() { + // Do we need to autotransition? 'Dst' can get populated in a variety of + // ways, including 'addTransition()' adding the predecessor node to Dst + // without actually generated a new node. We also shouldn't autotransition + // if we are building sinks or we generated a node and decided to not + // add it as a transition. + if (Dst.size() == size && !B.BuildSinks && !B.HasGeneratedNode) { + if (state && state != B.GetState(Pred)) { + static int autoTransitionTag = 0; + B.Tag = &autoTransitionTag; + addTransition(state); + } + else + Dst.Add(Pred); + } +} diff --git a/lib/Analysis/DereferenceChecker.cpp b/lib/Analysis/DereferenceChecker.cpp index c3aa8f3..9824387 100644 --- a/lib/Analysis/DereferenceChecker.cpp +++ b/lib/Analysis/DereferenceChecker.cpp @@ -21,7 +21,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN DereferenceChecker : public Checker { +class DereferenceChecker : public Checker { BuiltinBug *BT_null; BuiltinBug *BT_undef; llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes; @@ -56,8 +56,7 @@ void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) { // Check for dereference of an undefined value. if (l.isUndef()) { - ExplodedNode *N = C.GenerateNode(S, true); - if (N) { + if (ExplodedNode *N = C.GenerateSink()) { if (!BT_undef) BT_undef = new BuiltinBug("Dereference of undefined pointer value"); @@ -82,34 +81,55 @@ void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, // The explicit NULL case. if (nullState) { - // Generate an error node. - ExplodedNode *N = C.GenerateNode(S, nullState, true); - if (N) { - if (!notNullState) { - // We know that 'location' cannot be non-null. This is what - // we call an "explicit" null dereference. - if (!BT_null) - BT_null = new BuiltinBug("Null pointer dereference", - "Dereference of null pointer"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - - C.EmitReport(report); + if (!notNullState) { + // Generate an error node. + ExplodedNode *N = C.GenerateSink(nullState); + if (!N) return; + + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null = new BuiltinBug("Dereference of null pointer"); + + llvm::SmallString<100> buf; + + switch (S->getStmtClass()) { + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast<UnaryOperator>(S); + const Expr *SU = U->getSubExpr()->IgnoreParens(); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SU)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + llvm::raw_svector_ostream os(buf); + os << "Dereference of null pointer loaded from variable '" + << VD->getName() << '\''; + } + } + } + default: + break; } + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, + buf.empty() ? BT_null->getDescription():buf.str(), + N); + + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + + C.EmitReport(report); + return; + } + else { // Otherwise, we have the case where the location could either be // null or not-null. Record the error node as an "implicit" null - // dereference. - ImplicitNullDerefNodes.push_back(N); + // dereference. + if (ExplodedNode *N = C.GenerateSink(nullState)) + ImplicitNullDerefNodes.push_back(N); } } // From this point forward, we know that the location is not null. - assert(notNullState); - C.addTransition(state != nullState ? C.GenerateNode(S, notNullState) : - C.getPredecessor()); + C.addTransition(notNullState); } diff --git a/lib/Analysis/DivZeroChecker.cpp b/lib/Analysis/DivZeroChecker.cpp index a8630f1..266c236 100644 --- a/lib/Analysis/DivZeroChecker.cpp +++ b/lib/Analysis/DivZeroChecker.cpp @@ -18,7 +18,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN DivZeroChecker : public CheckerVisitor<DivZeroChecker> { +class DivZeroChecker : public CheckerVisitor<DivZeroChecker> { BuiltinBug *BT; public: DivZeroChecker() : BT(0) {} @@ -63,7 +63,7 @@ void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, llvm::tie(stateNotZero, stateZero) = CM.AssumeDual(C.getState(), *DV); if (stateZero && !stateNotZero) { - if (ExplodedNode *N = C.GenerateNode(B, stateZero, true)) { + if (ExplodedNode *N = C.GenerateSink(stateZero)) { if (!BT) BT = new BuiltinBug("Division by zero"); @@ -80,6 +80,5 @@ void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, // If we get here, then the denom should not be zero. We abandon the implicit // zero denom case for now. - if (stateNotZero != C.getState()) - C.addTransition(C.GenerateNode(B, stateNotZero)); + C.addTransition(stateNotZero); } diff --git a/lib/Analysis/Environment.cpp b/lib/Analysis/Environment.cpp index 1610ad4..dd2f08b 100644 --- a/lib/Analysis/Environment.cpp +++ b/lib/Analysis/Environment.cpp @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/PathSensitive/GRState.h" #include "clang/Analysis/Analyses/LiveVariables.h" -#include "llvm/Support/Compiler.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; @@ -83,7 +82,7 @@ Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S, } namespace { -class VISIBILITY_HIDDEN MarkLiveCallback : public SymbolVisitor { +class MarkLiveCallback : public SymbolVisitor { SymbolReaper &SymReaper; public: MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} diff --git a/lib/Analysis/FixedAddressChecker.cpp b/lib/Analysis/FixedAddressChecker.cpp index 80096dc..031ca44 100644 --- a/lib/Analysis/FixedAddressChecker.cpp +++ b/lib/Analysis/FixedAddressChecker.cpp @@ -19,7 +19,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN FixedAddressChecker +class FixedAddressChecker : public CheckerVisitor<FixedAddressChecker> { BuiltinBug *BT; public: @@ -53,7 +53,7 @@ void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, if (!RV.isConstant() || RV.isZeroConstant()) return; - if (ExplodedNode *N = C.GenerateNode(B)) { + if (ExplodedNode *N = C.GenerateNode()) { if (!BT) BT = new BuiltinBug("Use fixed address", "Using a fixed address is not portable because that " diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp index b99ba4f..644dd19 100644 --- a/lib/Analysis/GRCoreEngine.cpp +++ b/lib/Analysis/GRCoreEngine.cpp @@ -15,7 +15,6 @@ #include "clang/Analysis/PathSensitive/GRCoreEngine.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/AST/Expr.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/DenseMap.h" #include <vector> @@ -30,7 +29,7 @@ using namespace clang; //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN DFS : public GRWorkList { +class DFS : public GRWorkList { llvm::SmallVector<GRWorkListUnit,20> Stack; public: virtual bool hasWork() const { @@ -49,7 +48,7 @@ public: } }; -class VISIBILITY_HIDDEN BFS : public GRWorkList { +class BFS : public GRWorkList { std::queue<GRWorkListUnit> Queue; public: virtual bool hasWork() const { @@ -79,7 +78,7 @@ GRWorkList *GRWorkList::MakeDFS() { return new DFS(); } GRWorkList *GRWorkList::MakeBFS() { return new BFS(); } namespace { - class VISIBILITY_HIDDEN BFSBlockDFSContents : public GRWorkList { + class BFSBlockDFSContents : public GRWorkList { std::queue<GRWorkListUnit> Queue; llvm::SmallVector<GRWorkListUnit,20> Stack; public: diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 2633177..20820d4 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -13,6 +13,7 @@ // //===----------------------------------------------------------------------===// +#include "GRExprEngineInternalChecks.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h" #include "clang/Analysis/PathSensitive/Checker.h" @@ -22,7 +23,6 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/StringSwitch.h" @@ -37,12 +37,21 @@ using llvm::cast; using llvm::APSInt; //===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(0, &II); +} + +//===----------------------------------------------------------------------===// // Batch auditor. DEPRECATED. //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN MappedBatchAuditor : public GRSimpleAPICheck { +class MappedBatchAuditor : public GRSimpleAPICheck { typedef llvm::ImmutableList<GRSimpleAPICheck*> Checks; typedef llvm::DenseMap<void*,Checks> MapTy; @@ -107,16 +116,17 @@ public: // Checker worklist routines. //===----------------------------------------------------------------------===// -void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, +bool GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, bool isPrevisit) { if (Checkers.empty()) { - Dst = Src; - return; + Dst.insert(Src); + return false; } ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; + bool stopProcessingAfterCurrentChecker = false; for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) { @@ -126,17 +136,33 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, CurrSet->clear(); 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, tag, isPrevisit); + NI != NE; ++NI) { + // FIXME: Halting evaluation of the checkers is something we may + // not support later. The design is still evolving. + if (checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, + tag, isPrevisit)) { + if (CurrSet != &Dst) + Dst.insert(*CurrSet); + + stopProcessingAfterCurrentChecker = true; + continue; + } + assert(stopProcessingAfterCurrentChecker == false && + "Inconsistent setting of 'stopProcessingAfterCurrentChecker'"); + } + + if (stopProcessingAfterCurrentChecker) + return true; - // Update which NodeSet is the current one. + // Continue on to the next checker. Update the current NodeSet. PrevSet = CurrSet; } // Don't autotransition. The CheckerContext objects should do this // automatically. + return false; } // FIXME: This is largely copy-paste from CheckerVisit(). Need to @@ -179,12 +205,30 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, // Engine construction and deletion. //===----------------------------------------------------------------------===// -static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(0, &II); +static void RegisterInternalChecks(GRExprEngine &Eng) { + // Register internal "built-in" BugTypes with the BugReporter. These BugTypes + // are different than what probably many checks will do since they don't + // 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. + // The following checks do not need to have their associated BugTypes + // explicitly registered with the BugReporter. If they issue any BugReports, + // their associated BugType will get registered with the BugReporter + // automatically. Note that the check itself is owned by the GRExprEngine + // object. + RegisterAttrNonNullChecker(Eng); + RegisterCallAndMessageChecker(Eng); + RegisterDereferenceChecker(Eng); + RegisterVLASizeChecker(Eng); + RegisterDivZeroChecker(Eng); + RegisterReturnStackAddressChecker(Eng); + RegisterReturnUndefChecker(Eng); + RegisterUndefinedArraySubscriptChecker(Eng); + RegisterUndefinedAssignmentChecker(Eng); + RegisterUndefBranchChecker(Eng); + RegisterUndefResultChecker(Eng); } - GRExprEngine::GRExprEngine(AnalysisManager &mgr) : AMgr(mgr), CoreEngine(mgr.getASTContext(), *this), @@ -198,7 +242,11 @@ GRExprEngine::GRExprEngine(AnalysisManager &mgr) CurrentStmt(NULL), NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), RaiseSel(GetNullarySelector("raise", G.getContext())), - BR(mgr, *this) {} + BR(mgr, *this) +{ + // Register internal checks. + RegisterInternalChecks(*this); +} GRExprEngine::~GRExprEngine() { BR.FlushReports(); @@ -211,7 +259,6 @@ GRExprEngine::~GRExprEngine() { // Utility methods. //===----------------------------------------------------------------------===// - void GRExprEngine::setTransferFunctions(GRTransferFuncs* tf) { StateMgr.TF = tf; tf->RegisterChecks(*this); @@ -410,6 +457,10 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); break; + case Stmt::BlockExprClass: + VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); + break; + case Stmt::BinaryOperatorClass: { BinaryOperator* B = cast<BinaryOperator>(S); @@ -771,55 +822,49 @@ void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, Condition->getLocStart(), "Error evaluating branch"); - const GRState* PrevState = builder.getState(); - SVal X = PrevState->getSVal(Condition); - DefinedSVal *V = NULL; - - while (true) { - V = dyn_cast<DefinedSVal>(&X); - - if (!V) { - if (X.isUnknown()) { - if (const Expr *Ex = dyn_cast<Expr>(Condition)) { - if (Ex->getType()->isIntegerType()) { - // Try to recover some path-sensitivity. Right now casts of symbolic - // integers that promote their values are currently not tracked well. - // If 'Condition' is such an expression, try and recover the - // underlying value and use that instead. - SVal recovered = RecoverCastedSymbol(getStateManager(), - builder.getState(), Condition, - getContext()); - - if (!recovered.isUnknown()) { - X = recovered; - continue; - } - } - } + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + checker->VisitBranchCondition(builder, *this, Condition, tag); + } - builder.generateNode(MarkBranch(PrevState, Term, true), true); - builder.generateNode(MarkBranch(PrevState, Term, false), false); - return; - } + // If the branch condition is undefined, return; + if (!builder.isFeasible(true) && !builder.isFeasible(false)) + return; - assert(X.isUndef()); - ExplodedNode *N = builder.generateNode(PrevState, true); + const GRState* PrevState = builder.getState(); + SVal X = PrevState->getSVal(Condition); - if (N) { - N->markAsSink(); - UndefBranches.insert(N); + if (X.isUnknown()) { + // Give it a chance to recover from unknown. + if (const Expr *Ex = dyn_cast<Expr>(Condition)) { + if (Ex->getType()->isIntegerType()) { + // Try to recover some path-sensitivity. Right now casts of symbolic + // integers that promote their values are currently not tracked well. + // If 'Condition' is such an expression, try and recover the + // underlying value and use that instead. + SVal recovered = RecoverCastedSymbol(getStateManager(), + builder.getState(), Condition, + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } } - - builder.markInfeasible(false); + } + // If the condition is still unknown, give up. + if (X.isUnknown()) { + builder.generateNode(MarkBranch(PrevState, Term, true), true); + builder.generateNode(MarkBranch(PrevState, Term, false), false); return; } - - break; } + DefinedSVal V = cast<DefinedSVal>(X); + // Process the true branch. if (builder.isFeasible(true)) { - if (const GRState *state = PrevState->Assume(*V, true)) + if (const GRState *state = PrevState->Assume(V, true)) builder.generateNode(MarkBranch(state, Term, true), true); else builder.markInfeasible(true); @@ -827,7 +872,7 @@ void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, // Process the false branch. if (builder.isFeasible(false)) { - if (const GRState *state = PrevState->Assume(*V, false)) + if (const GRState *state = PrevState->Assume(V, false)) builder.generateNode(MarkBranch(state, Term, false), false); else builder.markInfeasible(false); @@ -866,8 +911,9 @@ void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) { if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) { // Dispatch to the first target and mark it as a sink. - ExplodedNode* N = builder.generateNode(builder.begin(), state, true); - UndefBranches.insert(N); + //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); + // FIXME: add checker visit. + // UndefBranches.insert(N); return; } @@ -918,8 +964,10 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { SVal CondV_untested = state->getSVal(CondE); if (CondV_untested.isUndef()) { - ExplodedNode* N = builder.generateDefaultCaseNode(state, true); - UndefBranches.insert(N); + //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); + // FIXME: add checker + //UndefBranches.insert(N); + return; } DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); @@ -1052,6 +1100,22 @@ void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, // Transfer functions: Loads and stores. //===----------------------------------------------------------------------===// +void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + ExplodedNodeSet Tmp; + + CanQualType T = getContext().getCanonicalType(BE->getType()); + SVal V = ValMgr.getBlockPointer(BE->getBlockDecl(), T, + Pred->getLocationContext()); + + MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), + ProgramPoint::PostLValueKind); + + // Post-visit the BlockExpr. + CheckerVisit(BE, Dst, Tmp, false); +} + void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst, bool asLValue) { @@ -1278,7 +1342,7 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, ExplodedNode* Pred, const GRState* state, SVal location, const void *tag, bool isLoad) { - + // Early checks for performance reason. if (location.isUnknown() || Checkers.empty()) { Dst.Add(Pred); return; @@ -1298,9 +1362,13 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, Checker *checker = I->second; for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checker->GR_VisitLocation(*CurrSet, *Builder, *this, S, *NI, state, + NI != NE; ++NI) { + // Use the 'state' argument only when the predecessor node is the + // same as Pred. This allows us to catch updates to the state. + checker->GR_VisitLocation(*CurrSet, *Builder, *this, S, *NI, + *NI == Pred ? state : GetState(*NI), location, tag, isLoad); + } // Update which NodeSet is the current one. PrevSet = CurrSet; @@ -1850,197 +1918,89 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, ExplodedNode* Pred, ExplodedNodeSet& Dst) { - // FIXME: More logic for the processing the method call. - - const GRState* state = GetState(Pred); - bool RaisesException = false; - - - if (Expr* Receiver = ME->getReceiver()) { - - SVal L_untested = state->getSVal(Receiver); - - // Check for undefined control-flow. - if (L_untested.isUndef()) { - ExplodedNode* N = Builder->generateNode(ME, state, Pred); - - if (N) { - N->markAsSink(); - UndefReceivers.insert(N); - } - - return; - } - - // "Assume" that the receiver is not NULL. - DefinedOrUnknownSVal L = cast<DefinedOrUnknownSVal>(L_untested); - const GRState *StNotNull = state->Assume(L, true); - - // "Assume" that the receiver is NULL. - const GRState *StNull = state->Assume(L, false); - - if (StNull) { - QualType RetTy = ME->getType(); - - // Check if the receiver was nil and the return value a struct. - if (RetTy->isRecordType()) { - if (Pred->getParentMap().isConsumedExpr(ME)) { - // The [0 ...] expressions will return garbage. Flag either an - // explicit or implicit error. Because of the structure of this - // function we currently do not bifurfacte the state graph at - // this point. - // FIXME: We should bifurcate and fill the returned struct with - // garbage. - if (ExplodedNode* N = Builder->generateNode(ME, StNull, Pred)) { - N->markAsSink(); - if (StNotNull) - NilReceiverStructRetImplicit.insert(N); - else - NilReceiverStructRetExplicit.insert(N); - } - } - } - else { - ASTContext& Ctx = getContext(); - if (RetTy != Ctx.VoidTy) { - if (Pred->getParentMap().isConsumedExpr(ME)) { - // sizeof(void *) - const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); - // sizeof(return type) - const uint64_t returnTypeSize = Ctx.getTypeSize(ME->getType()); - - if (voidPtrSize < returnTypeSize) { - if (ExplodedNode* N = Builder->generateNode(ME, StNull, Pred)) { - N->markAsSink(); - if (StNotNull) - NilReceiverLargerThanVoidPtrRetImplicit.insert(N); - else - NilReceiverLargerThanVoidPtrRetExplicit.insert(N); - } - } - else if (!StNotNull) { - // Handle the safe cases where the return value is 0 if the - // receiver is nil. - // - // FIXME: For now take the conservative approach that we only - // return null values if we *know* that the receiver is nil. - // This is because we can have surprises like: - // - // ... = [[NSScreens screens] objectAtIndex:0]; - // - // What can happen is that [... screens] could return nil, but - // it most likely isn't nil. We should assume the semantics - // of this case unless we have *a lot* more knowledge. - // - SVal V = ValMgr.makeZeroVal(ME->getType()); - MakeNode(Dst, ME, Pred, StNull->BindExpr(ME, V)); - return; - } - } - } - } - // We have handled the cases where the receiver is nil. The remainder - // of this method should assume that the receiver is not nil. - if (!StNotNull) - return; - - state = StNotNull; - } - - // Check if the "raise" message was sent. - if (ME->getSelector() == RaiseSel) - RaisesException = true; + // Handle previsits checks. + ExplodedNodeSet Src, DstTmp; + Src.Add(Pred); + + if (CheckerVisit(ME, DstTmp, Src, true)) { + Dst.insert(DstTmp); + return; } - else { - - IdentifierInfo* ClsName = ME->getClassName(); - Selector S = ME->getSelector(); - - // Check for special instance methods. + + unsigned size = Dst.size(); - if (!NSExceptionII) { - ASTContext& Ctx = getContext(); + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); + DI!=DE; ++DI) { + Pred = *DI; + bool RaisesException = false; - NSExceptionII = &Ctx.Idents.get("NSException"); + if (ME->getReceiver()) { + // Check if the "raise" message was sent. + if (ME->getSelector() == RaiseSel) + RaisesException = true; } + else { - if (ClsName == NSExceptionII) { - - enum { NUM_RAISE_SELECTORS = 2 }; - - // Lazily create a cache of the selectors. + IdentifierInfo* ClsName = ME->getClassName(); + Selector S = ME->getSelector(); - if (!NSExceptionInstanceRaiseSelectors) { + // Check for special instance methods. + if (!NSExceptionII) { ASTContext& Ctx = getContext(); - NSExceptionInstanceRaiseSelectors = new Selector[NUM_RAISE_SELECTORS]; + NSExceptionII = &Ctx.Idents.get("NSException"); + } - llvm::SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; - unsigned idx = 0; + if (ClsName == NSExceptionII) { - // raise:format: - II.push_back(&Ctx.Idents.get("raise")); - II.push_back(&Ctx.Idents.get("format")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); + enum { NUM_RAISE_SELECTORS = 2 }; - // raise:format::arguments: - II.push_back(&Ctx.Idents.get("arguments")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - } + // Lazily create a cache of the selectors. - for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) - if (S == NSExceptionInstanceRaiseSelectors[i]) { - RaisesException = true; break; - } - } - } + if (!NSExceptionInstanceRaiseSelectors) { - // Check for any arguments that are uninitialized/undefined. + ASTContext& Ctx = getContext(); - for (ObjCMessageExpr::arg_iterator I = ME->arg_begin(), E = ME->arg_end(); - I != E; ++I) { + NSExceptionInstanceRaiseSelectors = new Selector[NUM_RAISE_SELECTORS]; - if (state->getSVal(*I).isUndef()) { + llvm::SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; + unsigned idx = 0; - // Generate an error node for passing an uninitialized/undefined value - // as an argument to a message expression. This node is a sink. - ExplodedNode* N = Builder->generateNode(ME, state, Pred); + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); - if (N) { - N->markAsSink(); - MsgExprUndefArgs[N] = *I; - } + // raise:format::arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + } - return; + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; break; + } + } } - } - // Handle previsits checks. - ExplodedNodeSet Src, DstTmp; - Src.Add(Pred); - CheckerVisit(ME, DstTmp, Src, true); - - // Check if we raise an exception. For now treat these as sinks. Eventually - // we will want to handle exceptions properly. - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - if (RaisesException) - Builder->BuildSinks = true; + // Check if we raise an exception. For now treat these as sinks. Eventually + // we will want to handle exceptions properly. + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + if (RaisesException) + Builder->BuildSinks = true; - // Dispatch to plug-in transfer function. - unsigned size = Dst.size(); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); - DI!=DE; ++DI) - EvalObjCMessageExpr(Dst, ME, *DI); + // Dispatch to plug-in transfer function. + SaveOr OldHasGen(Builder->HasGeneratedNode); + EvalObjCMessageExpr(Dst, ME, Pred); + } // Handle the case where no nodes where generated. Auto-generate that // contains the updated state if we aren't generating sinks. if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode) - MakeNode(Dst, ME, Pred, state); + MakeNode(Dst, ME, Pred, GetState(Pred)); } //===----------------------------------------------------------------------===// @@ -2157,7 +2117,7 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, namespace { // This class is used by VisitInitListExpr as an item in a worklist // for processing the values contained in an InitListExpr. -class VISIBILITY_HIDDEN InitListWLItem { +class InitListWLItem { public: llvm::ImmutableList<SVal> Vals; ExplodedNode* N; @@ -2246,8 +2206,6 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, return; } - - printf("InitListExpr type = %s\n", T.getAsString().c_str()); assert(0 && "unprocessed InitListExpr type"); } @@ -2689,6 +2647,8 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, else Visit(LHS, Pred, Tmp1); + ExplodedNodeSet Tmp3; + for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { SVal LeftV = (*I1)->getState()->getSVal(LHS); ExplodedNodeSet Tmp2; @@ -2723,7 +2683,7 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, // Simulate the effects of a "store": bind the value of the RHS // to the L-Value represented by the LHS. - EvalStore(Dst, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV, RightV); + EvalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV, RightV); continue; } @@ -2735,28 +2695,17 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, if (Result.isUnknown()) { if (OldSt != state) { // Generate a new node if we have already created a new state. - MakeNode(Dst, B, *I2, state); + MakeNode(Tmp3, B, *I2, state); } else - Dst.Add(*I2); + Tmp3.Add(*I2); continue; } state = state->BindExpr(B, Result); - if (Result.isUndef()) { - // The operands were *not* undefined, but the result is undefined. - // This is a special node that should be flagged as an error. - if (ExplodedNode *UndefNode = Builder->generateNode(B, state, *I2)){ - UndefNode->markAsSink(); - UndefResults.insert(UndefNode); - } - continue; - } - - // Otherwise, create a new node. - MakeNode(Dst, B, *I2, state); + MakeNode(Tmp3, B, *I2, state); continue; } @@ -2809,15 +2758,6 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, RightV, CTy), state, B->getType(), CTy); - if (Result.isUndef()) { - // The operands were not undefined, but the result is undefined. - if (ExplodedNode* UndefNode = Builder->generateNode(B, state, *I3)) { - UndefNode->markAsSink(); - UndefResults.insert(UndefNode); - } - continue; - } - // EXPERIMENTAL: "Conjured" symbols. // FIXME: Handle structs. @@ -2844,11 +2784,13 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, llvm::tie(state, LHSVal) = SVator.EvalCast(Result, state, LTy, CTy); } - EvalStore(Dst, B, LHS, *I3, state->BindExpr(B, Result), + EvalStore(Tmp3, B, LHS, *I3, state->BindExpr(B, Result), location, LHSVal); } } } + + CheckerVisit(B, Dst, Tmp3, false); } //===----------------------------------------------------------------------===// @@ -2870,8 +2812,11 @@ static SourceManager* GraphPrintSourceManager; namespace llvm { template<> -struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : +struct DOTGraphTraits<ExplodedNode*> : public DefaultDOTGraphTraits { + + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + // FIXME: Since we do not cache error nodes in GRExprEngine now, this does not // work. static std::string getNodeAttributes(const ExplodedNode* N, void*) { @@ -2888,15 +2833,14 @@ struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : GraphPrintCheckerState->isBadCall(N) || GraphPrintCheckerState->isUndefArg(N)) return "color=\"red\",style=\"filled\""; -#endif if (GraphPrintCheckerState->isNoReturnCall(N)) return "color=\"blue\",style=\"filled\""; - +#endif return ""; } - static std::string getNodeLabel(const ExplodedNode* N, void*,bool ShortNames){ + static std::string getNodeLabel(const ExplodedNode* N, void*){ std::string sbuf; llvm::raw_string_ostream Out(sbuf); diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.cpp b/lib/Analysis/GRExprEngineExperimentalChecks.cpp index 2fb7e9f..33479b0 100644 --- a/lib/Analysis/GRExprEngineExperimentalChecks.cpp +++ b/lib/Analysis/GRExprEngineExperimentalChecks.cpp @@ -31,6 +31,8 @@ void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { // Note that this must be registered after ReturnStackAddresEngsChecker. RegisterReturnPointerRangeChecker(Eng); + + RegisterFixedAddressChecker(Eng); RegisterPointerSubChecker(Eng); RegisterPointerArithChecker(Eng); RegisterCastToStructChecker(Eng); diff --git a/lib/Analysis/GRExprEngineInternalChecks.cpp b/lib/Analysis/GRExprEngineInternalChecks.cpp deleted file mode 100644 index d0f60fd..0000000 --- a/lib/Analysis/GRExprEngineInternalChecks.cpp +++ /dev/null @@ -1,400 +0,0 @@ -//=-- GRExprEngineInternalChecks.cpp - Builtin GRExprEngine Checks---*- C++ -*-= -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the BugType classes used by GRExprEngine to report -// bugs derived from builtin checks in the path-sensitive engine. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace clang::bugreporter; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -template <typename ITERATOR> inline -ExplodedNode* GetNode(ITERATOR I) { - return *I; -} - -template <> inline -ExplodedNode* GetNode(GRExprEngine::undef_arg_iterator I) { - return I->first; -} - -//===----------------------------------------------------------------------===// -// Bug Descriptions. -//===----------------------------------------------------------------------===// -namespace clang { -class BuiltinBugReport : public RangedBugReport { -public: - BuiltinBugReport(BugType& bt, const char* desc, - ExplodedNode *n) - : RangedBugReport(bt, desc, n) {} - - BuiltinBugReport(BugType& bt, const char *shortDesc, const char *desc, - ExplodedNode *n) - : RangedBugReport(bt, shortDesc, desc, n) {} - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N); -}; - -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))); -} - -class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug { -public: - NilReceiverStructRet(GRExprEngine* eng) : - BuiltinBug(eng, "'nil' receiver with struct return type") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::nil_receiver_struct_ret_iterator - I=Eng.nil_receiver_struct_ret_begin(), - E=Eng.nil_receiver_struct_ret_end(); I!=E; ++I) { - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - PostStmt P = cast<PostStmt>((*I)->getLocation()); - const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(P.getStmt()); - os << "The receiver in the message expression is 'nil' and results in the" - " returned value (of type '" - << ME->getType().getAsString() - << "') to be garbage or otherwise undefined"; - - BuiltinBugReport *R = new BuiltinBugReport(*this, os.str().c_str(), *I); - R->addRange(ME->getReceiver()->getSourceRange()); - BR.EmitReport(R); - } - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); - } -}; - -class VISIBILITY_HIDDEN NilReceiverLargerThanVoidPtrRet : public BuiltinBug { -public: - NilReceiverLargerThanVoidPtrRet(GRExprEngine* eng) : - BuiltinBug(eng, - "'nil' receiver with return type larger than sizeof(void *)") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::nil_receiver_larger_than_voidptr_ret_iterator - I=Eng.nil_receiver_larger_than_voidptr_ret_begin(), - E=Eng.nil_receiver_larger_than_voidptr_ret_end(); I!=E; ++I) { - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - PostStmt P = cast<PostStmt>((*I)->getLocation()); - const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(P.getStmt()); - os << "The receiver in the message expression is 'nil' and results in the" - " returned value (of type '" - << ME->getType().getAsString() - << "' and of size " - << Eng.getContext().getTypeSize(ME->getType()) / 8 - << " bytes) to be garbage or otherwise undefined"; - - BuiltinBugReport *R = new BuiltinBugReport(*this, os.str().c_str(), *I); - R->addRange(ME->getReceiver()->getSourceRange()); - BR.EmitReport(R); - } - } - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); - } -}; - -class VISIBILITY_HIDDEN UndefResult : public BuiltinBug { -public: - UndefResult(GRExprEngine* eng) - : BuiltinBug(eng,"Undefined or garbage result", - "Result of operation is garbage or undefined") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::undef_result_iterator I=Eng.undef_results_begin(), - E = Eng.undef_results_end(); I!=E; ++I) { - - ExplodedNode *N = *I; - const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - BuiltinBugReport *report = NULL; - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) { - llvm::SmallString<256> sbuf; - llvm::raw_svector_ostream OS(sbuf); - const GRState *ST = N->getState(); - const Expr *Ex = NULL; - bool isLeft = true; - - if (ST->getSVal(B->getLHS()).isUndef()) { - Ex = B->getLHS()->IgnoreParenCasts(); - isLeft = true; - } - else if (ST->getSVal(B->getRHS()).isUndef()) { - Ex = B->getRHS()->IgnoreParenCasts(); - isLeft = false; - } - - if (Ex) { - OS << "The " << (isLeft ? "left" : "right") - << " operand of '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' is a garbage value"; - } - else { - // Neither operand was undefined, but the result is undefined. - OS << "The result of the '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' expression is undefined"; - } - - // FIXME: Use StringRefs to pass string information. - report = new BuiltinBugReport(*this, OS.str().str().c_str(), N); - if (Ex) report->addRange(Ex->getSourceRange()); - } - else { - report = new BuiltinBugReport(*this, - "Expression evaluates to an uninitialized" - " or undefined value", N); - } - - BR.EmitReport(report); - } - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - - const Stmt *S = N->getLocationAs<StmtPoint>()->getStmt(); - const Stmt *X = S; - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) { - const GRState *ST = N->getState(); - if (ST->getSVal(B->getLHS()).isUndef()) - X = B->getLHS(); - else if (ST->getSVal(B->getRHS()).isUndef()) - X = B->getRHS(); - } - - registerTrackNullOrUndefValue(BRC, X, N); - } -}; - -class VISIBILITY_HIDDEN ArgReport : public BuiltinBugReport { - const Stmt *Arg; -public: - ArgReport(BugType& bt, const char* desc, ExplodedNode *n, - const Stmt *arg) - : BuiltinBugReport(bt, desc, n), Arg(arg) {} - - ArgReport(BugType& bt, const char *shortDesc, const char *desc, - ExplodedNode *n, const Stmt *arg) - : BuiltinBugReport(bt, shortDesc, desc, n), Arg(arg) {} - - const Stmt *getArg() const { return Arg; } -}; - -class VISIBILITY_HIDDEN BadArg : public BuiltinBug { -public: - BadArg(GRExprEngine* eng=0) : BuiltinBug(eng,"Uninitialized argument", - "Pass-by-value argument in function call is undefined") {} - - BadArg(GRExprEngine* eng, const char* d) - : BuiltinBug(eng,"Uninitialized argument", d) {} - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(R)->getArg(), - N); - } -}; - -class VISIBILITY_HIDDEN BadMsgExprArg : public BadArg { -public: - BadMsgExprArg(GRExprEngine* eng) - : BadArg(eng,"Pass-by-value argument in message expression is undefined"){} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::UndefArgsTy::iterator I=Eng.msg_expr_undef_arg_begin(), - E = Eng.msg_expr_undef_arg_end(); I!=E; ++I) { - // Generate a report for this bug. - ArgReport *report = new ArgReport(*this, desc.c_str(), I->first, - I->second); - report->addRange(I->second->getSourceRange()); - BR.EmitReport(report); - } - } -}; - -class VISIBILITY_HIDDEN BadReceiver : public BuiltinBug { -public: - BadReceiver(GRExprEngine* eng) - : BuiltinBug(eng,"Uninitialized receiver", - "Receiver in message expression is an uninitialized value") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::ErrorNodes::iterator I=Eng.undef_receivers_begin(), - End = Eng.undef_receivers_end(); I!=End; ++I) { - - // Generate a report for this bug. - BuiltinBugReport *report = new BuiltinBugReport(*this, desc.c_str(), *I); - ExplodedNode* N = *I; - const Stmt *S = cast<PostStmt>(N->getLocation()).getStmt(); - const Expr* E = cast<ObjCMessageExpr>(S)->getReceiver(); - assert (E && "Receiver cannot be NULL"); - report->addRange(E->getSourceRange()); - BR.EmitReport(report); - } - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); - } -}; - -class VISIBILITY_HIDDEN UndefBranch : public BuiltinBug { - struct VISIBILITY_HIDDEN FindUndefExpr { - GRStateManager& VM; - const GRState* St; - - FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} - - Expr* FindExpr(Expr* Ex) { - if (!MatchesCriteria(Ex)) - return 0; - - for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) - if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) { - Expr* E2 = FindExpr(ExI); - if (E2) return E2; - } - - return Ex; - } - - bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); } - }; - -public: - UndefBranch(GRExprEngine *eng) - : BuiltinBug(eng,"Use of garbage value", - "Branch condition evaluates to an undefined or garbage value") - {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::undef_branch_iterator I=Eng.undef_branches_begin(), - E=Eng.undef_branches_end(); I!=E; ++I) { - - // What's going on here: we want to highlight the subexpression of the - // condition that is the most likely source of the "uninitialized - // branch condition." We do a recursive walk of the condition's - // subexpressions and roughly look for the most nested subexpression - // that binds to Undefined. We then highlight that expression's range. - BlockEdge B = cast<BlockEdge>((*I)->getLocation()); - Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); - assert (Ex && "Block must have a terminator."); - - // Get the predecessor node and check if is a PostStmt with the Stmt - // being the terminator condition. We want to inspect the state - // of that node instead because it will contain main information about - // the subexpressions. - assert (!(*I)->pred_empty()); - - // Note: any predecessor will do. They should have identical state, - // since all the BlockEdge did was act as an error sink since the value - // had to already be undefined. - ExplodedNode *N = *(*I)->pred_begin(); - ProgramPoint P = N->getLocation(); - const GRState* St = (*I)->getState(); - - if (PostStmt* PS = dyn_cast<PostStmt>(&P)) - if (PS->getStmt() == Ex) - St = N->getState(); - - FindUndefExpr FindIt(Eng.getStateManager(), St); - Ex = FindIt.FindExpr(Ex); - - ArgReport *R = new ArgReport(*this, desc.c_str(), *I, Ex); - R->addRange(Ex->getSourceRange()); - BR.EmitReport(R); - } - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(R)->getArg(), - N); - } -}; - -} // end clang namespace - -//===----------------------------------------------------------------------===// -// Check registration. -//===----------------------------------------------------------------------===// - -void GRExprEngine::RegisterInternalChecks() { - // Register internal "built-in" BugTypes with the BugReporter. These BugTypes - // are different than what probably many checks will do since they don't - // 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 UndefBranch(this)); - BR.Register(new UndefResult(this)); - BR.Register(new BadMsgExprArg(this)); - BR.Register(new BadReceiver(this)); - BR.Register(new NilReceiverStructRet(this)); - BR.Register(new NilReceiverLargerThanVoidPtrRet(this)); - - // The following checks do not need to have their associated BugTypes - // explicitly registered with the BugReporter. If they issue any BugReports, - // their associated BugType will get registered with the BugReporter - // automatically. Note that the check itself is owned by the GRExprEngine - // object. - registerCheck(new UndefinedAssignmentChecker()); - - RegisterAttrNonNullChecker(*this); - RegisterUndefinedArgChecker(*this); - RegisterBadCallChecker(*this); - RegisterDereferenceChecker(*this); - RegisterVLASizeChecker(*this); - RegisterDivZeroChecker(*this); - RegisterReturnStackAddressChecker(*this); - RegisterReturnUndefChecker(*this); - RegisterFixedAddressChecker(*this); - RegisterUndefinedArraySubscriptChecker(*this); -} diff --git a/lib/Analysis/GRExprEngineInternalChecks.h b/lib/Analysis/GRExprEngineInternalChecks.h index a9077bf..5b7a757 100644 --- a/lib/Analysis/GRExprEngineInternalChecks.h +++ b/lib/Analysis/GRExprEngineInternalChecks.h @@ -20,7 +20,6 @@ namespace clang { class GRExprEngine; void RegisterAttrNonNullChecker(GRExprEngine &Eng); -void RegisterBadCallChecker(GRExprEngine &Eng); void RegisterDereferenceChecker(GRExprEngine &Eng); void RegisterDivZeroChecker(GRExprEngine &Eng); void RegisterReturnPointerRangeChecker(GRExprEngine &Eng); @@ -31,9 +30,12 @@ void RegisterPointerSubChecker(GRExprEngine &Eng); void RegisterPointerArithChecker(GRExprEngine &Eng); void RegisterFixedAddressChecker(GRExprEngine &Eng); void RegisterCastToStructChecker(GRExprEngine &Eng); -void RegisterUndefinedArgChecker(GRExprEngine &Eng); +void RegisterCallAndMessageChecker(GRExprEngine &Eng); void RegisterArrayBoundChecker(GRExprEngine &Eng); void RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng); +void RegisterUndefinedAssignmentChecker(GRExprEngine &Eng); +void RegisterUndefBranchChecker(GRExprEngine &Eng); +void RegisterUndefResultChecker(GRExprEngine &Eng); } // end clang namespace #endif diff --git a/lib/Analysis/GRState.cpp b/lib/Analysis/GRState.cpp index 23ee0b2..a56859d 100644 --- a/lib/Analysis/GRState.cpp +++ b/lib/Analysis/GRState.cpp @@ -232,7 +232,7 @@ const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){ //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN ScanReachableSymbols : public SubRegionMap::Visitor { +class ScanReachableSymbols : public SubRegionMap::Visitor { typedef llvm::DenseSet<const MemRegion*> VisitedRegionsTy; VisitedRegionsTy visited; @@ -308,6 +308,27 @@ bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { return S.scan(val); } +bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for ( ; I != E; ++I) { + if (S.scan(*I)) + return true; + } + return false; +} + +bool GRState::scanReachableSymbols(const MemRegion * const *I, + const MemRegion * const *E, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for ( ; I != E; ++I) { + if (S.scan(*I)) + return true; + } + return false; +} + //===----------------------------------------------------------------------===// // Queries. //===----------------------------------------------------------------------===// diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp index 2510445..84e268f 100644 --- a/lib/Analysis/LiveVariables.cpp +++ b/lib/Analysis/LiveVariables.cpp @@ -19,9 +19,9 @@ #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" #include "clang/Analysis/Support/SaveAndRestore.h" +#include "clang/Analysis/PathSensitive/AnalysisContext.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -38,7 +38,7 @@ static const bool Dead = false; //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN RegisterDecls +class RegisterDecls : public CFGRecStmtDeclVisitor<RegisterDecls> { LiveVariables::AnalysisDataTy& AD; @@ -77,10 +77,12 @@ public: }; } // end anonymous namespace -LiveVariables::LiveVariables(ASTContext& Ctx, CFG& cfg) { +LiveVariables::LiveVariables(AnalysisContext &AC) { // Register all referenced VarDecls. + CFG &cfg = *AC.getCFG(); getAnalysisData().setCFG(cfg); - getAnalysisData().setContext(Ctx); + getAnalysisData().setContext(AC.getASTContext()); + getAnalysisData().AC = &AC; RegisterDecls R(getAnalysisData()); cfg.VisitBlockStmts(R); @@ -92,7 +94,7 @@ LiveVariables::LiveVariables(ASTContext& Ctx, CFG& cfg) { namespace { -class VISIBILITY_HIDDEN TransferFuncs : public CFGRecStmtVisitor<TransferFuncs>{ +class TransferFuncs : public CFGRecStmtVisitor<TransferFuncs>{ LiveVariables::AnalysisDataTy& AD; LiveVariables::ValTy LiveState; public: @@ -103,6 +105,7 @@ public: void VisitDeclRefExpr(DeclRefExpr* DR); void VisitBinaryOperator(BinaryOperator* B); + void VisitBlockExpr(BlockExpr *B); void VisitAssign(BinaryOperator* B); void VisitDeclStmt(DeclStmt* DS); void BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S); @@ -153,7 +156,17 @@ void TransferFuncs::VisitTerminator(CFGBlock* B) { void TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) { if (VarDecl* V = dyn_cast<VarDecl>(DR->getDecl())) - LiveState(V,AD) = Alive; + LiveState(V, AD) = Alive; +} + +void TransferFuncs::VisitBlockExpr(BlockExpr *BE) { + AnalysisContext::referenced_decls_iterator I, E; + llvm::tie(I, E) = AD.AC->getReferencedBlockVars(BE->getBlockDecl()); + for ( ; I != E ; ++I) { + DeclBitVector_Types::Idx i = AD.getIdx(*I); + if (i.isValid()) + LiveState.getBit(i) = Alive; + } } void TransferFuncs::VisitBinaryOperator(BinaryOperator* B) { diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp index 93e7083..204c7b3 100644 --- a/lib/Analysis/MallocChecker.cpp +++ b/lib/Analysis/MallocChecker.cpp @@ -46,9 +46,9 @@ struct RefState { } }; -class VISIBILITY_HIDDEN RegionState {}; +class RegionState {}; -class VISIBILITY_HIDDEN MallocChecker : public CheckerVisitor<MallocChecker> { +class MallocChecker : public CheckerVisitor<MallocChecker> { BuiltinBug *BT_DoubleFree; BuiltinBug *BT_Leak; IdentifierInfo *II_malloc; @@ -65,7 +65,7 @@ private: void MallocMem(CheckerContext &C, const CallExpr *CE); void FreeMem(CheckerContext &C, const CallExpr *CE); }; -} +} // end anonymous namespace namespace clang { template <> @@ -112,9 +112,7 @@ void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { SymbolRef Sym = CallVal.getAsLocSymbol(); assert(Sym); // Set the symbol's state to Allocated. - const GRState *AllocState - = state->set<RegionState>(Sym, RefState::getAllocated(CE)); - C.addTransition(C.GenerateNode(CE, AllocState)); + C.addTransition(state->set<RegionState>(Sym, RefState::getAllocated(CE))); } void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { @@ -128,7 +126,7 @@ void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { // Check double free. if (RS->isReleased()) { - ExplodedNode *N = C.GenerateNode(CE, true); + ExplodedNode *N = C.GenerateSink(); if (N) { if (!BT_DoubleFree) BT_DoubleFree = new BuiltinBug("Double free", @@ -144,7 +142,7 @@ void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { // Normal free. const GRState *FreedState = state->set<RegionState>(Sym, RefState::getReleased(CE)); - C.addTransition(C.GenerateNode(CE, FreedState)); + C.addTransition(FreedState); } void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, @@ -158,7 +156,7 @@ void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, return; if (RS->isAllocated()) { - ExplodedNode *N = C.GenerateNode(S, true); + ExplodedNode *N = C.GenerateSink(); if (N) { if (!BT_Leak) BT_Leak = new BuiltinBug("Memory leak", @@ -173,6 +171,7 @@ void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng) { + SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode); const GRState *state = B.getState(); typedef llvm::ImmutableMap<SymbolRef, RefState> SymMap; SymMap M = state->get<RegionState>(); @@ -212,7 +211,5 @@ void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { if (RS->isAllocated()) state = state->set<RegionState>(Sym, RefState::getEscaped(S)); - ExplodedNode *N = C.GenerateNode(S, state); - if (N) - C.addTransition(N); + C.addTransition(state); } diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp index 8c0b85c..af8bd16 100644 --- a/lib/Analysis/MemRegion.cpp +++ b/lib/Analysis/MemRegion.cpp @@ -17,15 +17,25 @@ #include "clang/Analysis/PathSensitive/MemRegion.h" #include "clang/Analysis/PathSensitive/ValueManager.h" #include "clang/Analysis/PathSensitive/AnalysisContext.h" +#include "clang/AST/StmtVisitor.h" using namespace clang; //===----------------------------------------------------------------------===// -// Basic methods. +// Object destruction. //===----------------------------------------------------------------------===// MemRegion::~MemRegion() {} +MemRegionManager::~MemRegionManager() { + // All regions and their data are BumpPtrAllocated. No need to call + // their destructors. +} + +//===----------------------------------------------------------------------===// +// Basic methods. +//===----------------------------------------------------------------------===// + bool SubRegion::isSubRegionOf(const MemRegion* R) const { const MemRegion* r = getSuperRegion(); while (r != 0) { @@ -126,15 +136,39 @@ void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); } -void CodeTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const FunctionDecl *FD, - const MemRegion*) { - ID.AddInteger(MemRegion::CodeTextRegionKind); +void FunctionTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const FunctionDecl *FD, + const MemRegion*) { + ID.AddInteger(MemRegion::FunctionTextRegionKind); ID.AddPointer(FD); } -void CodeTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - CodeTextRegion::ProfileRegion(ID, FD, superRegion); +void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + FunctionTextRegion::ProfileRegion(ID, FD, superRegion); +} + +void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockDecl *BD, CanQualType, + const MemRegion*) { + ID.AddInteger(MemRegion::BlockTextRegionKind); + ID.AddPointer(BD); +} + +void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockTextRegion::ProfileRegion(ID, BD, locTy, superRegion); +} + +void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockTextRegion *BC, + const LocationContext *LC, + const MemRegion *) { + ID.AddInteger(MemRegion::BlockDataRegionKind); + ID.AddPointer(BC); + ID.AddPointer(LC); +} + +void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockDataRegion::ProfileRegion(ID, BC, LC, NULL); } //===----------------------------------------------------------------------===// @@ -160,10 +194,19 @@ void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const { os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; } -void CodeTextRegion::dumpToStream(llvm::raw_ostream& os) const { +void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const { os << "code{" << getDecl()->getDeclName().getAsString() << '}'; } +void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "block_code{" << (void*) this << '}'; +} + +void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "block_data{" << BC << '}'; +} + + void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { // FIXME: More elaborate pretty-printing. os << "{ " << (void*) CL << " }"; @@ -259,6 +302,18 @@ VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, return getRegion<VarRegion>(D, LC); } +BlockDataRegion *MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, + const LocationContext *LC) +{ + // FIXME: Once we implement scope handling, we will need to properly lookup + // 'D' to the proper LocationContext. For now, just strip down to the + // StackFrame. + while (!isa<StackFrameContext>(LC)) + LC = LC->getParent(); + + return getSubRegion<BlockDataRegion>(BC, LC, getStackRegion()); +} + CompoundLiteralRegion* MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL) { return getRegion<CompoundLiteralRegion>(CL); @@ -287,10 +342,17 @@ MemRegionManager::getElementRegion(QualType elementType, SVal Idx, return R; } -CodeTextRegion *MemRegionManager::getCodeTextRegion(const FunctionDecl *FD) { - return getRegion<CodeTextRegion>(FD); +FunctionTextRegion * +MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { + return getRegion<FunctionTextRegion>(FD); +} + +BlockTextRegion *MemRegionManager::getBlockTextRegion(const BlockDecl *BD, + CanQualType locTy) { + return getRegion<BlockTextRegion>(BD, locTy); } + /// getSymbolicRegion - Retrieve or create a "symbolic" memory region. SymbolicRegion* MemRegionManager::getSymbolicRegion(SymbolRef sym) { return getRegion<SymbolicRegion>(sym); @@ -473,3 +535,53 @@ RegionRawOffset ElementRegion::getAsRawOffset() const { return RegionRawOffset(superR, offset); } +//===----------------------------------------------------------------------===// +// BlockDataRegion +//===----------------------------------------------------------------------===// + +void BlockDataRegion::LazyInitializeReferencedVars() { + if (ReferencedVars) + return; + + AnalysisContext *AC = LC->getAnalysisContext(); + AnalysisContext::referenced_decls_iterator I, E; + llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); + + if (I == E) { + ReferencedVars = (void*) 0x1; + return; + } + + MemRegionManager &MemMgr = *getMemRegionManager(); + llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); + BumpVectorContext BC(A); + + typedef BumpVector<const MemRegion*> VarVec; + VarVec *BV = (VarVec*) A.Allocate<VarVec>(); + new (BV) VarVec(BC, (E - I) / sizeof(*I)); + + for ( ; I != E; ++I) + BV->push_back(MemMgr.getVarRegion(*I, LC), BC); + + ReferencedVars = BV; +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_begin() const { + const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); + + BumpVector<const MemRegion*> *Vec = + static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); + + return Vec == (void*) 0x1 ? NULL : Vec->begin(); +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_end() const { + const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); + + BumpVector<const MemRegion*> *Vec = + static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); + + return Vec == (void*) 0x1 ? NULL : Vec->end(); +} diff --git a/lib/Analysis/NSAutoreleasePoolChecker.cpp b/lib/Analysis/NSAutoreleasePoolChecker.cpp index e0a8d0d..2ff0487 100644 --- a/lib/Analysis/NSAutoreleasePoolChecker.cpp +++ b/lib/Analysis/NSAutoreleasePoolChecker.cpp @@ -19,14 +19,13 @@ #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "BasicObjCFoundationChecks.h" -#include "llvm/Support/Compiler.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" using namespace clang; namespace { -class VISIBILITY_HIDDEN NSAutoreleasePoolChecker +class NSAutoreleasePoolChecker : public CheckerVisitor<NSAutoreleasePoolChecker> { Selector releaseS; @@ -65,6 +64,9 @@ NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, // the type of the expression. const ObjCObjectPointerType* PT = receiver->getType()->getAs<ObjCObjectPointerType>(); + + if (!PT) + return; const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); if (!OD) return; diff --git a/lib/Analysis/NSErrorChecker.cpp b/lib/Analysis/NSErrorChecker.cpp index 93b617b..e3cf57f 100644 --- a/lib/Analysis/NSErrorChecker.cpp +++ b/lib/Analysis/NSErrorChecker.cpp @@ -20,7 +20,6 @@ #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" #include "BasicObjCFoundationChecks.h" -#include "llvm/Support/Compiler.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" #include "llvm/ADT/SmallVector.h" @@ -28,7 +27,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN NSErrorChecker : public BugType { +class NSErrorChecker : public BugType { const Decl &CodeDecl; const bool isNSErrorWarning; IdentifierInfo * const II; @@ -117,7 +116,7 @@ void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { BR.EmitBasicReport(isNSErrorWarning ? "Bad return type when passing NSError**" : "Bad return type when passing CFError*", - getCategory().c_str(), os.str().c_str(), + getCategory(), os.str(), CodeDecl.getLocation()); } @@ -229,7 +228,7 @@ void NSErrorChecker::CheckParamDeref(const VarDecl *Param, os << Param->getNameAsString() << "' may be null."; - BugReport *report = new BugReport(*this, os.str().c_str(), *I); + BugReport *report = new BugReport(*this, os.str(), *I); // FIXME: Notable symbols are now part of the report. We should // add support for notable symbols in BugReport. // BR.addNotableSymbol(SV->getSymbol()); diff --git a/lib/Analysis/PointerArithChecker.cpp b/lib/Analysis/PointerArithChecker.cpp index 9382348..370233c 100644 --- a/lib/Analysis/PointerArithChecker.cpp +++ b/lib/Analysis/PointerArithChecker.cpp @@ -18,7 +18,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN PointerArithChecker +class PointerArithChecker : public CheckerVisitor<PointerArithChecker> { BuiltinBug *BT; public: @@ -53,7 +53,7 @@ void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || isa<CompoundLiteralRegion>(LR)) { - if (ExplodedNode *N = C.GenerateNode(B)) { + if (ExplodedNode *N = C.GenerateNode()) { if (!BT) BT = new BuiltinBug("Dangerous pointer arithmetic", "Pointer arithmetic done on non-array variables " diff --git a/lib/Analysis/PointerSubChecker.cpp b/lib/Analysis/PointerSubChecker.cpp index 4c7906f..c597a25 100644 --- a/lib/Analysis/PointerSubChecker.cpp +++ b/lib/Analysis/PointerSubChecker.cpp @@ -19,7 +19,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN PointerSubChecker +class PointerSubChecker : public CheckerVisitor<PointerSubChecker> { BuiltinBug *BT; public: @@ -61,7 +61,7 @@ void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) return; - if (ExplodedNode *N = C.GenerateNode(B)) { + if (ExplodedNode *N = C.GenerateNode()) { if (!BT) BT = new BuiltinBug("Pointer subtraction", "Subtraction of two pointers that do not point to " diff --git a/lib/Analysis/PthreadLockChecker.cpp b/lib/Analysis/PthreadLockChecker.cpp index 6620661..e95095c 100644 --- a/lib/Analysis/PthreadLockChecker.cpp +++ b/lib/Analysis/PthreadLockChecker.cpp @@ -21,7 +21,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN PthreadLockChecker +class PthreadLockChecker : public CheckerVisitor<PthreadLockChecker> { BugType *BT; public: @@ -42,7 +42,7 @@ public: } // end anonymous namespace // GDM Entry for tracking lock state. -namespace { class VISIBILITY_HIDDEN LockSet {}; } +namespace { class LockSet {}; } namespace clang { template <> struct GRStateTrait<LockSet> : public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { @@ -59,8 +59,8 @@ void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); - const CodeTextRegion *R = - dyn_cast_or_null<CodeTextRegion>(state->getSVal(Callee).getAsRegion()); + const FunctionTextRegion *R = + dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); if (!R) return; diff --git a/lib/Analysis/RangeConstraintManager.cpp b/lib/Analysis/RangeConstraintManager.cpp index f5cae69..7330b62 100644 --- a/lib/Analysis/RangeConstraintManager.cpp +++ b/lib/Analysis/RangeConstraintManager.cpp @@ -17,7 +17,6 @@ #include "clang/Analysis/PathSensitive/GRStateTrait.h" #include "clang/Analysis/PathSensitive/GRTransferFuncs.h" #include "clang/Analysis/ManagerRegistry.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" @@ -25,14 +24,14 @@ using namespace clang; -namespace { class VISIBILITY_HIDDEN ConstraintRange {}; } +namespace { class ConstraintRange {}; } static int ConstraintRangeIndex = 0; /// A Range represents the closed range [from, to]. The caller must /// guarantee that from <= to. Note that Range is immutable, so as not /// to subvert RangeSet's immutability. namespace { -class VISIBILITY_HIDDEN Range : public std::pair<const llvm::APSInt*, +class Range : public std::pair<const llvm::APSInt*, const llvm::APSInt*> { public: Range(const llvm::APSInt &from, const llvm::APSInt &to) @@ -59,7 +58,7 @@ public: }; -class VISIBILITY_HIDDEN RangeTrait : public llvm::ImutContainerInfo<Range> { +class RangeTrait : public llvm::ImutContainerInfo<Range> { public: // When comparing if one Range is less than another, we should compare // the actual APSInt values instead of their pointers. This keeps the order @@ -74,7 +73,7 @@ public: /// RangeSet contains a set of ranges. If the set is empty, then /// there the value of a symbol is overly constrained and there are no /// possible values for that symbol. -class VISIBILITY_HIDDEN RangeSet { +class RangeSet { typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet; PrimRangeSet ranges; // no need to make const, since it is an // ImmutableSet - this allows default operator= @@ -232,7 +231,7 @@ struct GRStateTrait<ConstraintRange> } namespace { -class VISIBILITY_HIDDEN RangeConstraintManager : public SimpleConstraintManager{ +class RangeConstraintManager : public SimpleConstraintManager{ RangeSet GetRange(const GRState *state, SymbolRef sym); public: RangeConstraintManager() {} diff --git a/lib/Analysis/RegionStore.cpp b/lib/Analysis/RegionStore.cpp index ae3fa14..e645172 100644 --- a/lib/Analysis/RegionStore.cpp +++ b/lib/Analysis/RegionStore.cpp @@ -25,7 +25,6 @@ #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Compiler.h" using namespace clang; @@ -86,10 +85,10 @@ typedef llvm::ImmutableMap<const MemRegion*, BindingVal> RegionBindings; //===----------------------------------------------------------------------===// namespace { -struct VISIBILITY_HIDDEN minimal_features_tag {}; -struct VISIBILITY_HIDDEN maximal_features_tag {}; +struct minimal_features_tag {}; +struct maximal_features_tag {}; -class VISIBILITY_HIDDEN RegionStoreFeatures { +class RegionStoreFeatures { bool SupportsFields; bool SupportsRemaining; @@ -114,7 +113,7 @@ public: // MemRegions represent chunks of memory with a size (their "extent"). This // GDM entry tracks the extents for regions. Extents are in bytes. // -namespace { class VISIBILITY_HIDDEN RegionExtents {}; } +namespace { class RegionExtents {}; } static int RegionExtentsIndex = 0; namespace clang { template<> struct GRStateTrait<RegionExtents> @@ -141,7 +140,7 @@ static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) { namespace { -class VISIBILITY_HIDDEN RegionStoreSubRegionMap : public SubRegionMap { +class RegionStoreSubRegionMap : public SubRegionMap { typedef llvm::ImmutableSet<const MemRegion*> SetTy; typedef llvm::DenseMap<const MemRegion*, SetTy> Map; SetTy::Factory F; @@ -188,7 +187,7 @@ public: } }; -class VISIBILITY_HIDDEN RegionStoreManager : public StoreManager { +class RegionStoreManager : public StoreManager { const RegionStoreFeatures Features; RegionBindings::Factory RBFactory; @@ -215,6 +214,13 @@ public: /// getDefaultBinding - Returns an SVal* representing an optional default /// binding associated with a region and its subregions. Optional<SVal> getDefaultBinding(RegionBindings B, const MemRegion *R); + + /// setImplicitDefaultValue - Set the default binding for the provided + /// MemRegion to the value implicitly defined for compound literals when + /// the value is not specified. + const GRState *setImplicitDefaultValue(const GRState *state, + const MemRegion *R, + QualType T); /// getLValueString - Returns an SVal representing the lvalue of a /// StringLiteral. Within RegionStore a StringLiteral has an @@ -705,7 +711,9 @@ DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, assert(0 && "Cannot index into a MemSpace"); return UnknownVal(); - case MemRegion::CodeTextRegionKind: + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: // Technically this can happen if people do funny things with casts. return UnknownVal(); @@ -850,7 +858,9 @@ SVal RegionStoreManager::EvalBinOp(const GRState *state, case MemRegion::ObjCIvarRegionKind: return UnknownVal(); - case MemRegion::CodeTextRegionKind: + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: // Technically this can happen if people do funny things with casts. return UnknownVal(); @@ -1437,6 +1447,30 @@ RegionStoreManager::BindCompoundLiteral(const GRState *state, return Bind(state, loc::MemRegionVal(R), V); } +const GRState *RegionStoreManager::setImplicitDefaultValue(const GRState *state, + const MemRegion *R, + QualType T) { + Store store = state->getStore(); + RegionBindings B = GetRegionBindings(store); + SVal V; + + if (Loc::IsLocType(T)) + V = ValMgr.makeNull(); + else if (T->isIntegerType()) + V = ValMgr.makeZeroVal(T); + else if (T->isStructureType() || T->isArrayType()) { + // Set the default value to a zero constant when it is a structure + // or array. The type doesn't really matter. + V = ValMgr.makeZeroVal(ValMgr.getContext().IntTy); + } + else { + return state; + } + + B = RBFactory.Add(B, R, BindingVal(V, BindingVal::Default)); + return state->makeWithStore(B.getRoot()); +} + const GRState *RegionStoreManager::BindArray(const GRState *state, const TypedRegion* R, SVal Init) { @@ -1478,6 +1512,10 @@ const GRState *RegionStoreManager::BindArray(const GRState *state, return CopyLazyBindings(*LCV, state, R); // Remaining case: explicit compound values. + + if (Init.isUnknown()) + return setImplicitDefaultValue(state, R, ElementTy); + nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(Init); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); uint64_t i = 0; @@ -1497,17 +1535,10 @@ const GRState *RegionStoreManager::BindArray(const GRState *state, state = Bind(state, ValMgr.makeLoc(ER), *VI); } - // If the init list is shorter than the array length, set the array default - // value. - if (i < size) { - if (ElementTy->isIntegerType()) { - SVal V = ValMgr.makeZeroVal(ElementTy); - Store store = state->getStore(); - RegionBindings B = GetRegionBindings(store); - B = RBFactory.Add(B, R, BindingVal(V, BindingVal::Default)); - state = state->makeWithStore(B.getRoot()); - } - } + // If the init list is shorter than the array length, set the + // array default value. + if (i < size) + state = setImplicitDefaultValue(state, R, ElementTy); return state; } @@ -1619,9 +1650,9 @@ void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, llvm::OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(store)); - // Do a pass over the regions in the store. For VarRegions we check if - // the variable is still live and if so add it to the list of live roots. - // For other regions we populate our region backmap. + // Do a pass over the regions in the store. For VarRegions we check if + // the variable is still live and if so add it to the list of live roots. + // For other regions we populate our region backmap. llvm::SmallVector<const MemRegion*, 10> IntermediateRoots; // Scan the direct bindings for "intermediate" roots. @@ -1719,8 +1750,21 @@ tryAgain: // Mark the symbol for any live SymbolicRegion as "live". This means we // should continue to track that symbol. - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(R)) + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) SymReaper.markLive(SymR->getSymbol()); + + // For BlockDataRegions, enqueue all VarRegions for that are referenced + // via BlockDeclRefExprs. + if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) { + for (BlockDataRegion::referenced_vars_iterator + RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); + RI != RE; ++RI) + WorkList.push_back(std::make_pair(state_N, *RI)); + + // No possible data bindings on a BlockDataRegion. Continue to the + // next region in the worklist. + continue; + } Store store_N = state_N->getStore(); RegionBindings B_N = GetRegionBindings(store_N); diff --git a/lib/Analysis/ReturnPointerRangeChecker.cpp b/lib/Analysis/ReturnPointerRangeChecker.cpp index 44887b2..ab0fcab 100644 --- a/lib/Analysis/ReturnPointerRangeChecker.cpp +++ b/lib/Analysis/ReturnPointerRangeChecker.cpp @@ -20,7 +20,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN ReturnPointerRangeChecker : +class ReturnPointerRangeChecker : public CheckerVisitor<ReturnPointerRangeChecker> { BuiltinBug *BT; public: @@ -70,7 +70,7 @@ void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.GenerateNode(RS, StOutBound, true); + ExplodedNode *N = C.GenerateSink(StOutBound); if (!N) return; @@ -91,7 +91,6 @@ void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, new RangedBugReport(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); - C.EmitReport(report); } } diff --git a/lib/Analysis/ReturnStackAddressChecker.cpp b/lib/Analysis/ReturnStackAddressChecker.cpp index e4be871..3a6d8a4 100644 --- a/lib/Analysis/ReturnStackAddressChecker.cpp +++ b/lib/Analysis/ReturnStackAddressChecker.cpp @@ -17,12 +17,13 @@ #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallString.h" using namespace clang; namespace { -class VISIBILITY_HIDDEN ReturnStackAddressChecker : +class ReturnStackAddressChecker : public CheckerVisitor<ReturnStackAddressChecker> { BuiltinBug *BT; public: @@ -53,7 +54,7 @@ void ReturnStackAddressChecker::PreVisitReturnStmt(CheckerContext &C, if (!R || !R->hasStackStorage()) return; - ExplodedNode *N = C.GenerateNode(RS, C.getState(), true); + ExplodedNode *N = C.GenerateSink(); if (!N) return; @@ -83,6 +84,14 @@ void ReturnStackAddressChecker::PreVisitReturnStmt(CheckerContext &C, << C.getSourceManager().getInstantiationLineNumber(L) << " returned to caller"; } + else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + const BlockDecl *BD = BR->getCodeRegion()->getDecl(); + SourceLocation L = BD->getLocStart(); + range = BD->getSourceRange(); + os << "Address of stack-allocated block declared on line " + << C.getSourceManager().getInstantiationLineNumber(L) + << " returned to caller"; + } else { os << "Address of stack memory associated with local variable '" << R->getString() << "' returned."; diff --git a/lib/Analysis/ReturnUndefChecker.cpp b/lib/Analysis/ReturnUndefChecker.cpp index 796c760..7cd7126 100644 --- a/lib/Analysis/ReturnUndefChecker.cpp +++ b/lib/Analysis/ReturnUndefChecker.cpp @@ -22,7 +22,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN ReturnUndefChecker : +class ReturnUndefChecker : public CheckerVisitor<ReturnUndefChecker> { BuiltinBug *BT; public: @@ -50,7 +50,7 @@ void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, if (!C.getState()->getSVal(RetE).isUndef()) return; - ExplodedNode *N = C.GenerateNode(RS, C.getState(), true); + ExplodedNode *N = C.GenerateSink(); if (!N) return; diff --git a/lib/Analysis/SVals.cpp b/lib/Analysis/SVals.cpp index d5d36e3..9163b27 100644 --- a/lib/Analysis/SVals.cpp +++ b/lib/Analysis/SVals.cpp @@ -51,7 +51,7 @@ bool SVal::hasConjuredSymbol() const { const FunctionDecl *SVal::getAsFunctionDecl() const { if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(this)) { const MemRegion* R = X->getRegion(); - if (const CodeTextRegion *CTR = R->getAs<CodeTextRegion>()) + if (const FunctionTextRegion *CTR = R->getAs<FunctionTextRegion>()) return CTR->getDecl(); } diff --git a/lib/Analysis/SimpleSValuator.cpp b/lib/Analysis/SimpleSValuator.cpp index 4487aa9..2afcd3e 100644 --- a/lib/Analysis/SimpleSValuator.cpp +++ b/lib/Analysis/SimpleSValuator.cpp @@ -13,12 +13,11 @@ #include "clang/Analysis/PathSensitive/SValuator.h" #include "clang/Analysis/PathSensitive/GRState.h" -#include "llvm/Support/Compiler.h" using namespace clang; namespace { -class VISIBILITY_HIDDEN SimpleSValuator : public SValuator { +class SimpleSValuator : public SValuator { protected: virtual SVal EvalCastNL(NonLoc val, QualType castTy); virtual SVal EvalCastL(Loc val, QualType castTy); diff --git a/lib/Analysis/Store.cpp b/lib/Analysis/Store.cpp index 2fd72ac..2fd573c 100644 --- a/lib/Analysis/Store.cpp +++ b/lib/Analysis/Store.cpp @@ -85,7 +85,10 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) assert(0 && "Invalid region cast"); break; } - case MemRegion::CodeTextRegionKind: { + + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: { // CodeTextRegion should be cast to only a function or block pointer type, // although they can in practice be casted to anything, e.g, void*, char*, // etc. @@ -194,12 +197,11 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) /// as another region. SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, QualType castTy) { - ASTContext &Ctx = ValMgr.getContext(); - if (castTy.isNull()) return V; - assert(Ctx.hasSameUnqualifiedType(castTy, R->getValueType(Ctx))); + assert(ValMgr.getContext().hasSameUnqualifiedType(castTy, + R->getValueType(ValMgr.getContext()))); return V; } diff --git a/lib/Analysis/UndefBranchChecker.cpp b/lib/Analysis/UndefBranchChecker.cpp new file mode 100644 index 0000000..c739d1a --- /dev/null +++ b/lib/Analysis/UndefBranchChecker.cpp @@ -0,0 +1,117 @@ +//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines UndefBranchChecker, which checks for undefined branch +// condition. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/Checker.h" + +using namespace clang; + +namespace { + +class UndefBranchChecker : public Checker { + BuiltinBug *BT; + + struct FindUndefExpr { + GRStateManager& VM; + const GRState* St; + + FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} + + Expr* FindExpr(Expr* Ex) { + if (!MatchesCriteria(Ex)) + return 0; + + for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) + if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) { + Expr* E2 = FindExpr(ExI); + if (E2) return E2; + } + + return Ex; + } + + bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); } + }; + +public: + UndefBranchChecker() : BT(0) {} + static void *getTag(); + void VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng, + Stmt *Condition, void *tag); +}; + +} + +void clang::RegisterUndefBranchChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefBranchChecker()); +} + +void *UndefBranchChecker::getTag() { + static int x; + return &x; +} + +void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder, + GRExprEngine &Eng, + Stmt *Condition, void *tag) { + const GRState *state = Builder.getState(); + SVal X = state->getSVal(Condition); + if (X.isUndef()) { + ExplodedNode *N = Builder.generateNode(state, true); + if (N) { + N->markAsSink(); + if (!BT) + BT = new BuiltinBug("Branch condition evaluates to a garbage value"); + + // What's going on here: we want to highlight the subexpression of the + // condition that is the most likely source of the "uninitialized + // branch condition." We do a recursive walk of the condition's + // subexpressions and roughly look for the most nested subexpression + // that binds to Undefined. We then highlight that expression's range. + BlockEdge B = cast<BlockEdge>(N->getLocation()); + Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); + assert (Ex && "Block must have a terminator."); + + // Get the predecessor node and check if is a PostStmt with the Stmt + // being the terminator condition. We want to inspect the state + // of that node instead because it will contain main information about + // the subexpressions. + assert (!N->pred_empty()); + + // Note: any predecessor will do. They should have identical state, + // since all the BlockEdge did was act as an error sink since the value + // had to already be undefined. + ExplodedNode *PrevN = *N->pred_begin(); + ProgramPoint P = PrevN->getLocation(); + const GRState* St = N->getState(); + + if (PostStmt* PS = dyn_cast<PostStmt>(&P)) + if (PS->getStmt() == Ex) + St = PrevN->getState(); + + FindUndefExpr FindIt(Eng.getStateManager(), St); + Ex = FindIt.FindExpr(Ex); + + // Emit the bug report. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + R->addRange(Ex->getSourceRange()); + + Eng.getBugReporter().EmitReport(R); + } + + Builder.markInfeasible(true); + Builder.markInfeasible(false); + } +} diff --git a/lib/Analysis/UndefResultChecker.cpp b/lib/Analysis/UndefResultChecker.cpp new file mode 100644 index 0000000..acc86dd --- /dev/null +++ b/lib/Analysis/UndefResultChecker.cpp @@ -0,0 +1,86 @@ +//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefResultChecker, a builtin check in GRExprEngine that +// performs checks for undefined results of non-assignment binary operators. +// +//===----------------------------------------------------------------------===// + +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +namespace { +class UndefResultChecker + : public CheckerVisitor<UndefResultChecker> { + + BugType *BT; + +public: + UndefResultChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void clang::RegisterUndefResultChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UndefResultChecker()); +} + +void UndefResultChecker::PostVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + const GRState *state = C.getState(); + if (state->getSVal(B).isUndef()) { + // Generate an error node. + ExplodedNode *N = C.GenerateSink(); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Result of operation is garbage or undefined"); + + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream OS(sbuf); + const Expr *Ex = NULL; + bool isLeft = true; + + if (state->getSVal(B->getLHS()).isUndef()) { + Ex = B->getLHS()->IgnoreParenCasts(); + isLeft = true; + } + else if (state->getSVal(B->getRHS()).isUndef()) { + Ex = B->getRHS()->IgnoreParenCasts(); + isLeft = false; + } + + if (Ex) { + OS << "The " << (isLeft ? "left" : "right") + << " operand of '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' is a garbage value"; + } + else { + // Neither operand was undefined, but the result is undefined. + OS << "The result of the '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' expression is undefined"; + } + EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); + if (Ex) { + report->addRange(Ex->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + } + else + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); + C.EmitReport(report); + } +} diff --git a/lib/Analysis/UndefinedArgChecker.cpp b/lib/Analysis/UndefinedArgChecker.cpp deleted file mode 100644 index 923a7e1..0000000 --- a/lib/Analysis/UndefinedArgChecker.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//===--- UndefinedArgChecker.h - Undefined arguments checker ----*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines BadCallChecker, a builtin check in GRExprEngine that performs -// checks for undefined arguments. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class VISIBILITY_HIDDEN UndefinedArgChecker - : public CheckerVisitor<UndefinedArgChecker> { - BugType *BT; -public: - UndefinedArgChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); -}; -} // end anonymous namespace - -void clang::RegisterUndefinedArgChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefinedArgChecker()); -} - -void UndefinedArgChecker::PreVisitCallExpr(CheckerContext &C, - const CallExpr *CE){ - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) { - if (C.getState()->getSVal(*I).isUndef()) { - if (ExplodedNode *N = C.GenerateNode(CE, true)) { - if (!BT) - BT = new BuiltinBug("Pass-by-value argument in function call is " - "undefined"); - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addRange((*I)->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); - C.EmitReport(R); - } - } - } -} diff --git a/lib/Analysis/UndefinedArraySubscriptChecker.cpp b/lib/Analysis/UndefinedArraySubscriptChecker.cpp index 887c775..d6aacaf 100644 --- a/lib/Analysis/UndefinedArraySubscriptChecker.cpp +++ b/lib/Analysis/UndefinedArraySubscriptChecker.cpp @@ -19,7 +19,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN UndefinedArraySubscriptChecker +class UndefinedArraySubscriptChecker : public CheckerVisitor<UndefinedArraySubscriptChecker> { BugType *BT; public: @@ -41,7 +41,7 @@ void UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, const ArraySubscriptExpr *A) { if (C.getState()->getSVal(A->getIdx()).isUndef()) { - if (ExplodedNode *N = C.GenerateNode(A, true)) { + if (ExplodedNode *N = C.GenerateSink()) { if (!BT) BT = new BuiltinBug("Array subscript is undefined"); diff --git a/lib/Analysis/UndefinedAssignmentChecker.cpp b/lib/Analysis/UndefinedAssignmentChecker.cpp index b8062f3..4630b82 100644 --- a/lib/Analysis/UndefinedAssignmentChecker.cpp +++ b/lib/Analysis/UndefinedAssignmentChecker.cpp @@ -12,11 +12,29 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h" +#include "GRExprEngineInternalChecks.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" #include "clang/Analysis/PathSensitive/BugReporter.h" using namespace clang; +namespace { +class UndefinedAssignmentChecker + : public CheckerVisitor<UndefinedAssignmentChecker> { + BugType *BT; +public: + UndefinedAssignmentChecker() : BT(0) {} + static void *getTag(); + virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE, + const Stmt *StoreE, SVal location, + SVal val); +}; +} + +void clang::RegisterUndefinedAssignmentChecker(GRExprEngine &Eng){ + Eng.registerCheck(new UndefinedAssignmentChecker()); +} + void *UndefinedAssignmentChecker::getTag() { static int x = 0; return &x; @@ -30,7 +48,7 @@ void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, if (!val.isUndef()) return; - ExplodedNode *N = C.GenerateNode(StoreE, true); + ExplodedNode *N = C.GenerateSink(); if (!N) return; diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index 8e7b158..6fa4b53 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -17,7 +17,6 @@ #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" -#include "llvm/Support/Compiler.h" #include "llvm/ADT/SmallPtrSet.h" @@ -29,7 +28,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN RegisterDecls +class RegisterDecls : public CFGRecStmtDeclVisitor<RegisterDecls> { UninitializedValues::AnalysisDataTy& AD; @@ -52,7 +51,7 @@ void UninitializedValues::InitializeValues(const CFG& cfg) { //===----------------------------------------------------------------------===// namespace { -class VISIBILITY_HIDDEN TransferFuncs +class TransferFuncs : public CFGStmtVisitor<TransferFuncs,bool> { UninitializedValues::ValTy V; @@ -269,7 +268,7 @@ namespace { UninitializedValues_ValueTypes::ObserverTy::~ObserverTy() {} namespace { -class VISIBILITY_HIDDEN UninitializedValuesChecker +class UninitializedValuesChecker : public UninitializedValues::ObserverTy { ASTContext &Ctx; diff --git a/lib/Analysis/VLASizeChecker.cpp b/lib/Analysis/VLASizeChecker.cpp index 799a73e..2690d6f 100644 --- a/lib/Analysis/VLASizeChecker.cpp +++ b/lib/Analysis/VLASizeChecker.cpp @@ -20,7 +20,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN VLASizeChecker : public CheckerVisitor<VLASizeChecker> { +class VLASizeChecker : public CheckerVisitor<VLASizeChecker> { BugType *BT_zero; BugType *BT_undef; @@ -55,7 +55,7 @@ void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { if (sizeV.isUndef()) { // Generate an error node. - ExplodedNode *N = C.GenerateNode(DS, true); + ExplodedNode *N = C.GenerateSink(); if (!N) return; @@ -78,7 +78,7 @@ void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { llvm::tie(stateNotZero, stateZero) = state->Assume(sizeD); if (stateZero && !stateNotZero) { - ExplodedNode* N = C.GenerateNode(DS, stateZero, true); + ExplodedNode* N = C.GenerateSink(stateZero); if (!BT_zero) BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " "size"); @@ -92,6 +92,5 @@ void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { } // From this point on, assume that the size is not zero. - if (state != stateNotZero) - C.addTransition(C.GenerateNode(DS, stateNotZero)); + C.addTransition(stateNotZero); } diff --git a/lib/Analysis/ValueManager.cpp b/lib/Analysis/ValueManager.cpp index fe670e7..22a8211 100644 --- a/lib/Analysis/ValueManager.cpp +++ b/lib/Analysis/ValueManager.cpp @@ -138,6 +138,15 @@ ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, } DefinedSVal ValueManager::getFunctionPointer(const FunctionDecl* FD) { - CodeTextRegion *R = MemMgr.getCodeTextRegion(FD); + CodeTextRegion *R = MemMgr.getFunctionTextRegion(FD); return loc::MemRegionVal(R); } + +DefinedSVal ValueManager::getBlockPointer(const BlockDecl *D, + CanQualType locTy, + const LocationContext *LC) { + BlockTextRegion *BC = MemMgr.getBlockTextRegion(D, locTy); + BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); + return loc::MemRegionVal(BD); +} + |