diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
36 files changed, 4746 insertions, 3140 deletions
diff --git a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp b/lib/StaticAnalyzer/Core/AggExprVisitor.cpp deleted file mode 100644 index 0936d61..0000000 --- a/lib/StaticAnalyzer/Core/AggExprVisitor.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//=-- AggExprVisitor.cpp - evaluating expressions of C++ class type -*- 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 AggExprVisitor class, which contains lots of boiler -// plate code for evaluating expressions of C++ class type. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/AST/StmtVisitor.h" - -using namespace clang; -using namespace ento; - -namespace { -/// AggExprVisitor is designed after AggExprEmitter of the CodeGen module. It -/// is used for evaluating exprs of C++ object type. Evaluating such exprs -/// requires a destination pointer pointing to the object being evaluated -/// into. Passing such a pointer around would pollute the Visit* interface of -/// ExprEngine. AggExprVisitor encapsulates code that goes through various -/// cast and construct exprs (and others), and at the final point, dispatches -/// back to the ExprEngine to let the real evaluation logic happen. -class AggExprVisitor : public StmtVisitor<AggExprVisitor> { - const MemRegion *Dest; - ExplodedNode *Pred; - ExplodedNodeSet &DstSet; - ExprEngine &Eng; - -public: - AggExprVisitor(const MemRegion *dest, ExplodedNode *N, ExplodedNodeSet &dst, - ExprEngine &eng) - : Dest(dest), Pred(N), DstSet(dst), Eng(eng) {} - - void VisitCastExpr(CastExpr *E); - void VisitCXXConstructExpr(CXXConstructExpr *E); - void VisitCXXMemberCallExpr(CXXMemberCallExpr *E); -}; -} - -void AggExprVisitor::VisitCastExpr(CastExpr *E) { - switch (E->getCastKind()) { - default: - llvm_unreachable("Unhandled cast kind"); - case CK_NoOp: - case CK_ConstructorConversion: - case CK_UserDefinedConversion: - Visit(E->getSubExpr()); - break; - } -} - -void AggExprVisitor::VisitCXXConstructExpr(CXXConstructExpr *E) { - Eng.VisitCXXConstructExpr(E, Dest, Pred, DstSet); -} - -void AggExprVisitor::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { - Eng.Visit(E, Pred, DstSet); -} - -void ExprEngine::VisitAggExpr(const Expr *E, const MemRegion *Dest, - ExplodedNode *Pred, ExplodedNodeSet &Dst) { - AggExprVisitor(Dest, Pred, Dst, *this).Visit(const_cast<Expr *>(E)); -} diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 17ec70d..82ac8bd 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -14,6 +14,8 @@ using namespace clang; using namespace ento; +void AnalysisManager::anchor() { } + AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, const LangOptions &lang, PathDiagnosticConsumer *pd, @@ -25,17 +27,27 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, bool vizdot, bool vizubi, AnalysisPurgeMode purge, bool eager, bool trim, - bool inlinecall, bool useUnoptimizedCFG, + bool useUnoptimizedCFG, bool addImplicitDtors, bool addInitializers, - bool eagerlyTrimEGraph) + bool eagerlyTrimEGraph, + AnalysisIPAMode ipa, + unsigned inlineMaxStack, + unsigned inlineMaxFunctionSize, + AnalysisInliningMode IMode, + bool NoRetry) : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), - Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd), + Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), Idxer(idxer), AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit), VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge), - EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall), - EagerlyTrimEGraph(eagerlyTrimEGraph) + EagerlyAssume(eager), TrimGraph(trim), + EagerlyTrimEGraph(eagerlyTrimEGraph), + IPAMode(ipa), + InlineMaxStackDepth(inlineMaxStack), + InlineMaxFunctionSize(inlineMaxFunctionSize), + InliningMode(IMode), + NoRetryExhausted(NoRetry) { AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } @@ -46,7 +58,7 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors, ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers), Ctx(ctx), Diags(diags), - LangInfo(ParentAM.LangInfo), PD(ParentAM.getPathDiagnosticConsumer()), + LangOpts(ParentAM.LangOpts), PD(ParentAM.getPathDiagnosticConsumer()), CreateStoreMgr(ParentAM.CreateStoreMgr), CreateConstraintMgr(ParentAM.CreateConstraintMgr), CheckerMgr(ParentAM.CheckerMgr), @@ -59,15 +71,19 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, PurgeDead(ParentAM.PurgeDead), EagerlyAssume(ParentAM.EagerlyAssume), TrimGraph(ParentAM.TrimGraph), - InlineCall(ParentAM.InlineCall), - EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph) + EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph), + IPAMode(ParentAM.IPAMode), + InlineMaxStackDepth(ParentAM.InlineMaxStackDepth), + InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize), + InliningMode(ParentAM.InliningMode), + NoRetryExhausted(ParentAM.NoRetryExhausted) { AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } -AnalysisContext * -AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { +AnalysisDeclContext * +AnalysisManager::getAnalysisDeclContextInAnotherTU(const Decl *D) { idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D), Idxer->getProgram()); FunctionDecl *FuncDef; @@ -77,7 +93,7 @@ AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { if (FuncDef == 0) return 0; - // This AnalysisContext wraps function definition in another translation unit. + // This AnalysisDeclContext wraps function definition in another translation unit. // But it is still owned by the AnalysisManager associated with the current // translation unit. return AnaCtxMgr.getContext(FuncDef, TU); diff --git a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp index 6c748b6..2d9addd 100644 --- a/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/BasicConstraintManager.cpp @@ -56,59 +56,59 @@ public: : SimpleConstraintManager(subengine), ISetFactory(statemgr.getAllocator()) {} - const ProgramState *assumeSymNE(const ProgramState *state, + ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymEQ(const ProgramState *state, + ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLT(const ProgramState *state, + ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGT(const ProgramState *state, + ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGE(const ProgramState *state, + ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLE(const ProgramState *state, + ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment); - const ProgramState *AddEQ(const ProgramState *state, + ProgramStateRef AddEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V); - const ProgramState *AddNE(const ProgramState *state, + ProgramStateRef AddNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V); - const llvm::APSInt* getSymVal(const ProgramState *state, + const llvm::APSInt* getSymVal(ProgramStateRef state, SymbolRef sym) const; - bool isNotEqual(const ProgramState *state, + bool isNotEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const; - bool isEqual(const ProgramState *state, + bool isEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const; - const ProgramState *removeDeadBindings(const ProgramState *state, + ProgramStateRef removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper); - void print(const ProgramState *state, + void print(ProgramStateRef state, raw_ostream &Out, const char* nl, const char *sep); @@ -122,8 +122,8 @@ ento::CreateBasicConstraintManager(ProgramStateManager& statemgr, return new BasicConstraintManager(statemgr, subengine); } -const ProgramState* -BasicConstraintManager::assumeSymNE(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -143,8 +143,8 @@ BasicConstraintManager::assumeSymNE(const ProgramState *state, return AddNE(state, sym, Adjusted); } -const ProgramState* -BasicConstraintManager::assumeSymEQ(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -165,8 +165,8 @@ BasicConstraintManager::assumeSymEQ(const ProgramState *state, } // The logic for these will be handled in another ConstraintManager. -const ProgramState* -BasicConstraintManager::assumeSymLT(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -180,8 +180,8 @@ BasicConstraintManager::assumeSymLT(const ProgramState *state, return assumeSymNE(state, sym, V, Adjustment); } -const ProgramState* -BasicConstraintManager::assumeSymGT(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -195,8 +195,8 @@ BasicConstraintManager::assumeSymGT(const ProgramState *state, return assumeSymNE(state, sym, V, Adjustment); } -const ProgramState* -BasicConstraintManager::assumeSymGE(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -224,8 +224,8 @@ BasicConstraintManager::assumeSymGE(const ProgramState *state, return state; } -const ProgramState* -BasicConstraintManager::assumeSymLE(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) { @@ -253,14 +253,14 @@ BasicConstraintManager::assumeSymLE(const ProgramState *state, return state; } -const ProgramState *BasicConstraintManager::AddEQ(const ProgramState *state, +ProgramStateRef BasicConstraintManager::AddEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) { // Create a new state with the old binding replaced. return state->set<ConstEq>(sym, &state->getBasicVals().getValue(V)); } -const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state, +ProgramStateRef BasicConstraintManager::AddNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) { @@ -275,13 +275,13 @@ const ProgramState *BasicConstraintManager::AddNE(const ProgramState *state, return state->set<ConstNotEq>(sym, S); } -const llvm::APSInt* BasicConstraintManager::getSymVal(const ProgramState *state, +const llvm::APSInt* BasicConstraintManager::getSymVal(ProgramStateRef state, SymbolRef sym) const { const ConstEqTy::data_type* T = state->get<ConstEq>(sym); return T ? *T : NULL; } -bool BasicConstraintManager::isNotEqual(const ProgramState *state, +bool BasicConstraintManager::isNotEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const { @@ -292,7 +292,7 @@ bool BasicConstraintManager::isNotEqual(const ProgramState *state, return T ? T->contains(&state->getBasicVals().getValue(V)) : false; } -bool BasicConstraintManager::isEqual(const ProgramState *state, +bool BasicConstraintManager::isEqual(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V) const { // Retrieve the EQ-set associated with the given symbol. @@ -303,8 +303,8 @@ bool BasicConstraintManager::isEqual(const ProgramState *state, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const ProgramState* -BasicConstraintManager::removeDeadBindings(const ProgramState *state, +ProgramStateRef +BasicConstraintManager::removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper) { ConstEqTy CE = state->get<ConstEq>(); @@ -329,7 +329,7 @@ BasicConstraintManager::removeDeadBindings(const ProgramState *state, return state->set<ConstNotEq>(CNE); } -void BasicConstraintManager::print(const ProgramState *state, +void BasicConstraintManager::print(ProgramStateRef state, raw_ostream &Out, const char* nl, const char *sep) { // Print equality constraints. diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index fbbdb04..a264212 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/CFG.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" @@ -25,8 +26,10 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include <queue> using namespace clang; @@ -34,6 +37,8 @@ using namespace ento; BugReporterVisitor::~BugReporterVisitor() {} +void BugReporterContext::anchor() {} + //===----------------------------------------------------------------------===// // Helper routines for walking the ExplodedGraph and fetching statements. //===----------------------------------------------------------------------===// @@ -106,6 +111,59 @@ GetCurrentOrNextStmt(const ExplodedNode *N) { } //===----------------------------------------------------------------------===// +// Diagnostic cleanup. +//===----------------------------------------------------------------------===// + +/// Recursively scan through a path and prune out calls and macros pieces +/// that aren't needed. Return true if afterwards the path contains +/// "interesting stuff" which means it should be pruned from the parent path. +static bool RemoveUneededCalls(PathPieces &pieces) { + bool containsSomethingInteresting = false; + const unsigned N = pieces.size(); + + for (unsigned i = 0 ; i < N ; ++i) { + // Remove the front piece from the path. If it is still something we + // want to keep once we are done, we will push it back on the end. + IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front()); + pieces.pop_front(); + + switch (piece->getKind()) { + case PathDiagnosticPiece::Call: { + PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece); + // Recursively clean out the subclass. Keep this call around if + // it contains any informative diagnostics. + if (!RemoveUneededCalls(call->path)) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::Macro: { + PathDiagnosticMacroPiece *macro = cast<PathDiagnosticMacroPiece>(piece); + if (!RemoveUneededCalls(macro->subPieces)) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::Event: { + PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece); + // We never throw away an event, but we do throw it away wholesale + // as part of a path if we throw the entire path away. + if (event->isPrunable()) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::ControlFlow: + break; + } + + pieces.push_back(piece); + } + + return containsSomethingInteresting; +} + +//===----------------------------------------------------------------------===// // PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// @@ -128,14 +186,17 @@ public: class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; PathDiagnosticConsumer *PDC; - llvm::OwningPtr<ParentMap> PM; + OwningPtr<ParentMap> PM; NodeMapClosure NMC; public: + const LocationContext *LC; + PathDiagnosticBuilder(GRBugReporter &br, BugReport *r, NodeBackMap *Backmap, PathDiagnosticConsumer *pdc) : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap) {} + R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext()) + {} PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); @@ -145,12 +206,8 @@ public: BugReport *getBugReport() { return R; } Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } - - const LocationContext* getLocationContext() { - return R->getErrorNode()->getLocationContext(); - } - - ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); } + + ParentMap& getParentMap() { return LC->getParentMap(); } const Stmt *getParent(const Stmt *S) { return getParentMap().getParent(S); @@ -173,7 +230,7 @@ public: PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { if (const Stmt *S = GetNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager(), getLocationContext()); + return PathDiagnosticLocation(S, getSourceManager(), LC); return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), getSourceManager()); @@ -234,7 +291,6 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { assert(S && "Null Stmt *passed to getEnclosingStmtLocation"); ParentMap &P = getParentMap(); SourceManager &SMgr = getSourceManager(); - const LocationContext *LC = getLocationContext(); while (IsNested(S, P)) { const Stmt *Parent = P.getParentIgnoreParens(S); @@ -322,208 +378,87 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { } //===----------------------------------------------------------------------===// -// ScanNotableSymbols: closure-like callback for scanning Store bindings. +// "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// - -static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode *N, - ProgramStateManager& VMgr, - SVal X) { - - for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { - - ProgramPoint P = N->getLocation(); - - if (!isa<PostStmt>(P)) - continue; - - const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); - - if (!DR) - continue; - - SVal Y = N->getState()->getSVal(DR); - - if (X != Y) - continue; - - const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); - - if (!VD) - continue; - - return VD; - } - - return 0; -} - -namespace { -class NotableSymbolHandler -: public StoreManager::BindingsHandler { - - SymbolRef Sym; - const ProgramState *PrevSt; - const Stmt *S; - ProgramStateManager& VMgr; - const ExplodedNode *Pred; - PathDiagnostic& PD; - BugReporter& BR; - -public: - - NotableSymbolHandler(SymbolRef sym, - const ProgramState *prevst, - const Stmt *s, - ProgramStateManager& vmgr, - const ExplodedNode *pred, - PathDiagnostic& pd, - BugReporter& br) - : Sym(sym), - PrevSt(prevst), - S(s), - VMgr(vmgr), - Pred(pred), - PD(pd), - BR(br) {} - - bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, - SVal V) { - - SymbolRef ScanSym = V.getAsSymbol(); - - if (ScanSym != Sym) - return true; - - // Check if the previous state has this binding. - SVal X = PrevSt->getSVal(loc::MemRegionVal(R)); - - if (X == V) // Same binding? - return true; - - // Different binding. Only handle assignments for now. We don't pull - // this check out of the loop because we will eventually handle other - // cases. - - VarDecl *VD = 0; - - if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { - if (!B->isAssignmentOp()) - return true; - - // What variable did we assign to? - DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts()); - - if (!DR) - return true; - - VD = dyn_cast<VarDecl>(DR->getDecl()); - } - else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { - // FIXME: Eventually CFGs won't have DeclStmts. Right now we - // assume that each DeclStmt has a single Decl. This invariant - // holds by construction in the CFG. - VD = dyn_cast<VarDecl>(*DS->decl_begin()); - } - - if (!VD) - return true; - - // What is the most recently referenced variable with this binding? - const VarDecl *MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); - - if (!MostRecent) - return true; - - // Create the diagnostic. - if (Loc::isLocType(VD->getType())) { - llvm::SmallString<64> buf; - llvm::raw_svector_ostream os(buf); - os << '\'' << *VD << "' now aliases '" << *MostRecent << '\''; - PathDiagnosticLocation L = - PathDiagnosticLocation::createBegin(S, BR.getSourceManager(), - Pred->getLocationContext()); - PD.push_front(new PathDiagnosticEventPiece(L, os.str())); - } - - return true; +typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair; +typedef SmallVector<StackDiagPair, 6> StackDiagVector; + +static void updateStackPiecesWithMessage(PathDiagnosticPiece *P, + StackDiagVector &CallStack) { + // If the piece contains a special message, add it to all the call + // pieces on the active stack. + if (PathDiagnosticEventPiece *ep = + dyn_cast<PathDiagnosticEventPiece>(P)) { + + if (ep->hasCallStackHint()) + for (StackDiagVector::iterator I = CallStack.begin(), + E = CallStack.end(); I != E; ++I) { + PathDiagnosticCallPiece *CP = I->first; + const ExplodedNode *N = I->second; + std::string stackMsg = ep->getCallStackMessage(N); + + // The last message on the path to final bug is the most important + // one. Since we traverse the path backwards, do not add the message + // if one has been previously added. + if (!CP->hasCallStackMessage()) + CP->setCallStackMessage(stackMsg); + } } -}; -} - -static void HandleNotableSymbol(const ExplodedNode *N, - const Stmt *S, - SymbolRef Sym, BugReporter& BR, - PathDiagnostic& PD) { - - const ExplodedNode *Pred = N->pred_empty() ? 0 : *N->pred_begin(); - const ProgramState *PrevSt = Pred ? Pred->getState() : 0; - - if (!PrevSt) - return; - - // Look at the region bindings of the current state that map to the - // specified symbol. Are any of them not in the previous state? - ProgramStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager(); - NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); - cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H); } -namespace { -class ScanNotableSymbols -: public StoreManager::BindingsHandler { - - llvm::SmallSet<SymbolRef, 10> AlreadyProcessed; - const ExplodedNode *N; - const Stmt *S; - GRBugReporter& BR; - PathDiagnostic& PD; - -public: - ScanNotableSymbols(const ExplodedNode *n, const Stmt *s, - GRBugReporter& br, PathDiagnostic& pd) - : N(n), S(s), BR(br), PD(pd) {} - - bool HandleBinding(StoreManager& SMgr, Store store, - const MemRegion* R, SVal V) { - - SymbolRef ScanSym = V.getAsSymbol(); - - if (!ScanSym) - return true; - - if (!BR.isNotable(ScanSym)) - return true; - - if (AlreadyProcessed.count(ScanSym)) - return true; - - AlreadyProcessed.insert(ScanSym); - - HandleNotableSymbol(N, S, ScanSym, BR, PD); - return true; - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// "Minimal" path diagnostic generation algorithm. -//===----------------------------------------------------------------------===// - -static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM); +static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { + const ExplodedNode *N, + ArrayRef<BugReporterVisitor *> visitors) { SourceManager& SMgr = PDB.getSourceManager(); - const LocationContext *LC = PDB.getLocationContext(); + const LocationContext *LC = PDB.LC; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); + + StackDiagVector CallStack; + while (NextNode) { N = NextNode; + PDB.LC = N->getLocationContext(); NextNode = GetPredecessorNode(N); ProgramPoint P = N->getLocation(); + + if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + PathDiagnosticCallPiece *C = + PathDiagnosticCallPiece::construct(N, *CE, SMgr); + PD.getActivePath().push_front(C); + PD.pushActivePath(&C->path); + CallStack.push_back(StackDiagPair(C, N)); + continue; + } + + if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + PD.popActivePath(); + // The current active path should never be empty. Either we + // just added a bunch of stuff to the top-level path, or + // we have a previous CallExit. If the front of the active + // path is not a PathDiagnosticCallPiece, it means that the + // path terminated within a function call. We must then take the + // current contents of the active path and place it within + // a new PathDiagnosticCallPiece. + assert(!PD.getActivePath().empty()); + PathDiagnosticCallPiece *C = + dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + if (!C) { + const Decl *Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + } + C->setCallee(*CE, SMgr); + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + continue; + } if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { const CFGBlock *Src = BE->getSrc(); @@ -554,8 +489,8 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); break; } @@ -606,13 +541,13 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } } - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "'Default' branch taken. "; const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } @@ -624,7 +559,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, std::string sbuf; llvm::raw_string_ostream os(sbuf); PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; } @@ -646,7 +581,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; } @@ -669,14 +604,14 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticLocation End(B->getLHS(), SMgr, LC); PathDiagnosticLocation Start = PathDiagnosticLocation::createOperatorLoc(B, SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { os << "true"; PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } } @@ -688,7 +623,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os << "false"; PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { @@ -696,7 +631,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, PathDiagnosticLocation End(B->getLHS(), SMgr, LC); PathDiagnosticLocation Start = PathDiagnosticLocation::createOperatorLoc(B, SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } } @@ -715,7 +650,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { @@ -724,7 +659,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Loop condition is false. Exiting loop")); } @@ -742,7 +677,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); } else { @@ -750,7 +685,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Loop condition is true. Entering loop body")); } @@ -764,10 +699,10 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, End = PDB.getEnclosingStmtLocation(S); if (*(Src->succ_begin()+1) == Dst) - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Taking false branch")); else - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(Start, End, "Taking true branch")); break; @@ -778,24 +713,20 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (NextNode) { // Add diagnostic pieces from custom visitors. BugReport *R = PDB.getBugReport(); - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) - PD.push_front(p); + for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), + E = visitors.end(); + I != E; ++I) { + if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { + PD.getActivePath().push_front(p); + updateStackPiecesWithMessage(p, CallStack); + } } } - - if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { - // Scan the region bindings, and see if a "notable" symbol has a new - // lval binding. - ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD); - PDB.getStateManager().iterBindings(N->getState(), SNS); - } } // After constructing the full PathDiagnostic, do a pass over it to compact // PathDiagnosticPieces that occur within a macro. - CompactPathDiagnostic(PD, PDB.getSourceManager()); + CompactPathDiagnostic(PD.getMutablePieces(), PDB.getSourceManager()); } //===----------------------------------------------------------------------===// @@ -879,7 +810,7 @@ class EdgeBuilder { } if (S != Original) - L = PathDiagnosticLocation(S, L.getManager(), PDB.getLocationContext()); + L = PathDiagnosticLocation(S, L.getManager(), PDB.LC); } if (firstCharOnly) @@ -902,8 +833,8 @@ public: // If the PathDiagnostic already has pieces, add the enclosing statement // of the first piece as a context as well. - if (!PD.empty()) { - PrevLoc = PD.begin()->getLocation(); + if (!PD.path.empty()) { + PrevLoc = (*PD.path.begin())->getLocation(); if (const Stmt *S = PrevLoc.asStmt()) addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); @@ -916,12 +847,18 @@ public: // Finally, add an initial edge from the start location of the first // statement (if it doesn't already exist). PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin( - PDB.getLocationContext(), + PDB.LC, PDB.getSourceManager()); if (L.isValid()) rawAddEdge(L); } + void flushLocations() { + while (!CLocs.empty()) + popLocation(); + PrevLoc = PathDiagnosticLocation(); + } + void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); void rawAddEdge(PathDiagnosticLocation NewLoc); @@ -988,7 +925,7 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, SM.getExpansionColumnNumber(ContaineeRBeg)) && (ContainerEndLine != ContaineeEndLine || SM.getExpansionColumnNumber(ContainerREnd) >= - SM.getExpansionColumnNumber(ContainerREnd))); + SM.getExpansionColumnNumber(ContaineeREnd))); } void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { @@ -1008,7 +945,7 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { PrevLocClean.asLocation().getExpansionLoc()) return; - PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); + PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); PrevLoc = NewLoc; } @@ -1093,7 +1030,7 @@ void EdgeBuilder::addContext(const Stmt *S) { if (!S) return; - PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.getLocationContext()); + PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC); while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1116,9 +1053,11 @@ void EdgeBuilder::addContext(const Stmt *S) { static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { + const ExplodedNode *N, + ArrayRef<BugReporterVisitor *> visitors) { EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); + StackDiagVector CallStack; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { @@ -1127,14 +1066,74 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { + if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + const StackFrameContext *LCtx = + CE->getLocationContext()->getCurrentStackFrame(); + PathDiagnosticLocation Loc(LCtx->getCallSite(), + PDB.getSourceManager(), + LCtx); + EB.addEdge(Loc, true); + EB.flushLocations(); + PathDiagnosticCallPiece *C = + PathDiagnosticCallPiece::construct(N, *CE, SM); + PD.getActivePath().push_front(C); + PD.pushActivePath(&C->path); + CallStack.push_back(StackDiagPair(C, N)); + break; + } + + // Pop the call hierarchy if we are done walking the contents + // of a function call. + if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + // Add an edge to the start of the function. + const Decl *D = CE->getCalleeContext()->getDecl(); + PathDiagnosticLocation pos = + PathDiagnosticLocation::createBegin(D, SM); + EB.addEdge(pos); + + // Flush all locations, and pop the active path. + EB.flushLocations(); + PD.popActivePath(); + assert(!PD.getActivePath().empty()); + PDB.LC = N->getLocationContext(); + + // The current active path should never be empty. Either we + // just added a bunch of stuff to the top-level path, or + // we have a previous CallExit. If the front of the active + // path is not a PathDiagnosticCallPiece, it means that the + // path terminated within a function call. We must then take the + // current contents of the active path and place it within + // a new PathDiagnosticCallPiece. + PathDiagnosticCallPiece *C = + dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + if (!C) { + const Decl * Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + } + C->setCallee(*CE, SM); + EB.addContext(CE->getCallExpr()); + + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + break; + } + + // Note that is important that we update the LocationContext + // after looking at CallExits. CallExit basically adds an + // edge in the *caller*, so we don't want to update the LocationContext + // too soon. + PDB.LC = N->getLocationContext(); + // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { const CFGBlock &Blk = *BE->getSrc(); const Stmt *Term = Blk.getTerminator(); // Are we jumping to the head of a loop? Add a special diagnostic. if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, SM, PDB.getLocationContext()); + PathDiagnosticLocation L(Loop, SM, PDB.LC); const CompoundStmt *CS = NULL; if (!Term) { @@ -1147,9 +1146,10 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticEventPiece *p = new PathDiagnosticEventPiece(L, "Looping back to the head of the loop"); + p->setPrunable(true); EB.addEdge(p->getLocation(), true); - PD.push_front(p); + PD.getActivePath().push_front(p); if (CS) { PathDiagnosticLocation BL = @@ -1177,6 +1177,8 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, break; } + + } while (0); if (!NextNode) @@ -1184,12 +1186,15 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Add pieces from custom visitors. BugReport *R = PDB.getBugReport(); - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); I!=E; ++I) { + for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(), + E = visitors.end(); + I != E; ++I) { if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) { const PathDiagnosticLocation &Loc = p->getLocation(); EB.addEdge(Loc, true); - PD.push_front(p); + PD.getActivePath().push_front(p); + updateStackPiecesWithMessage(p, CallStack); + if (const Stmt *S = Loc.asStmt()) EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); } @@ -1204,10 +1209,14 @@ BugType::~BugType() { } void BugType::FlushReports(BugReporter &BR) {} +void BuiltinBug::anchor() {} + //===----------------------------------------------------------------------===// // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// +void BugReport::NodeResolver::anchor() {} + void BugReport::addVisitor(BugReporterVisitor* visitor) { if (!visitor) return; @@ -1222,7 +1231,8 @@ void BugReport::addVisitor(BugReporterVisitor* visitor) { } CallbacksSet.InsertNode(visitor, InsertPos); - Callbacks = F.add(visitor, Callbacks); + Callbacks.push_back(visitor); + ++ConfigurationChangeToken; } BugReport::~BugReport() { @@ -1231,10 +1241,24 @@ BugReport::~BugReport() { } } +const Decl *BugReport::getDeclWithIssue() const { + if (DeclWithIssue) + return DeclWithIssue; + + const ExplodedNode *N = getErrorNode(); + if (!N) + return 0; + + const LocationContext *LC = N->getLocationContext(); + return LC->getCurrentStackFrame()->getDecl(); +} + void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { hash.AddPointer(&BT); hash.AddString(Description); - if (Location.isValid()) { + if (UniqueingLocation.isValid()) { + UniqueingLocation.Profile(hash); + } else if (Location.isValid()) { Location.Profile(hash); } else { assert(ErrorNode); @@ -1251,6 +1275,61 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { } } +void BugReport::markInteresting(SymbolRef sym) { + if (!sym) + return; + + // If the symbol wasn't already in our set, note a configuration change. + if (interestingSymbols.insert(sym).second) + ++ConfigurationChangeToken; + + if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym)) + interestingRegions.insert(meta->getRegion()); +} + +void BugReport::markInteresting(const MemRegion *R) { + if (!R) + return; + + // If the base region wasn't already in our set, note a configuration change. + R = R->getBaseRegion(); + if (interestingRegions.insert(R).second) + ++ConfigurationChangeToken; + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + interestingSymbols.insert(SR->getSymbol()); +} + +void BugReport::markInteresting(SVal V) { + markInteresting(V.getAsRegion()); + markInteresting(V.getAsSymbol()); +} + +bool BugReport::isInteresting(SVal V) const { + return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); +} + +bool BugReport::isInteresting(SymbolRef sym) const { + if (!sym) + return false; + // We don't currently consider metadata symbols to be interesting + // even if we know their region is interesting. Is that correct behavior? + return interestingSymbols.count(sym); +} + +bool BugReport::isInteresting(const MemRegion *R) const { + if (!R) + return false; + R = R->getBaseRegion(); + bool b = interestingRegions.count(R); + if (b) + return true; + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + return interestingSymbols.count(SR->getSymbol()); + return false; +} + + const Stmt *BugReport::getStmt() const { if (!ErrorNode) return 0; @@ -1316,10 +1395,7 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { // Methods for BugReporter and subclasses. //===----------------------------------------------------------------------===// -BugReportEquivClass::~BugReportEquivClass() { - for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; -} - +BugReportEquivClass::~BugReportEquivClass() { } GRBugReporter::~GRBugReporter() { } BugReporterData::~BugReporterData() {} @@ -1394,8 +1470,8 @@ MakeReportGraph(const ExplodedGraph* G, // Create owning pointers for GTrim and NMap just to ensure that they are // released when this function exists. - llvm::OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim); - llvm::OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap); + OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim); + OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap); // Find the (first) error node in the trimmed graph. We just need to consult // the node map (NMap) which maps from nodes in the original graph to nodes @@ -1513,19 +1589,28 @@ MakeReportGraph(const ExplodedGraph* G, /// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object /// and collapses PathDiagosticPieces that are expanded by macros. -static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { - typedef std::vector<std::pair<PathDiagnosticMacroPiece*, SourceLocation> > - MacroStackTy; +static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { + typedef std::vector<std::pair<IntrusiveRefCntPtr<PathDiagnosticMacroPiece>, + SourceLocation> > MacroStackTy; - typedef std::vector<PathDiagnosticPiece*> + typedef std::vector<IntrusiveRefCntPtr<PathDiagnosticPiece> > PiecesTy; MacroStackTy MacroStack; PiecesTy Pieces; - for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) { + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I!=E; ++I) { + + PathDiagnosticPiece *piece = I->getPtr(); + + // Recursively compact calls. + if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){ + CompactPathDiagnostic(call->path, SM); + } + // Get the location of the PathDiagnosticPiece. - const FullSourceLoc Loc = I->getLocation().asLocation(); + const FullSourceLoc Loc = piece->getLocation().asLocation(); // Determine the instantiation location, which is the location we group // related PathDiagnosticPieces. @@ -1535,7 +1620,7 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { if (Loc.isFileID()) { MacroStack.clear(); - Pieces.push_back(&*I); + Pieces.push_back(piece); continue; } @@ -1543,13 +1628,13 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { // Is the PathDiagnosticPiece within the same macro group? if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { - MacroStack.back().first->push_back(&*I); + MacroStack.back().first->subPieces.push_back(piece); continue; } // We aren't in the same group. Are we descending into a new macro // or are part of an old one? - PathDiagnosticMacroPiece *MacroGroup = 0; + IntrusiveRefCntPtr<PathDiagnosticMacroPiece> MacroGroup; SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? SM.getExpansionLoc(Loc) : @@ -1574,10 +1659,10 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { // Create a new macro group and add it to the stack. PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece( - PathDiagnosticLocation::createSingleLocation(I->getLocation())); + PathDiagnosticLocation::createSingleLocation(piece->getLocation())); if (MacroGroup) - MacroGroup->push_back(NewGroup); + MacroGroup->subPieces.push_back(NewGroup); else { assert(InstantiationLoc.isFileID()); Pieces.push_back(NewGroup); @@ -1588,21 +1673,14 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { } // Finally, add the PathDiagnosticPiece to the group. - MacroGroup->push_back(&*I); + MacroGroup->subPieces.push_back(piece); } // Now take the pieces and construct a new PathDiagnostic. - PD.resetPath(false); + path.clear(); - for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) { - if (PathDiagnosticMacroPiece *MP=dyn_cast<PathDiagnosticMacroPiece>(*I)) - if (!MP->containsEvent()) { - delete MP; - continue; - } - - PD.push_back(*I); - } + for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) + path.push_back(*I); } void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, @@ -1626,8 +1704,8 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, BugReport *R = bugReports[GPair.second.second]; assert(R && "No original report found for sliced graph."); - llvm::OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); - llvm::OwningPtr<NodeBackMap> BackMap(GPair.first.second); + OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); + OwningPtr<NodeBackMap> BackMap(GPair.first.second); const ExplodedNode *N = GPair.second.first; // Start building the path diagnostic... @@ -1638,32 +1716,61 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, R->addVisitor(new NilReceiverBRVisitor()); R->addVisitor(new ConditionBRVisitor()); - // Generate the very last diagnostic piece - the piece is visible before - // the trace is expanded. - PathDiagnosticPiece *LastPiece = 0; - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { - assert (!LastPiece && - "There can only be one final piece in a diagnostic."); - LastPiece = Piece; + BugReport::VisitorList visitors; + unsigned originalReportConfigToken, finalReportConfigToken; + + // While generating diagnostics, it's possible the visitors will decide + // new symbols and regions are interesting, or add other visitors based on + // the information they find. If they do, we need to regenerate the path + // based on our new report configuration. + do { + // Get a clean copy of all the visitors. + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); I != E; ++I) + visitors.push_back((*I)->clone()); + + // Clear out the active path from any previous work. + PD.getActivePath().clear(); + originalReportConfigToken = R->getConfigurationChangeToken(); + + // Generate the very last diagnostic piece - the piece is visible before + // the trace is expanded. + PathDiagnosticPiece *LastPiece = 0; + for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end(); + I != E; ++I) { + if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { + assert (!LastPiece && + "There can only be one final piece in a diagnostic."); + LastPiece = Piece; + } } - } - if (!LastPiece) - LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); - if (LastPiece) - PD.push_back(LastPiece); - else - return; + if (!LastPiece) + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); + if (LastPiece) + PD.getActivePath().push_back(LastPiece); + else + return; - switch (PDB.getGenerationScheme()) { + switch (PDB.getGenerationScheme()) { case PathDiagnosticConsumer::Extensive: - GenerateExtensivePathDiagnostic(PD, PDB, N); + GenerateExtensivePathDiagnostic(PD, PDB, N, visitors); break; case PathDiagnosticConsumer::Minimal: - GenerateMinimalPathDiagnostic(PD, PDB, N); + GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); break; - } + } + + // Clean up the visitors we used. + llvm::DeleteContainerPointers(visitors); + + // Did anything change while generating this path? + finalReportConfigToken = R->getConfigurationChangeToken(); + } while(finalReportConfigToken != originalReportConfigToken); + + // Finally, prune the diagnostic path of uninteresting stuff. + bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); + assert(hasSomethingInteresting); + (void) hasSomethingInteresting; } void BugReporter::Register(BugType *BT) { @@ -1711,17 +1818,17 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - BugReport *R = *I; - BugType& BT = R->getBugType(); + BugType& BT = I->getBugType(); // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class // to 'Nodes'. Any of the reports will serve as a "representative" report. if (!BT.isSuppressOnSink()) { + BugReport *R = I; for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { const ExplodedNode *N = I->getErrorNode(); if (N) { - R = *I; + R = I; bugReports.push_back(R); } } @@ -1737,8 +1844,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, BugReport *exampleReport = 0; for (; I != E; ++I) { - R = *I; - const ExplodedNode *errorNode = R->getErrorNode(); + const ExplodedNode *errorNode = I->getErrorNode(); if (!errorNode) continue; @@ -1748,9 +1854,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { - bugReports.push_back(R); + bugReports.push_back(I); if (!exampleReport) - exampleReport = R; + exampleReport = I; continue; } @@ -1774,9 +1880,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, if (Succ->succ_empty()) { // If we found an end-of-path node that is not a sink. if (!Succ->isSink()) { - bugReports.push_back(R); + bugReports.push_back(I); if (!exampleReport) - exampleReport = R; + exampleReport = I; WL.clear(); break; } @@ -1857,8 +1963,9 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { // Probably doesn't make a difference in practice. BugType& BT = exampleReport->getBugType(); - llvm::OwningPtr<PathDiagnostic> - D(new PathDiagnostic(exampleReport->getBugType().getName(), + OwningPtr<PathDiagnostic> + D(new PathDiagnostic(exampleReport->getDeclWithIssue(), + exampleReport->getBugType().getName(), !PD || PD->useVerboseDescription() ? exampleReport->getDescription() : exampleReport->getShortDescription(), @@ -1866,9 +1973,6 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { if (!bugReports.empty()) GeneratePathDiagnostic(*D.get(), bugReports); - - if (IsCachedDiagnostic(exampleReport, D.get())) - return; // Get the meta data. const BugReport::ExtraTextList &Meta = @@ -1883,24 +1987,23 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { llvm::tie(Beg, End) = exampleReport->getRanges(); DiagnosticsEngine &Diag = getDiagnostic(); - // Search the description for '%', as that will be interpretted as a - // format character by FormatDiagnostics. - StringRef desc = exampleReport->getShortDescription(); - unsigned ErrorDiag; - { - llvm::SmallString<512> TmpStr; + if (!IsCachedDiagnostic(exampleReport, D.get())) { + // Search the description for '%', as that will be interpretted as a + // format character by FormatDiagnostics. + StringRef desc = exampleReport->getShortDescription(); + + SmallString<512> TmpStr; llvm::raw_svector_ostream Out(TmpStr); - for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) + for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) { if (*I == '%') Out << "%%"; else Out << *I; + } Out.flush(); - ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); - } + unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); - { DiagnosticBuilder diagBuilder = Diag.Report( exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag); for (BugReport::ranges_iterator I = Beg; I != End; ++I) @@ -1911,25 +2014,21 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { if (!PD) return; - if (D->empty()) { + if (D->path.empty()) { PathDiagnosticPiece *piece = new PathDiagnosticEventPiece( exampleReport->getLocation(getSourceManager()), exampleReport->getDescription()); + for ( ; Beg != End; ++Beg) + piece->addRange(*Beg); - for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->push_back(piece); + D->getActivePath().push_back(piece); } PD->HandlePathDiagnostic(D.take()); } -void BugReporter::EmitBasicReport(StringRef name, StringRef str, - PathDiagnosticLocation Loc, - SourceRange* RBeg, unsigned NumRanges) { - EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); -} - -void BugReporter::EmitBasicReport(StringRef name, +void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, + StringRef name, StringRef category, StringRef str, PathDiagnosticLocation Loc, SourceRange* RBeg, unsigned NumRanges) { @@ -1937,13 +2036,14 @@ void BugReporter::EmitBasicReport(StringRef name, // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(name, category); BugReport *R = new BugReport(*BT, str, Loc); + R->setDeclWithIssue(DeclWithIssue); for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); EmitReport(R); } BugType *BugReporter::getBugTypeForName(StringRef name, StringRef category) { - llvm::SmallString<136> fullDesc; + SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << name << ":" << category; llvm::StringMapEntry<BugType *> & entry = StrBugTypes.GetOrCreateValue(fullDesc); diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 1abd8ba..6532486 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallString.h" using namespace clang; using namespace ento; @@ -84,26 +85,8 @@ PathDiagnosticPiece* BugReporterVisitor::getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { - const ProgramPoint &PP = EndPathNode->getLocation(); - PathDiagnosticLocation L; - - if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { - const CFGBlock *block = BE->getBlock(); - if (block->getBlockID() == 0) { - L = PathDiagnosticLocation::createDeclEnd(PP.getLocationContext(), - BRC.getSourceManager()); - } - } - - if (!L.isValid()) { - const Stmt *S = BR.getStmt(); - - if (!S) - return NULL; - - L = PathDiagnosticLocation(S, BRC.getSourceManager(), - PP.getLocationContext()); - } + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); BugReport::ranges_iterator Beg, End; llvm::tie(Beg, End) = BR.getRanges(); @@ -138,17 +121,20 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, if (!StoreSite) { const ExplodedNode *Node = N, *Last = NULL; - for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + for ( ; Node ; Node = Node->getFirstPred()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { if (const PostStmt *P = Node->getLocationAs<PostStmt>()) if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) if (DS->getSingleDecl() == VR->getDecl()) { + // Record the last seen initialization point. Last = Node; break; } } + // Does the region still bind to value V? If not, we are done + // looking for store sites. if (Node->getState()->getSVal(R) != V) break; } @@ -165,7 +151,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *N, return NULL; satisfied = true; - llvm::SmallString<256> sbuf; + SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { @@ -301,7 +287,8 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, BugReporterVisitor * bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, - const Stmt *S) { + const Stmt *S, + BugReport *report) { if (!S || !N) return 0; @@ -321,25 +308,27 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, if (!N) return 0; - const ProgramState *state = N->getState(); - - // Walk through lvalue-to-rvalue conversions. - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - const VarRegion *R = - StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + ProgramStateRef state = N->getState(); - // What did we load? - SVal V = state->getSVal(loc::MemRegionVal(R)); + // Walk through lvalue-to-rvalue conversions. + const Expr *Ex = dyn_cast<Expr>(S); + if (Ex) { + Ex = Ex->IgnoreParenLValueCasts(); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); - if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) - || V.isUndef()) { + // What did we load? + SVal V = state->getSVal(loc::MemRegionVal(R)); + report->markInteresting(R); + report->markInteresting(V); return new FindLastStoreBRVisitor(V, R); } } } - SVal V = state->getSValAsScalarOrLoc(S); + SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. @@ -353,7 +342,7 @@ bugreporter::getTrackNullOrUndefValueVisitor(const ExplodedNode *N, } if (R) { - assert(isa<SymbolicRegion>(R)); + report->markInteresting(R); return new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); } } @@ -366,7 +355,7 @@ FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, const MemRegion *R) { assert(R && "The memory region is null."); - const ProgramState *state = N->getState(); + ProgramStateRef state = N->getState(); SVal V = state->getSVal(R); if (V.isUnknown()) return 0; @@ -388,8 +377,8 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, const Expr *Receiver = ME->getInstanceReceiver(); if (!Receiver) return 0; - const ProgramState *state = N->getState(); - const SVal &V = state->getSVal(Receiver); + ProgramStateRef state = N->getState(); + const SVal &V = state->getSVal(Receiver, N->getLocationContext()); const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); if (!DV) return 0; @@ -400,11 +389,11 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver)); + BR.addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Receiver, &BR)); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); - return new PathDiagnosticEventPiece(L, "No method actually called " + return new PathDiagnosticEventPiece(L, "No method is called " "because the receiver is nil"); } @@ -419,7 +408,7 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, const Stmt *Head = WorkList.front(); WorkList.pop_front(); - const ProgramState *state = N->getState(); + ProgramStateRef state = N->getState(); ProgramStateManager &StateMgr = state->getStateManager(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { @@ -428,7 +417,7 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? - SVal V = state->getSVal(S); + SVal V = state->getSVal(S, N->getLocationContext()); if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { // Register a new visitor with the BugReport. @@ -450,11 +439,22 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *Prev, BugReporterContext &BRC, BugReport &BR) { + PathDiagnosticPiece *piece = VisitNodeImpl(N, Prev, BRC, BR); + if (PathDiagnosticEventPiece *ev = + dyn_cast_or_null<PathDiagnosticEventPiece>(piece)) + ev->setPrunable(true, /* override */ false); + return piece; +} + +PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, + const ExplodedNode *Prev, + BugReporterContext &BRC, + BugReport &BR) { const ProgramPoint &progPoint = N->getLocation(); - const ProgramState *CurrentState = N->getState(); - const ProgramState *PrevState = Prev->getState(); + ProgramStateRef CurrentState = N->getState(); + ProgramStateRef PrevState = Prev->getState(); // Compare the GDMs of the state, because that is where constraints // are managed. Note that ensure that we only look at nodes that @@ -468,7 +468,7 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) { const CFGBlock *srcBlk = BE->getSrc(); if (const Stmt *term = srcBlk->getTerminator()) - return VisitTerminator(term, N, srcBlk, BE->getDst(), BRC); + return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); return 0; } @@ -482,10 +482,10 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNode(const ExplodedNode *N, const ProgramPointTag *tag = PS->getTag(); if (tag == tags.first) return VisitTrueTest(cast<Expr>(PS->getStmt()), true, - BRC, N->getLocationContext()); + BRC, BR, N); if (tag == tags.second) return VisitTrueTest(cast<Expr>(PS->getStmt()), false, - BRC, N->getLocationContext()); + BRC, BR, N); return 0; } @@ -498,6 +498,7 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk, const CFGBlock *dstBlk, + BugReport &R, BugReporterContext &BRC) { const Expr *Cond = 0; @@ -516,14 +517,15 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, assert(srcBlk->succ_size() == 2); const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; return VisitTrueTest(Cond->IgnoreParenNoopCasts(BRC.getASTContext()), - tookTrue, BRC, N->getLocationContext()); + tookTrue, BRC, R, N); } PathDiagnosticPiece * ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, BugReporterContext &BRC, - const LocationContext *LC) { + BugReport &R, + const ExplodedNode *N) { const Expr *Ex = Cond; @@ -533,9 +535,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, default: return 0; case Stmt::BinaryOperatorClass: - return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, LC); + return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, + R, N); case Stmt::DeclRefExprClass: - return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, LC); + return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, + R, N); case Stmt::UnaryOperatorClass: { const UnaryOperator *UO = cast<UnaryOperator>(Ex); if (UO->getOpcode() == UO_LNot) { @@ -550,14 +554,31 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, } bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, - BugReporterContext &BRC) { + BugReporterContext &BRC, + BugReport &report, + const ExplodedNode *N, + llvm::Optional<bool> &prunable) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { const bool quotes = isa<VarDecl>(DR->getDecl()); - if (quotes) + if (quotes) { Out << '\''; + const LocationContext *LCtx = N->getLocationContext(); + const ProgramState *state = N->getState().getPtr(); + if (const MemRegion *R = state->getLValue(cast<VarDecl>(DR->getDecl()), + LCtx).getAsRegion()) { + if (report.isInteresting(R)) + prunable = false; + else { + const ProgramState *state = N->getState().getPtr(); + SVal V = state->getSVal(R); + if (report.isInteresting(V)) + prunable = false; + } + } + } Out << DR->getDecl()->getDeclName().getAsString(); if (quotes) Out << '\''; @@ -591,31 +612,43 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, const bool tookTrue, BugReporterContext &BRC, - const LocationContext *LC) { + BugReport &R, + const ExplodedNode *N) { bool shouldInvert = false; + llvm::Optional<bool> shouldPrune; - llvm::SmallString<128> LhsString, RhsString; + SmallString<128> LhsString, RhsString; { - llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); - const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC); - const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC); + llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); + const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC, R, N, + shouldPrune); + const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N, + shouldPrune); shouldInvert = !isVarLHS && isVarRHS; } + BinaryOperator::Opcode Op = BExpr->getOpcode(); + + if (BinaryOperator::isAssignmentOp(Op)) { + // For assignment operators, all that we care about is that the LHS + // evaluates to "true" or "false". + return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue, + BRC, R, N); + } + + // For non-assignment operations, we require that we can understand + // both the LHS and RHS. if (LhsString.empty() || RhsString.empty()) return 0; - - // Should we invert the strings if the LHS is not a variable name? - llvm::SmallString<256> buf; + // Should we invert the strings if the LHS is not a variable name? + SmallString<256> buf; llvm::raw_svector_ostream Out(buf); Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; // Do we need to invert the opcode? - BinaryOperator::Opcode Op = BExpr->getOpcode(); - if (shouldInvert) switch (Op) { default: break; @@ -637,7 +670,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, return 0; } - switch (BExpr->getOpcode()) { + switch (Op) { case BO_EQ: Out << "equal to "; break; @@ -650,9 +683,55 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, } Out << (shouldInvert ? LhsString : RhsString); + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + PathDiagnosticEventPiece *event = + new PathDiagnosticEventPiece(Loc, Out.str()); + if (shouldPrune.hasValue()) + event->setPrunable(shouldPrune.getValue()); + return event; +} - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); - return new PathDiagnosticEventPiece(Loc, Out.str()); +PathDiagnosticPiece * +ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, + const Expr *CondVarExpr, + const bool tookTrue, + BugReporterContext &BRC, + BugReport &report, + const ExplodedNode *N) { + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + Out << "Assuming " << LhsString << " is "; + + QualType Ty = CondVarExpr->getType(); + + if (Ty->isPointerType()) + Out << (tookTrue ? "not null" : "null"); + else if (Ty->isObjCObjectPointerType()) + Out << (tookTrue ? "not nil" : "nil"); + else if (Ty->isBooleanType()) + Out << (tookTrue ? "true" : "false"); + else if (Ty->isIntegerType()) + Out << (tookTrue ? "non-zero" : "zero"); + else + return 0; + + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); + PathDiagnosticEventPiece *event = + new PathDiagnosticEventPiece(Loc, Out.str()); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const ProgramState *state = N->getState().getPtr(); + if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { + if (report.isInteresting(R)) + event->setPrunable(false); + } + } + } + + return event; } PathDiagnosticPiece * @@ -660,13 +739,14 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, const bool tookTrue, BugReporterContext &BRC, - const LocationContext *LC) { + BugReport &report, + const ExplodedNode *N) { const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); if (!VD) return 0; - llvm::SmallString<256> Buf; + SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); Out << "Assuming '"; @@ -684,6 +764,21 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, else return 0; - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LC); - return new PathDiagnosticEventPiece(Loc, Out.str()); + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + PathDiagnosticEventPiece *event = + new PathDiagnosticEventPiece(Loc, Out.str()); + + const ProgramState *state = N->getState().getPtr(); + if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { + if (report.isInteresting(R)) + event->setPrunable(false); + else { + SVal V = state->getSVal(R); + if (report.isInteresting(V)) + event->setPrunable(false); + } + } + return event; } + diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 391a781..1ea25bd 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS support) set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) add_clang_library(clangStaticAnalyzerCore - AggExprVisitor.cpp AnalysisManager.cpp BasicConstraintManager.cpp BasicValueFactory.cpp @@ -23,6 +22,7 @@ add_clang_library(clangStaticAnalyzerCore ExprEngineCXX.cpp ExprEngineCallAndReturn.cpp ExprEngineObjC.cpp + FunctionSummary.cpp HTMLDiagnostics.cpp MemRegion.cpp ObjCMessage.cpp @@ -36,6 +36,7 @@ add_clang_library(clangStaticAnalyzerCore SimpleConstraintManager.cpp SimpleSValBuilder.cpp Store.cpp + SubEngine.cpp SymbolManager.cpp TextPathDiagnostics.cpp ) diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index a3bf2c2..07e0aac 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/Checker.h" using namespace clang; @@ -20,3 +21,11 @@ StringRef CheckerBase::getTagDescription() const { // FIXME: We want to return the package + name of the checker here. return "A Checker"; } + +void Checker<check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck, + check::_VoidCheck, check::_VoidCheck, check::_VoidCheck + >::anchor() { } diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 5356edc..0a047d9 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -13,21 +13,71 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/Basic/Builtins.h" +#include "clang/Lex/Lexer.h" + using namespace clang; using namespace ento; -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 (ST && ST != Pred->getState()) { - static SimpleProgramPointTag autoTransitionTag("CheckerContext : auto"); - addTransition(ST, &autoTransitionTag); - } - else - Dst.Add(Pred); +const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { + ProgramStateRef State = getState(); + const Expr *Callee = CE->getCallee(); + SVal L = State->getSVal(Callee, Pred->getLocationContext()); + return L.getAsFunctionDecl(); +} + +StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const { + if (!FunDecl) + return StringRef(); + IdentifierInfo *funI = FunDecl->getIdentifier(); + if (!funI) + return StringRef(); + return funI->getName(); +} + + +bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, + StringRef Name) { + return isCLibraryFunction(FD, Name, getASTContext()); +} + +bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, + StringRef Name, ASTContext &Context) { + // To avoid false positives (Ex: finding user defined functions with + // similar names), only perform fuzzy name matching when it's a builtin. + // Using a string compare is slow, we might want to switch on BuiltinID here. + unsigned BId = FD->getBuiltinID(); + if (BId != 0) { + StringRef BName = Context.BuiltinInfo.GetName(BId); + if (BName.find(Name) != StringRef::npos) + return true; } + + const IdentifierInfo *II = FD->getIdentifier(); + // If this is a special C++ name without IdentifierInfo, it can't be a + // C library function. + if (!II) + return false; + + StringRef FName = II->getName(); + if (FName.equals(Name)) + return true; + + if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos)) + return true; + + if (FName.startswith("__") && FName.endswith("_chk") && + FName.find(Name) != StringRef::npos) + return true; + + return false; +} + +StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) { + if (Loc.isMacroID()) + return Lexer::getImmediateMacroName(Loc, getSourceManager(), + getLangOpts()); + SmallVector<char, 16> buf; + return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts()); } + diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index acacfb0..0bcc343 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -93,6 +93,9 @@ template <typename CHECK_CTX> static void expandGraphWithCheckers(CHECK_CTX checkCtx, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src) { + const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext(); + if (Src.empty()) + return; typename CHECK_CTX::CheckersTy::const_iterator I = checkCtx.checkers_begin(), E = checkCtx.checkers_end(); @@ -113,9 +116,15 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx, CurrSet->clear(); } + NodeBuilder B(*PrevSet, *CurrSet, BldrCtx); for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checkCtx.runChecker(*I, *CurrSet, *NI); + NI != NE; ++NI) { + checkCtx.runChecker(*I, B, *NI); + } + + // If all the produced transitions are sinks, stop. + if (CurrSet->empty()) + return; // Update which NodeSet is the current one. PrevSet = CurrSet; @@ -129,23 +138,24 @@ namespace { const CheckersTy &Checkers; const Stmt *S; ExprEngine &Eng; + bool wasInlined; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, - const Stmt *s, ExprEngine &eng) - : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { } + const Stmt *s, ExprEngine &eng, bool wasInlined = false) + : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), + wasInlined(wasInlined) {} void runChecker(CheckerManager::CheckStmtFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { // FIXME: Remove respondsToCallback from CheckerContext; ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : ProgramPoint::PostStmtKind; const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); - + CheckerContext C(Bldr, Eng, Pred, L, wasInlined); checkFn(S, C); } }; @@ -156,9 +166,10 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const Stmt *S, - ExprEngine &Eng) { + ExprEngine &Eng, + bool wasInlined) { CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit), - S, Eng); + S, Eng, wasInlined); expandGraphWithCheckers(C, Dst, Src); } @@ -178,12 +189,14 @@ namespace { : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { } void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : ProgramPoint::PostStmtKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(Msg.getOriginExpr(), - K, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + const ProgramPoint &L = + ProgramPoint::getProgramPoint(Msg.getMessageExpr(), + K, Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); checkFn(Msg, C); } @@ -209,35 +222,44 @@ namespace { const CheckersTy &Checkers; SVal Loc; bool IsLoad; - const Stmt *S; + const Stmt *NodeEx; /* Will become a CFGStmt */ + const Stmt *BoundEx; ExprEngine &Eng; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckLocationContext(const CheckersTy &checkers, - SVal loc, bool isLoad, const Stmt *s, ExprEngine &eng) - : Checkers(checkers), Loc(loc), IsLoad(isLoad), S(s), Eng(eng) { } + SVal loc, bool isLoad, const Stmt *NodeEx, + const Stmt *BoundEx, + ExprEngine &eng) + : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), + BoundEx(BoundEx), Eng(eng) {} void runChecker(CheckerManager::CheckLocationFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind : ProgramPoint::PreStoreKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, - Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); - - checkFn(Loc, IsLoad, S, C); + const ProgramPoint &L = + ProgramPoint::getProgramPoint(NodeEx, K, + Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(Loc, IsLoad, BoundEx, C); } }; } /// \brief Run checkers for load/store of a location. + void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, bool isLoad, - const Stmt *S, ExprEngine &Eng) { - CheckLocationContext C(LocationCheckers, location, isLoad, S, Eng); + const Stmt *NodeEx, + const Stmt *BoundEx, + ExprEngine &Eng) { + CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx, + BoundEx, Eng); expandGraphWithCheckers(C, Dst, Src); } @@ -249,20 +271,21 @@ namespace { SVal Val; const Stmt *S; ExprEngine &Eng; + ProgramPoint::Kind PointKind; CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } CheckBindContext(const CheckersTy &checkers, - SVal loc, SVal val, const Stmt *s, ExprEngine &eng) - : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng) { } + SVal loc, SVal val, const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind PK) + : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PointKind(PK) {} void runChecker(CheckerManager::CheckBindFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { - ProgramPoint::Kind K = ProgramPoint::PreStmtKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, PointKind, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + CheckerContext C(Bldr, Eng, Pred, L); checkFn(Loc, Val, S, C); } @@ -273,8 +296,9 @@ namespace { void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, - const Stmt *S, ExprEngine &Eng) { - CheckBindContext C(BindCheckers, location, val, S, Eng); + const Stmt *S, ExprEngine &Eng, + ProgramPoint::Kind PointKind) { + CheckBindContext C(BindCheckers, location, val, S, Eng, PointKind); expandGraphWithCheckers(C, Dst, Src); } @@ -286,27 +310,65 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, } /// \brief Run checkers for end of path. -void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B, +// Note, We do not chain the checker output (like in expandGraphWithCheckers) +// for this callback since end of path nodes are expected to be final. +void CheckerManager::runCheckersForEndPath(NodeBuilderContext &BC, + ExplodedNodeSet &Dst, ExprEngine &Eng) { + ExplodedNode *Pred = BC.Pred; + + // We define the builder outside of the loop bacause if at least one checkers + // creates a sucsessor for Pred, we do not need to generate an + // autotransition for it. + NodeBuilder Bldr(Pred, Dst, BC); for (unsigned i = 0, e = EndPathCheckers.size(); i != e; ++i) { - CheckEndPathFunc fn = EndPathCheckers[i]; - EndOfFunctionNodeBuilder specialB = B.withCheckerTag(fn.Checker); - fn(specialB, Eng); + CheckEndPathFunc checkFn = EndPathCheckers[i]; + + const ProgramPoint &L = BlockEntrance(BC.Block, + Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(C); } } +namespace { + struct CheckBranchConditionContext { + typedef std::vector<CheckerManager::CheckBranchConditionFunc> CheckersTy; + const CheckersTy &Checkers; + const Stmt *Condition; + ExprEngine &Eng; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckBranchConditionContext(const CheckersTy &checkers, + const Stmt *Cond, ExprEngine &eng) + : Checkers(checkers), Condition(Cond), Eng(eng) {} + + void runChecker(CheckerManager::CheckBranchConditionFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(Condition, C); + } + }; +} + /// \brief Run checkers for branch condition. -void CheckerManager::runCheckersForBranchCondition(const Stmt *condition, - BranchNodeBuilder &B, +void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, ExprEngine &Eng) { - for (unsigned i = 0, e = BranchConditionCheckers.size(); i != e; ++i) { - CheckBranchConditionFunc fn = BranchConditionCheckers[i]; - fn(condition, B, Eng); - } + ExplodedNodeSet Src; + Src.insert(Pred); + CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng); + expandGraphWithCheckers(C, Dst, Src); } /// \brief Run checkers for live symbols. -void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state, +void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state, SymbolReaper &SymReaper) { for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i) LiveSymbolsCheckers[i](state, SymReaper); @@ -328,11 +390,11 @@ namespace { : Checkers(checkers), SR(sr), S(s), Eng(eng) { } void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, - ExplodedNodeSet &Dst, ExplodedNode *Pred) { + NodeBuilder &Bldr, ExplodedNode *Pred) { ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind; const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, Pred->getLocationContext(), checkFn.Checker); - CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, L, 0); + CheckerContext C(Bldr, Eng, Pred, L); checkFn(SR, C); } @@ -350,7 +412,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, } /// \brief True if at least one checker wants to check region changes. -bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) { +bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) if (RegionChangesCheckers[i].WantUpdateFn(state)) return true; @@ -359,25 +421,26 @@ bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) { } /// \brief Run checkers for region changes. -const ProgramState * -CheckerManager::runCheckersForRegionChanges(const ProgramState *state, +ProgramStateRef +CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions) { + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. if (!state) return NULL; state = RegionChangesCheckers[i].CheckFn(state, invalidated, - ExplicitRegions, Regions); + ExplicitRegions, Regions, Call); } return state; } /// \brief Run checkers for handling assumptions on symbolic values. -const ProgramState * -CheckerManager::runCheckersForEvalAssume(const ProgramState *state, +ProgramStateRef +CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, SVal Cond, bool Assumption) { for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), @@ -437,11 +500,12 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } #endif + ExplodedNodeSet checkDst; + NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); // Next, check if any of the EvalCall callbacks can evaluate the call. for (std::vector<EvalCallFunc>::iterator EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end(); EI != EE; ++EI) { - ExplodedNodeSet checkDst; ProgramPoint::Kind K = ProgramPoint::PostStmtKind; const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(), EI->Checker); @@ -449,7 +513,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, { // CheckerContext generates transitions(populates checkDest) on // destruction, so introduce the scope to make sure it gets properly // populated. - CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, L, 0); + CheckerContext C(B, Eng, Pred, L); evaluated = (*EI)(CE, C); } assert(!(evaluated && anyEvaluated) @@ -483,7 +547,7 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit( } void CheckerManager::runCheckersForPrintState(raw_ostream &Out, - const ProgramState *State, + ProgramStateRef State, const char *NL, const char *Sep) { for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I) diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index 13401ac..9791e2ec 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -9,12 +9,13 @@ #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" +#include "llvm/ADT/SetVector.h" using namespace clang; using namespace ento; static const char PackageSeparator = '.'; -typedef llvm::DenseSet<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; +typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, @@ -72,7 +73,7 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, if (opt.isEnabled()) collected.insert(&*i); else - collected.erase(&*i); + collected.remove(&*i); } } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 5252198..eb986af 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "CoreEngine" + #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -20,9 +22,16 @@ #include "clang/AST/StmtCXX.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Statistic.h" + using namespace clang; using namespace ento; +STATISTIC(NumReachedMaxSteps, + "The # of times we reached the max number of steps."); +STATISTIC(NumPathsExplored, + "The # of paths explored by the analyzer."); + //===----------------------------------------------------------------------===// // Worklist classes for exploration of reachable states. //===----------------------------------------------------------------------===// @@ -152,7 +161,7 @@ WorkList* WorkList::makeBFSBlockDFSContents() { /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, - const ProgramState *InitState) { + ProgramStateRef InitState) { if (G->num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. @@ -165,6 +174,11 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); + // Mark the entry block as visited. + FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(), + L->getDecl(), + L->getCFG()->getNumBlockIDs()); + // Get the solitary successor. const CFGBlock *Succ = *(Entry->succ_begin()); @@ -187,8 +201,10 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, while (WList->hasWork()) { if (!UnlimitedSteps) { - if (Steps == 0) + if (Steps == 0) { + NumReachedMaxSteps++; break; + } --Steps; } @@ -200,67 +216,80 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, // Retrieve the node. ExplodedNode *Node = WU.getNode(); - // Dispatch on the location type. - switch (Node->getLocation().getKind()) { - case ProgramPoint::BlockEdgeKind: - HandleBlockEdge(cast<BlockEdge>(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockEntranceKind: - HandleBlockEntrance(cast<BlockEntrance>(Node->getLocation()), Node); - break; - - case ProgramPoint::BlockExitKind: - assert (false && "BlockExit location never occur in forward analysis."); - break; + dispatchWorkItem(Node, Node->getLocation(), WU); + } + SubEng.processEndWorklist(hasWorkRemaining()); + return WList->hasWork(); +} - case ProgramPoint::CallEnterKind: - HandleCallEnter(cast<CallEnter>(Node->getLocation()), WU.getBlock(), - WU.getIndex(), Node); - break; +void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU) { + // Dispatch on the location type. + switch (Loc.getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(cast<BlockEdge>(Loc), Pred); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(cast<BlockEntrance>(Loc), Pred); + break; + + case ProgramPoint::BlockExitKind: + assert (false && "BlockExit location never occur in forward analysis."); + break; + + case ProgramPoint::CallEnterKind: { + CallEnter CEnter = cast<CallEnter>(Loc); + if (AnalyzedCallees) + if (const CallExpr* CE = + dyn_cast_or_null<CallExpr>(CEnter.getCallExpr())) + if (const Decl *CD = CE->getCalleeDecl()) + AnalyzedCallees->insert(CD); + SubEng.processCallEnter(CEnter, Pred); + break; + } - case ProgramPoint::CallExitKind: - HandleCallExit(cast<CallExit>(Node->getLocation()), Node); - break; + case ProgramPoint::CallExitKind: + SubEng.processCallExit(Pred); + break; - default: - assert(isa<PostStmt>(Node->getLocation()) || - isa<PostInitializer>(Node->getLocation())); - HandlePostStmt(WU.getBlock(), WU.getIndex(), Node); - break; + case ProgramPoint::EpsilonKind: { + assert(Pred->hasSinglePred() && + "Assume epsilon has exactly one predecessor by construction"); + ExplodedNode *PNode = Pred->getFirstPred(); + dispatchWorkItem(Pred, PNode->getLocation(), WU); + break; } + default: + assert(isa<PostStmt>(Loc) || + isa<PostInitializer>(Loc)); + HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); + break; } - - SubEng.processEndWorklist(hasWorkRemaining()); - return WList->hasWork(); } -void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, +bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, - const ProgramState *InitState, + ProgramStateRef InitState, ExplodedNodeSet &Dst) { - ExecuteWorkList(L, Steps, InitState); - for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), - E = G->EndNodes.end(); I != E; ++I) { + bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); + for (ExplodedGraph::eop_iterator I = G->eop_begin(), + E = G->eop_end(); I != E; ++I) { Dst.Add(*I); } -} - -void CoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block, - unsigned Index, ExplodedNode *Pred) { - CallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), - L.getCalleeContext(), Block, Index); - SubEng.processCallEnter(Builder); -} - -void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { - CallExitNodeBuilder Builder(*this, Pred); - SubEng.processCallExit(Builder); + return DidNotFinish; } void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { const CFGBlock *Blk = L.getDst(); + NodeBuilderContext BuilderCtx(*this, Blk, Pred); + + // Mark this block as visited. + const LocationContext *LC = Pred->getLocationContext(); + FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(), + LC->getDecl(), + LC->getCFG()->getNumBlockIDs()); // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { @@ -269,53 +298,42 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { && "EXIT block cannot contain Stmts."); // Process the final state transition. - EndOfFunctionNodeBuilder Builder(Blk, Pred, this); - SubEng.processEndOfFunction(Builder); + SubEng.processEndOfFunction(BuilderCtx); // This path is done. Don't enqueue any more nodes. return; } - // Call into the subengine to process entering the CFGBlock. + // Call into the SubEngine to process entering the CFGBlock. ExplodedNodeSet dstNodes; BlockEntrance BE(Blk, Pred->getLocationContext()); - GenericNodeBuilder<BlockEntrance> nodeBuilder(*this, Pred, BE); - SubEng.processCFGBlockEntrance(dstNodes, nodeBuilder); + NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE); + SubEng.processCFGBlockEntrance(L, nodeBuilder); - if (dstNodes.empty()) { - if (!nodeBuilder.hasGeneratedNode) { - // Auto-generate a node and enqueue it to the worklist. - generateNode(BE, Pred->State, Pred); - } - } - else { - for (ExplodedNodeSet::iterator I = dstNodes.begin(), E = dstNodes.end(); - I != E; ++I) { - WList->enqueue(*I); - } + // Auto-generate a node. + if (!nodeBuilder.hasGeneratedNodes()) { + nodeBuilder.generateNode(Pred->State, Pred); } - for (SmallVectorImpl<ExplodedNode*>::const_iterator - I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end(); - I != E; ++I) { - blocksExhausted.push_back(std::make_pair(L, *I)); - } + // Enqueue nodes onto the worklist. + enqueue(dstNodes); } void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, ExplodedNode *Pred) { // Increment the block counter. + const LocationContext *LC = Pred->getLocationContext(); + unsigned BlockId = L.getBlock()->getBlockID(); BlockCounter Counter = WList->getBlockCounter(); - Counter = BCounterFactory.IncrementCount(Counter, - Pred->getLocationContext()->getCurrentStackFrame(), - L.getBlock()->getBlockID()); + Counter = BCounterFactory.IncrementCount(Counter, LC->getCurrentStackFrame(), + BlockId); WList->setBlockCounter(Counter); // Process the entrance of the block. if (CFGElement E = L.getFirstElement()) { - StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this); - SubEng.processCFGElement(E, Builder); + NodeBuilderContext Ctx(*this, L.getBlock(), Pred); + SubEng.processCFGElement(E, Pred, 0, &Ctx); } else HandleBlockExit(L.getBlock(), Pred); @@ -345,6 +363,19 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); return; + case Stmt::CXXTryStmtClass: { + // Generate a node for each of the successors. + // Our logic for EH analysis can certainly be improved. + for (CFGBlock::const_succ_iterator it = B->succ_begin(), + et = B->succ_end(); it != et; ++it) { + if (const CFGBlock *succ = *it) { + generateNode(BlockEdge(B, succ, Pred->getLocationContext()), + Pred->State, Pred); + } + } + return; + } + case Stmt::DoStmtClass: HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); return; @@ -417,31 +448,35 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock * B, ExplodedNode *Pred) { assert(B->succ_size() == 2); - BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), - Pred, this); - SubEng.processBranch(Cond, Term, Builder); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processBranch(Cond, Term, Ctx, Pred, Dst, + *(B->succ_begin()), *(B->succ_begin()+1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); } void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred) { - assert (!B->empty()); + assert(B); + assert(!B->empty()); if (StmtIdx == B->size()) HandleBlockExit(B, Pred); else { - StmtNodeBuilder Builder(B, StmtIdx, Pred, this); - SubEng.processCFGElement((*B)[StmtIdx], Builder); + NodeBuilderContext Ctx(*this, B, Pred); + SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); } } /// generateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. void CoreEngine::generateNode(const ProgramPoint &Loc, - const ProgramState *State, + ProgramStateRef State, ExplodedNode *Pred) { bool IsNew; - ExplodedNode *Node = G->getNode(Loc, State, &IsNew); + ExplodedNode *Node = G->getNode(Loc, State, false, &IsNew); if (Pred) Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. @@ -454,225 +489,181 @@ void CoreEngine::generateNode(const ProgramPoint &Loc, if (IsNew) WList->enqueue(Node); } -ExplodedNode * -GenericNodeBuilderImpl::generateNodeImpl(const ProgramState *state, - ExplodedNode *pred, - ProgramPoint programPoint, - bool asSink) { - - hasGeneratedNode = true; - bool isNew; - ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew); - if (pred) - node->addPredecessor(pred, engine.getGraph()); - if (isNew) { - if (asSink) { - node->markAsSink(); - sinksGenerated.push_back(node); - } - return node; - } - return 0; -} - -StmtNodeBuilder::StmtNodeBuilder(const CFGBlock *b, - unsigned idx, - ExplodedNode *N, - CoreEngine* e) - : Eng(*e), B(*b), Idx(idx), Pred(N), - PurgingDeadSymbols(false), BuildSinks(false), hasGeneratedNode(false), - PointKind(ProgramPoint::PostStmtKind), Tag(0) { - Deferred.insert(N); -} - -StmtNodeBuilder::~StmtNodeBuilder() { - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) - GenerateAutoTransition(*I); -} - -void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode *N) { +void CoreEngine::enqueueStmtNode(ExplodedNode *N, + const CFGBlock *Block, unsigned Idx) { + assert(Block); assert (!N->isSink()); // Check if this node entered a callee. if (isa<CallEnter>(N->getLocation())) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. - Eng.WList->enqueue(N, &B, Idx); + WList->enqueue(N, Block, Idx); return; } // Do not create extra nodes. Move to the next CFG element. if (isa<PostInitializer>(N->getLocation())) { - Eng.WList->enqueue(N, &B, Idx+1); + WList->enqueue(N, Block, Idx+1); return; } - PostStmt Loc(getStmt(), N->getLocationContext()); + if (isa<EpsilonPoint>(N->getLocation())) { + WList->enqueue(N, Block, Idx); + return; + } + + const CFGStmt *CS = (*Block)[Idx].getAs<CFGStmt>(); + const Stmt *St = CS ? CS->getStmt() : 0; + PostStmt Loc(St, N->getLocationContext()); if (Loc == N->getLocation()) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. - Eng.WList->enqueue(N, &B, Idx+1); + WList->enqueue(N, Block, Idx+1); return; } bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(Loc, N->State, &IsNew); - Succ->addPredecessor(N, *Eng.G); + ExplodedNode *Succ = G->getNode(Loc, N->getState(), false, &IsNew); + Succ->addPredecessor(N, *G); if (IsNew) - Eng.WList->enqueue(Succ, &B, Idx+1); + WList->enqueue(Succ, Block, Idx+1); } -ExplodedNode *StmtNodeBuilder::MakeNode(ExplodedNodeSet &Dst, - const Stmt *S, - ExplodedNode *Pred, - const ProgramState *St, - ProgramPoint::Kind K) { - - ExplodedNode *N = generateNode(S, St, Pred, K); +ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) { + // Create a CallExit node and enqueue it. + const StackFrameContext *LocCtx + = cast<StackFrameContext>(N->getLocationContext()); + const Stmt *CE = LocCtx->getCallSite(); - if (N) { - if (BuildSinks) - N->markAsSink(); - else - Dst.Add(N); - } - - return N; -} + // Use the the callee location context. + CallExit Loc(CE, LocCtx); -ExplodedNode* -StmtNodeBuilder::generateNodeInternal(const Stmt *S, - const ProgramState *state, - ExplodedNode *Pred, - ProgramPoint::Kind K, - const ProgramPointTag *tag) { - - const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, - Pred->getLocationContext(), tag); - return generateNodeInternal(L, state, Pred); + bool isNew; + ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew); + Node->addPredecessor(N, *G); + return isNew ? Node : 0; } -ExplodedNode* -StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, - const ProgramState *State, - ExplodedNode *Pred) { - bool IsNew; - ExplodedNode *N = Eng.G->getNode(Loc, State, &IsNew); - N->addPredecessor(Pred, *Eng.G); - Deferred.erase(Pred); - if (IsNew) { - Deferred.insert(N); - return N; +void CoreEngine::enqueue(ExplodedNodeSet &Set) { + for (ExplodedNodeSet::iterator I = Set.begin(), + E = Set.end(); I != E; ++I) { + WList->enqueue(*I); } +} - return NULL; +void CoreEngine::enqueue(ExplodedNodeSet &Set, + const CFGBlock *Block, unsigned Idx) { + for (ExplodedNodeSet::iterator I = Set.begin(), + E = Set.end(); I != E; ++I) { + enqueueStmtNode(*I, Block, Idx); + } } -// This function generate a new ExplodedNode but not a new branch(block edge). -ExplodedNode *BranchNodeBuilder::generateNode(const Stmt *Condition, - const ProgramState *State) { - bool IsNew; - - ExplodedNode *Succ - = Eng.G->getNode(PostCondition(Condition, Pred->getLocationContext()), State, - &IsNew); - - Succ->addPredecessor(Pred, *Eng.G); - - Pred = Succ; - - if (IsNew) - return Succ; - - return NULL; +void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { + for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { + ExplodedNode *N = *I; + // If we are in an inlined call, generate CallExit node. + if (N->getLocationContext()->getParent()) { + N = generateCallExitNode(N); + if (N) + WList->enqueue(N); + } else { + G->addEndOfPath(N); + NumPathsExplored++; + } + } } -ExplodedNode *BranchNodeBuilder::generateNode(const ProgramState *State, - bool branch) { - // If the branch has been marked infeasible we should not generate a node. - if (!isFeasible(branch)) - return NULL; +void NodeBuilder::anchor() { } +ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, + ProgramStateRef State, + ExplodedNode *FromN, + bool MarkAsSink) { + HasGeneratedNodes = true; bool IsNew; + ExplodedNode *N = C.Eng.G->getNode(Loc, State, MarkAsSink, &IsNew); + N->addPredecessor(FromN, *C.Eng.G); + Frontier.erase(FromN); - ExplodedNode *Succ = - Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), - State, &IsNew); + if (!IsNew) + return 0; - Succ->addPredecessor(Pred, *Eng.G); + if (!MarkAsSink) + Frontier.Add(N); - if (branch) - GeneratedTrue = true; - else - GeneratedFalse = true; + return N; +} - if (IsNew) { - Deferred.push_back(Succ); - return Succ; - } +void NodeBuilderWithSinks::anchor() { } - return NULL; +StmtNodeBuilder::~StmtNodeBuilder() { + if (EnclosingBldr) + for (ExplodedNodeSet::iterator I = Frontier.begin(), + E = Frontier.end(); I != E; ++I ) + EnclosingBldr->addNodes(*I); } -BranchNodeBuilder::~BranchNodeBuilder() { - if (!GeneratedTrue) generateNode(Pred->State, true); - if (!GeneratedFalse) generateNode(Pred->State, false); +void BranchNodeBuilder::anchor() { } - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) Eng.WList->enqueue(*I); -} +ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State, + bool branch, + ExplodedNode *NodePred) { + // If the branch has been marked infeasible we should not generate a node. + if (!isFeasible(branch)) + return NULL; + ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT:DstF, + NodePred->getLocationContext()); + ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred); + return Succ; +} ExplodedNode* IndirectGotoNodeBuilder::generateNode(const iterator &I, - const ProgramState *St, - bool isSink) { + ProgramStateRef St, + bool IsSink) { bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); - + Pred->getLocationContext()), St, + IsSink, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { + if (!IsNew) + return 0; - if (isSink) - Succ->markAsSink(); - else - Eng.WList->enqueue(Succ); - - return Succ; - } + if (!IsSink) + Eng.WList->enqueue(Succ); - return NULL; + return Succ; } ExplodedNode* SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, - const ProgramState *St) { + ProgramStateRef St) { bool IsNew; ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), - St, &IsNew); + Pred->getLocationContext()), St, + false, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { - Eng.WList->enqueue(Succ); - return Succ; - } - return NULL; + if (!IsNew) + return 0; + + Eng.WList->enqueue(Succ); + return Succ; } ExplodedNode* -SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St, - bool isSink) { +SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, + bool IsSink) { // Get the block for the default case. assert(Src->succ_rbegin() != Src->succ_rend()); CFGBlock *DefaultBlock = *Src->succ_rbegin(); @@ -683,145 +674,16 @@ SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St, return NULL; bool IsNew; - ExplodedNode *Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, &IsNew); + Pred->getLocationContext()), St, + IsSink, &IsNew); Succ->addPredecessor(Pred, *Eng.G); - if (IsNew) { - if (isSink) - Succ->markAsSink(); - else - Eng.WList->enqueue(Succ); - - return Succ; - } - - return NULL; -} - -EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() { - // Auto-generate an EOP node if one has not been generated. - if (!hasGeneratedNode) { - // If we are in an inlined call, generate CallExit node. - if (Pred->getLocationContext()->getParent()) - GenerateCallExitNode(Pred->State); - else - generateNode(Pred->State); - } -} - -ExplodedNode* -EndOfFunctionNodeBuilder::generateNode(const ProgramState *State, - ExplodedNode *P, - const ProgramPointTag *tag) { - hasGeneratedNode = true; - bool IsNew; - - ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B, - Pred->getLocationContext(), tag ? tag : Tag), - State, &IsNew); - - Node->addPredecessor(P ? P : Pred, *Eng.G); - - if (IsNew) { - Eng.G->addEndOfPath(Node); - return Node; - } - - return NULL; -} - -void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) { - hasGeneratedNode = true; - // Create a CallExit node and enqueue it. - const StackFrameContext *LocCtx - = cast<StackFrameContext>(Pred->getLocationContext()); - const Stmt *CE = LocCtx->getCallSite(); + if (!IsNew) + return 0; - // Use the the callee location context. - CallExit Loc(CE, LocCtx); - - bool isNew; - ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); - Node->addPredecessor(Pred, *Eng.G); - - if (isNew) - Eng.WList->enqueue(Node); -} - - -void CallEnterNodeBuilder::generateNode(const ProgramState *state) { - // Check if the callee is in the same translation unit. - if (CalleeCtx->getTranslationUnit() != - Pred->getLocationContext()->getTranslationUnit()) { - // Create a new engine. We must be careful that the new engine should not - // reference data structures owned by the old engine. - - AnalysisManager &OldMgr = Eng.SubEng.getAnalysisManager(); - - // Get the callee's translation unit. - idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit(); - - // Create a new AnalysisManager with components of the callee's - // TranslationUnit. - // The Diagnostic is actually shared when we create ASTUnits from AST files. - AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), OldMgr); - - // Create the new engine. - // FIXME: This cast isn't really safe. - bool GCEnabled = static_cast<ExprEngine&>(Eng.SubEng).isObjCGCEnabled(); - ExprEngine NewEng(AMgr, GCEnabled); - - // Create the new LocationContext. - AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), - CalleeCtx->getTranslationUnit()); - const StackFrameContext *OldLocCtx = CalleeCtx; - const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx, - OldLocCtx->getParent(), - OldLocCtx->getCallSite(), - OldLocCtx->getCallSiteBlock(), - OldLocCtx->getIndex()); - - // Now create an initial state for the new engine. - const ProgramState *NewState = - NewEng.getStateManager().MarshalState(state, NewLocCtx); - ExplodedNodeSet ReturnNodes; - NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), - NewState, ReturnNodes); - return; - } - - // Get the callee entry block. - const CFGBlock *Entry = &(CalleeCtx->getCFG()->getEntry()); - assert(Entry->empty()); - assert(Entry->succ_size() == 1); - - // Get the solitary successor. - const CFGBlock *SuccB = *(Entry->succ_begin()); - - // Construct an edge representing the starting location in the callee. - BlockEdge Loc(Entry, SuccB, CalleeCtx); - - bool isNew; - ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); - Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); - - if (isNew) - Eng.WList->enqueue(Node); -} + if (!IsSink) + Eng.WList->enqueue(Succ); -void CallExitNodeBuilder::generateNode(const ProgramState *state) { - // Get the callee's location context. - const StackFrameContext *LocCtx - = cast<StackFrameContext>(Pred->getLocationContext()); - // When exiting an implicit automatic obj dtor call, the callsite is the Stmt - // that triggers the dtor. - PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent()); - bool isNew; - ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); - Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); - if (isNew) - Eng.WList->enqueue(Node, LocCtx->getCallSiteBlock(), - LocCtx->getIndex() + 1); + return Succ; } diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index e1b982c..b5ea3db 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" @@ -19,7 +20,7 @@ using namespace clang; using namespace ento; -SVal Environment::lookupExpr(const Stmt *E) const { +SVal Environment::lookupExpr(const EnvironmentEntry &E) const { const SVal* X = ExprBindings.lookup(E); if (X) { SVal V = *X; @@ -28,17 +29,21 @@ SVal Environment::lookupExpr(const Stmt *E) const { return UnknownVal(); } -SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, - bool useOnlyDirectBindings) const { +SVal Environment::getSVal(const EnvironmentEntry &Entry, + SValBuilder& svalBuilder, + bool useOnlyDirectBindings) const { if (useOnlyDirectBindings) { // This branch is rarely taken, but can be exercised by // checkers that explicitly bind values to arbitrary // expressions. It is crucial that we do not ignore any // expression here, and do a direct lookup. - return lookupExpr(E); + return lookupExpr(Entry); } + const Stmt *E = Entry.getStmt(); + const LocationContext *LCtx = Entry.getLocationContext(); + for (;;) { if (const Expr *Ex = dyn_cast<Expr>(E)) E = Ex->IgnoreParens(); @@ -55,13 +60,12 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, case Stmt::GenericSelectionExprClass: llvm_unreachable("ParenExprs and GenericSelectionExprs should " "have been handled by IgnoreParens()"); - return UnknownVal(); case Stmt::CharacterLiteralClass: { const CharacterLiteral* C = cast<CharacterLiteral>(E); return svalBuilder.makeIntVal(C->getValue(), C->getType()); } case Stmt::CXXBoolLiteralExprClass: { - const SVal *X = ExprBindings.lookup(E); + const SVal *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx)); if (X) return *X; else @@ -69,12 +73,15 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, } case Stmt::IntegerLiteralClass: { // In C++, this expression may have been bound to a temporary object. - SVal const *X = ExprBindings.lookup(E); + SVal const *X = ExprBindings.lookup(EnvironmentEntry(E, LCtx)); if (X) return *X; else return svalBuilder.makeIntVal(cast<IntegerLiteral>(E)); } + case Stmt::ObjCBoolLiteralExprClass: + return svalBuilder.makeBoolVal(cast<ObjCBoolLiteralExpr>(E)); + // For special C0xx nullptr case, make a null pointer SVal. case Stmt::CXXNullPtrLiteralExprClass: return svalBuilder.makeNull(); @@ -86,6 +93,24 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, continue; case Stmt::ObjCPropertyRefExprClass: return loc::ObjCPropRef(cast<ObjCPropertyRefExpr>(E)); + case Stmt::ObjCStringLiteralClass: { + MemRegionManager &MRMgr = svalBuilder.getRegionManager(); + const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E); + return svalBuilder.makeLoc(MRMgr.getObjCStringRegion(SL)); + } + case Stmt::StringLiteralClass: { + MemRegionManager &MRMgr = svalBuilder.getRegionManager(); + const StringLiteral *SL = cast<StringLiteral>(E); + return svalBuilder.makeLoc(MRMgr.getStringRegion(SL)); + } + case Stmt::ReturnStmtClass: { + const ReturnStmt *RS = cast<ReturnStmt>(E); + if (const Expr *RE = RS->getRetValue()) { + E = RE; + continue; + } + return UndefinedVal(); + } // Handle all other Stmt* using a lookup. default: @@ -93,32 +118,33 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, }; break; } - return lookupExpr(E); + return lookupExpr(EnvironmentEntry(E, LCtx)); } -Environment EnvironmentManager::bindExpr(Environment Env, const Stmt *S, - SVal V, bool Invalidate) { - assert(S); - +Environment EnvironmentManager::bindExpr(Environment Env, + const EnvironmentEntry &E, + SVal V, + bool Invalidate) { if (V.isUnknown()) { if (Invalidate) - return Environment(F.remove(Env.ExprBindings, S)); + return Environment(F.remove(Env.ExprBindings, E)); else return Env; } - - return Environment(F.add(Env.ExprBindings, S, V)); + return Environment(F.add(Env.ExprBindings, E, V)); } -static inline const Stmt *MakeLocation(const Stmt *S) { - return (const Stmt*) (((uintptr_t) S) | 0x1); +static inline EnvironmentEntry MakeLocation(const EnvironmentEntry &E) { + const Stmt *S = E.getStmt(); + S = (const Stmt*) (((uintptr_t) S) | 0x1); + return EnvironmentEntry(S, E.getLocationContext()); } Environment EnvironmentManager::bindExprAndLocation(Environment Env, - const Stmt *S, + const EnvironmentEntry &E, SVal location, SVal V) { - return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(S), location), - S, V)); + return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(E), location), + E, V)); } namespace { @@ -126,14 +152,22 @@ class MarkLiveCallback : public SymbolVisitor { SymbolReaper &SymReaper; public: MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} - bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; } + bool VisitSymbol(SymbolRef sym) { + SymReaper.markLive(sym); + return true; + } + bool VisitMemRegion(const MemRegion *R) { + SymReaper.markLive(R); + return true; + } }; } // end anonymous namespace -// In addition to mapping from Stmt * - > SVals in the Environment, we also -// maintain a mapping from Stmt * -> SVals (locations) that were used during -// a load and store. -static inline bool IsLocation(const Stmt *S) { +// In addition to mapping from EnvironmentEntry - > SVals in the Environment, +// we also maintain a mapping from EnvironmentEntry -> SVals (locations) +// that were used during a load and store. +static inline bool IsLocation(const EnvironmentEntry &E) { + const Stmt *S = E.getStmt(); return (bool) (((uintptr_t) S) & 0x1); } @@ -147,19 +181,19 @@ static inline bool IsLocation(const Stmt *S) { Environment EnvironmentManager::removeDeadBindings(Environment Env, SymbolReaper &SymReaper, - const ProgramState *ST) { + ProgramStateRef ST) { // We construct a new Environment object entirely, as this is cheaper than // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). Environment NewEnv = getInitialEnvironment(); - SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations; + SmallVector<std::pair<EnvironmentEntry, SVal>, 10> deferredLocations; MarkLiveCallback CB(SymReaper); ScanReachableSymbols RSScaner(ST, CB); - llvm::ImmutableMapRef<const Stmt*,SVal> + llvm::ImmutableMapRef<EnvironmentEntry,SVal> EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), F.getTreeFactory()); @@ -167,7 +201,7 @@ EnvironmentManager::removeDeadBindings(Environment Env, for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - const Stmt *BlkExpr = I.getKey(); + const EnvironmentEntry &BlkExpr = I.getKey(); // For recorded locations (used when evaluating loads and stores), we // consider them live only when their associated normal expression is // also live. @@ -179,7 +213,7 @@ EnvironmentManager::removeDeadBindings(Environment Env, } const SVal &X = I.getData(); - if (SymReaper.isLive(BlkExpr)) { + if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { // Copy the binding to the new map. EBMapRef = EBMapRef.add(BlkExpr, X); @@ -204,13 +238,58 @@ EnvironmentManager::removeDeadBindings(Environment Env, // Go through he deferred locations and add them to the new environment if // the correspond Stmt* is in the map as well. - for (SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator + for (SmallVectorImpl<std::pair<EnvironmentEntry, SVal> >::iterator I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { - const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1); - if (EBMapRef.lookup(S)) - EBMapRef = EBMapRef.add(I->first, I->second); + const EnvironmentEntry &En = I->first; + const Stmt *S = (Stmt*) (((uintptr_t) En.getStmt()) & (uintptr_t) ~0x1); + if (EBMapRef.lookup(EnvironmentEntry(S, En.getLocationContext()))) + EBMapRef = EBMapRef.add(En, I->second); } NewEnv.ExprBindings = EBMapRef.asImmutableMap(); return NewEnv; } + +void Environment::print(raw_ostream &Out, const char *NL, + const char *Sep) const { + printAux(Out, false, NL, Sep); + printAux(Out, true, NL, Sep); +} + +void Environment::printAux(raw_ostream &Out, bool printLocations, + const char *NL, + const char *Sep) const{ + + bool isFirst = true; + + for (Environment::iterator I = begin(), E = end(); I != E; ++I) { + const EnvironmentEntry &En = I.getKey(); + if (IsLocation(En)) { + if (!printLocations) + continue; + } + else { + if (printLocations) + continue; + } + + if (isFirst) { + Out << NL << NL + << (printLocations ? "Load/Store locations:" : "Expressions:") + << NL; + isFirst = false; + } else { + Out << NL; + } + + const Stmt *S = En.getStmt(); + if (printLocations) { + S = (Stmt*) (((uintptr_t) S) & ((uintptr_t) ~0x1)); + } + + Out << " (" << (void*) En.getLocationContext() << ',' << (void*) S << ") "; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } +} diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 5762a21..0dcbe1f 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/Stmt.h" +#include "clang/AST/ParentMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -44,25 +45,18 @@ void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { // Cleanup. //===----------------------------------------------------------------------===// -typedef std::vector<ExplodedNode*> NodeList; -static inline NodeList*& getNodeList(void *&p) { return (NodeList*&) p; } +static const unsigned CounterTop = 1000; -ExplodedGraph::~ExplodedGraph() { - if (reclaimNodes) { - delete getNodeList(recentlyAllocatedNodes); - delete getNodeList(freeNodes); - } -} +ExplodedGraph::ExplodedGraph() + : NumNodes(0), reclaimNodes(false), reclaimCounter(CounterTop) {} + +ExplodedGraph::~ExplodedGraph() {} //===----------------------------------------------------------------------===// // Node reclamation. //===----------------------------------------------------------------------===// -void ExplodedGraph::reclaimRecentlyAllocatedNodes() { - if (!recentlyAllocatedNodes) - return; - NodeList &nl = *getNodeList(recentlyAllocatedNodes); - +bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Reclaimn all nodes that match *all* the following criteria: // // (1) 1 predecessor (that has one successor) @@ -72,61 +66,86 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() { // (5) The 'store' is the same as the predecessor. // (6) The 'GDM' is the same as the predecessor. // (7) The LocationContext is the same as the predecessor. - // (8) The PostStmt is for a non-CFGElement expression. - - for (NodeList::iterator i = nl.begin(), e = nl.end() ; i != e; ++i) { - ExplodedNode *node = *i; - - // Conditions 1 and 2. - if (node->pred_size() != 1 || node->succ_size() != 1) - continue; + // (8) The PostStmt is for a non-consumed Stmt or Expr. - ExplodedNode *pred = *(node->pred_begin()); - if (pred->succ_size() != 1) - continue; + // Conditions 1 and 2. + if (node->pred_size() != 1 || node->succ_size() != 1) + return false; - ExplodedNode *succ = *(node->succ_begin()); - if (succ->pred_size() != 1) - continue; + const ExplodedNode *pred = *(node->pred_begin()); + if (pred->succ_size() != 1) + return false; + + const ExplodedNode *succ = *(node->succ_begin()); + if (succ->pred_size() != 1) + return false; + + // Condition 3. + ProgramPoint progPoint = node->getLocation(); + if (!isa<PostStmt>(progPoint) || + (isa<CallEnter>(progPoint) || isa<CallExit>(progPoint))) + return false; + + // Condition 4. + PostStmt ps = cast<PostStmt>(progPoint); + if (ps.getTag()) + return false; + + if (isa<BinaryOperator>(ps.getStmt())) + return false; + + // Conditions 5, 6, and 7. + ProgramStateRef state = node->getState(); + ProgramStateRef pred_state = pred->getState(); + if (state->store != pred_state->store || state->GDM != pred_state->GDM || + progPoint.getLocationContext() != pred->getLocationContext()) + return false; + + // Condition 8. + if (const Expr *Ex = dyn_cast<Expr>(ps.getStmt())) { + ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(Ex)) + return false; + } + + return true; +} - // Condition 3. - ProgramPoint progPoint = node->getLocation(); - if (!isa<PostStmt>(progPoint)) - continue; - // Condition 4. - PostStmt ps = cast<PostStmt>(progPoint); - if (ps.getTag()) - continue; +void ExplodedGraph::collectNode(ExplodedNode *node) { + // Removing a node means: + // (a) changing the predecessors successor to the successor of this node + // (b) changing the successors predecessor to the predecessor of this node + // (c) Putting 'node' onto freeNodes. + assert(node->pred_size() == 1 || node->succ_size() == 1); + ExplodedNode *pred = *(node->pred_begin()); + ExplodedNode *succ = *(node->succ_begin()); + pred->replaceSuccessor(succ); + succ->replacePredecessor(pred); + FreeNodes.push_back(node); + Nodes.RemoveNode(node); + --NumNodes; + node->~ExplodedNode(); +} - if (isa<BinaryOperator>(ps.getStmt())) - continue; +void ExplodedGraph::reclaimRecentlyAllocatedNodes() { + if (ChangedNodes.empty()) + return; - // Conditions 5, 6, and 7. - const ProgramState *state = node->getState(); - const ProgramState *pred_state = pred->getState(); - if (state->store != pred_state->store || state->GDM != pred_state->GDM || - progPoint.getLocationContext() != pred->getLocationContext()) - continue; + // Only periodically relcaim nodes so that we can build up a set of + // nodes that meet the reclamation criteria. Freshly created nodes + // by definition have no successor, and thus cannot be reclaimed (see below). + assert(reclaimCounter > 0); + if (--reclaimCounter != 0) + return; + reclaimCounter = CounterTop; - // Condition 8. - if (node->getCFG().isBlkExpr(ps.getStmt())) - continue; - - // If we reach here, we can remove the node. This means: - // (a) changing the predecessors successor to the successor of this node - // (b) changing the successors predecessor to the predecessor of this node - // (c) Putting 'node' onto freeNodes. - pred->replaceSuccessor(succ); - succ->replacePredecessor(pred); - if (!freeNodes) - freeNodes = new NodeList(); - getNodeList(freeNodes)->push_back(node); - Nodes.RemoveNode(node); - --NumNodes; - node->~ExplodedNode(); + for (NodeVector::iterator it = ChangedNodes.begin(), et = ChangedNodes.end(); + it != et; ++it) { + ExplodedNode *node = *it; + if (shouldCollect(node)) + collectNode(node); } - - nl.clear(); + ChangedNodes.clear(); } //===----------------------------------------------------------------------===// @@ -215,36 +234,33 @@ ExplodedNode** ExplodedNode::NodeGroup::end() const { } ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, - const ProgramState *State, bool* IsNew) { + ProgramStateRef State, + bool IsSink, + bool* IsNew) { // Profile 'State' to determine if we already have an existing node. llvm::FoldingSetNodeID profile; void *InsertPos = 0; - NodeTy::Profile(profile, L, State); + NodeTy::Profile(profile, L, State, IsSink); NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); if (!V) { - if (freeNodes && !getNodeList(freeNodes)->empty()) { - NodeList *nl = getNodeList(freeNodes); - V = nl->back(); - nl->pop_back(); + if (!FreeNodes.empty()) { + V = FreeNodes.back(); + FreeNodes.pop_back(); } else { // Allocate a new node. V = (NodeTy*) getAllocator().Allocate<NodeTy>(); } - new (V) NodeTy(L, State); + new (V) NodeTy(L, State, IsSink); - if (reclaimNodes) { - if (!recentlyAllocatedNodes) - recentlyAllocatedNodes = new NodeList(); - getNodeList(recentlyAllocatedNodes)->push_back(V); - } + if (reclaimNodes) + ChangedNodes.push_back(V); // Insert the node into the node set and return it. Nodes.InsertNode(V, InsertPos); - ++NumNodes; if (IsNew) *IsNew = true; @@ -265,7 +281,7 @@ ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, assert (NBeg < NEnd); - llvm::OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); + OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); @@ -334,7 +350,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. - ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, NULL); + ExplodedNode *NewN = G->getNode(N->getLocation(), N->State, N->isSink(), 0); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. @@ -372,15 +388,13 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, if (Pass1.count(*I)) WL2.push_back(*I); } - - // Finally, explicitly mark all nodes without any successors as sinks. - if (N->isSink()) - NewN->markAsSink(); } return G; } +void InterExplodedGraphMap::anchor() { } + ExplodedNode* InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const { llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index ac9cf0b..d2da9aa 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -13,22 +13,24 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "ExprEngine" + #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngineBuilders.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" +#include "clang/AST/StmtCXX.h" #include "clang/AST/DeclCXX.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/Statistic.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -38,6 +40,19 @@ using namespace clang; using namespace ento; using llvm::APSInt; +STATISTIC(NumRemoveDeadBindings, + "The # of times RemoveDeadBindings is called"); +STATISTIC(NumRemoveDeadBindingsSkipped, + "The # of times RemoveDeadBindings is skipped"); +STATISTIC(NumMaxBlockCountReached, + "The # of aborted paths due to reaching the maximum block count in " + "a top level function"); +STATISTIC(NumMaxBlockCountReachedInInlined, + "The # of aborted paths due to reaching the maximum block count in " + "an inlined function"); +STATISTIC(NumTimesRetriedWithoutInlining, + "The # of times we re-evaluated a call without inlining"); + //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// @@ -51,17 +66,20 @@ static inline Selector GetNullarySelector(const char* name, ASTContext &Ctx) { // Engine construction and deletion. //===----------------------------------------------------------------------===// -ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled) +ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, + SetOfConstDecls *VisitedCallees, + FunctionSummariesTy *FS) : AMgr(mgr), - Engine(*this), + AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), + Engine(*this, VisitedCallees, FS), G(Engine.getGraph()), - Builder(NULL), StateMgr(getContext(), mgr.getStoreManagerCreator(), mgr.getConstraintManagerCreator(), G.getAllocator(), *this), SymMgr(StateMgr.getSymbolManager()), svalBuilder(StateMgr.getSValBuilder()), - EntryNode(NULL), currentStmt(NULL), + EntryNode(NULL), + currentStmt(NULL), currentStmtIdx(0), currentBuilderContext(0), NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), RaiseSel(GetNullarySelector("raise", getContext())), ObjCGCEnabled(gcEnabled), BR(mgr, *this) { @@ -81,15 +99,15 @@ ExprEngine::~ExprEngine() { // Utility methods. //===----------------------------------------------------------------------===// -const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) { - const ProgramState *state = StateMgr.getInitialState(InitLoc); +ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { + ProgramStateRef state = StateMgr.getInitialState(InitLoc); + const Decl *D = InitLoc->getDecl(); // Preconditions. - // FIXME: It would be nice if we had a more general mechanism to add // such preconditions. Some day. do { - const Decl *D = InitLoc->getDecl(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { // Precondition: the first argument of 'main' is an integer guaranteed // to be > 0. @@ -117,49 +135,45 @@ const ProgramState *ExprEngine::getInitialState(const LocationContext *InitLoc) if (!Constraint) break; - if (const ProgramState *newState = state->assume(*Constraint, true)) + if (ProgramStateRef newState = state->assume(*Constraint, true)) state = newState; - - break; } - - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { - // Precondition: 'self' is always non-null upon entry to an Objective-C - // method. - const ImplicitParamDecl *SelfD = MD->getSelfDecl(); - const MemRegion *R = state->getRegion(SelfD, InitLoc); - SVal V = state->getSVal(loc::MemRegionVal(R)); - - if (const Loc *LV = dyn_cast<Loc>(&V)) { - // Assume that the pointer value in 'self' is non-null. - state = state->assume(*LV, true); - assert(state && "'self' cannot be null"); - } + break; + } + while (0); + + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + // Precondition: 'self' is always non-null upon entry to an Objective-C + // method. + const ImplicitParamDecl *SelfD = MD->getSelfDecl(); + const MemRegion *R = state->getRegion(SelfD, InitLoc); + SVal V = state->getSVal(loc::MemRegionVal(R)); + + if (const Loc *LV = dyn_cast<Loc>(&V)) { + // Assume that the pointer value in 'self' is non-null. + state = state->assume(*LV, true); + assert(state && "'self' cannot be null"); } - } while (0); - - return state; -} + } -bool -ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const -{ - if (callOrMessage.isFunctionCall() && !callOrMessage.isCXXCall()) { - SVal calleeV = callOrMessage.getFunctionCallee(); - if (const FunctionTextRegion *codeR = - dyn_cast_or_null<FunctionTextRegion>(calleeV.getAsRegion())) { - - const FunctionDecl *fd = codeR->getDecl(); - if (const IdentifierInfo *ii = fd->getIdentifier()) { - StringRef fname = ii->getName(); - if (fname == "strlen") - return false; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (!MD->isStatic()) { + // Precondition: 'this' is always non-null upon entry to the + // top-level function. This is our starting assumption for + // analyzing an "open" program. + const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); + if (SFC->getParent() == 0) { + loc::MemRegionVal L(getCXXThisRegion(MD, SFC)); + SVal V = state->getSVal(L); + if (const Loc *LV = dyn_cast<Loc>(&V)) { + state = state->assume(*LV, true); + assert(state && "'this' cannot be null"); + } } } } - - // The conservative answer: invalidates globals. - return true; + + return state; } //===----------------------------------------------------------------------===// @@ -168,25 +182,26 @@ ExprEngine::doesInvalidateGlobals(const CallOrObjCMessage &callOrMessage) const /// evalAssume - Called by ConstraintManager. Used to call checker-specific /// logic for handling assumptions on symbolic values. -const ProgramState *ExprEngine::processAssume(const ProgramState *state, +ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, SVal cond, bool assumption) { return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); } -bool ExprEngine::wantsRegionChangeUpdate(const ProgramState *state) { +bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { return getCheckerManager().wantsRegionChangeUpdate(state); } -const ProgramState * -ExprEngine::processRegionChanges(const ProgramState *state, +ProgramStateRef +ExprEngine::processRegionChanges(ProgramStateRef state, const StoreManager::InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, - ArrayRef<const MemRegion *> Regions) { + ArrayRef<const MemRegion *> Regions, + const CallOrObjCMessage *Call) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, - Explicits, Regions); + Explicits, Regions, Call); } -void ExprEngine::printState(raw_ostream &Out, const ProgramState *State, +void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) { getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } @@ -195,54 +210,77 @@ void ExprEngine::processEndWorklist(bool hasWorkRemaining) { getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); } -void ExprEngine::processCFGElement(const CFGElement E, - StmtNodeBuilder& builder) { +void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx) { + currentStmtIdx = StmtIdx; + currentBuilderContext = Ctx; + switch (E.getKind()) { case CFGElement::Invalid: llvm_unreachable("Unexpected CFGElement kind."); case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), builder); + ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), builder); + ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), Pred); return; case CFGElement::AutomaticObjectDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: - ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), builder); + ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); return; } } -void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { - // TODO: Use RAII to remove the unnecessary, tagged nodes. - //RegisterCreatedNodes registerCreatedNodes(getGraph()); +static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, + const CFGStmt S, + const ExplodedNode *Pred, + const LocationContext *LC) { + + // Are we never purging state values? + if (AMgr.getPurgeMode() == PurgeNone) + return false; + + // Is this the beginning of a basic block? + if (isa<BlockEntrance>(Pred->getLocation())) + return true; + // Is this on a non-expression? + if (!isa<Expr>(S.getStmt())) + return true; + + // Run before processing a call. + if (isa<CallExpr>(S.getStmt())) + return true; + + // Is this an expression that is consumed by another expression? If so, + // postpone cleaning out the state. + ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); + return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); +} + +void ExprEngine::ProcessStmt(const CFGStmt S, + ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - // Recycle any unused states in the ProgramStateManager. - StateMgr.recycleUnusedStates(); currentStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currentStmt->getLocStart(), "Error evaluating statement"); - // A tag to track convenience transitions, which can be removed at cleanup. - static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); - Builder = &builder; - EntryNode = builder.getPredecessor(); + EntryNode = Pred; - const ProgramState *EntryState = EntryNode->getState(); + ProgramStateRef EntryState = EntryNode->getState(); CleanedState = EntryState; - ExplodedNode *CleanedNode = 0; // Create the cleaned state. const LocationContext *LC = EntryNode->getLocationContext(); SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager()); - if (AMgr.getPurgeMode() != PurgeNone) { + if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) { + NumRemoveDeadBindings++; getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); const StackFrameContext *SFC = LC->getCurrentStackFrame(); @@ -251,25 +289,23 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { // and the store. TODO: The function should just return new env and store, // not a new state. CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); + } else { + NumRemoveDeadBindingsSkipped++; } // Process any special transfer function for dead symbols. ExplodedNodeSet Tmp; + // A tag to track convenience transitions, which can be removed at cleanup. + static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node"); + if (!SymReaper.hasDeadSymbols()) { // Generate a CleanedNode that has the environment and store cleaned // up. Since no symbols are dead, we can optimize and not clean out // the constraint manager. - CleanedNode = - Builder->generateNode(currentStmt, CleanedState, EntryNode, &cleanupTag); - Tmp.Add(CleanedNode); + StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); + Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag); } else { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); - - SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); - Builder->PurgingDeadSymbols = true; - // Call checkers with the non-cleaned state so that they could query the // values of the soon to be dead symbols. ExplodedNodeSet CheckedSet; @@ -279,9 +315,10 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { // For each node in CheckedSet, generate CleanedNodes that have the // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. + StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext); for (ExplodedNodeSet::const_iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { - const ProgramState *CheckerState = (*I)->getState(); + ProgramStateRef CheckerState = (*I)->getState(); // The constraint manager has not been cleaned up yet, so clean up now. CheckerState = getConstraintManager().removeDeadBindings(CheckerState, @@ -296,109 +333,109 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { // Create a state based on CleanedState with CheckerState GDM and // generate a transition to that state. - const ProgramState *CleanedCheckerSt = + ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - ExplodedNode *CleanedNode = Builder->generateNode(currentStmt, - CleanedCheckerSt, *I, - &cleanupTag); - Tmp.Add(CleanedNode); + Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag, + ProgramPoint::PostPurgeDeadSymbolsKind); } } + ExplodedNodeSet Dst; for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // TODO: Remove Dest set, it's no longer needed. - ExplodedNodeSet Dst; + ExplodedNodeSet DstI; // Visit the statement. - Visit(currentStmt, *I, Dst); + Visit(currentStmt, *I, DstI); + Dst.insert(DstI); } + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); + // NULL out these variables to cleanup. CleanedState = NULL; EntryNode = NULL; currentStmt = 0; - Builder = NULL; } void ExprEngine::ProcessInitializer(const CFGInitializer Init, - StmtNodeBuilder &builder) { + ExplodedNode *Pred) { + ExplodedNodeSet Dst; + // We don't set EntryNode and currentStmt. And we don't clean up state. const CXXCtorInitializer *BMI = Init.getInitializer(); - - ExplodedNode *pred = builder.getPredecessor(); - - const StackFrameContext *stackFrame = cast<StackFrameContext>(pred->getLocationContext()); - const CXXConstructorDecl *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); + const StackFrameContext *stackFrame = + cast<StackFrameContext>(Pred->getLocationContext()); + const CXXConstructorDecl *decl = + cast<CXXConstructorDecl>(stackFrame->getDecl()); const CXXThisRegion *thisReg = getCXXThisRegion(decl, stackFrame); - SVal thisVal = pred->getState()->getSVal(thisReg); + SVal thisVal = Pred->getState()->getSVal(thisReg); if (BMI->isAnyMemberInitializer()) { - ExplodedNodeSet Dst; - // Evaluate the initializer. - Visit(BMI->getInit(), pred, Dst); - for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){ - ExplodedNode *Pred = *I; - const ProgramState *state = Pred->getState(); + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef state = Pred->getState(); - const FieldDecl *FD = BMI->getAnyMember(); + const FieldDecl *FD = BMI->getAnyMember(); - SVal FieldLoc = state->getLValue(FD, thisVal); - SVal InitVal = state->getSVal(BMI->getInit()); - state = state->bindLoc(FieldLoc, InitVal); + SVal FieldLoc = state->getLValue(FD, thisVal); + SVal InitVal = state->getSVal(BMI->getInit(), Pred->getLocationContext()); + state = state->bindLoc(FieldLoc, InitVal); - // Use a custom node building process. - PostInitializer PP(BMI, stackFrame); - // Builder automatically add the generated node to the deferred set, - // which are processed in the builder's dtor. - builder.generateNode(PP, state, Pred); - } - return; - } + // Use a custom node building process. + PostInitializer PP(BMI, stackFrame); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + Bldr.generateNode(PP, Pred, state); + } else { + assert(BMI->isBaseInitializer()); - assert(BMI->isBaseInitializer()); + // Get the base class declaration. + const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); - // Get the base class declaration. - const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit()); + // Create the base object region. + SVal baseVal = + getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); + const MemRegion *baseReg = baseVal.getAsRegion(); + assert(baseReg); + + VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst); + } - // Create the base object region. - SVal baseVal = - getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType()); - const MemRegion *baseReg = baseVal.getAsRegion(); - assert(baseReg); - Builder = &builder; - ExplodedNodeSet dst; - VisitCXXConstructExpr(ctorExpr, baseReg, pred, dst); + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, - StmtNodeBuilder &builder) { - Builder = &builder; - + ExplodedNode *Pred) { + ExplodedNodeSet Dst; switch (D.getKind()) { case CFGElement::AutomaticObjectDtor: - ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), builder); + ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), Pred, Dst); break; case CFGElement::BaseDtor: - ProcessBaseDtor(cast<CFGBaseDtor>(D), builder); + ProcessBaseDtor(cast<CFGBaseDtor>(D), Pred, Dst); break; case CFGElement::MemberDtor: - ProcessMemberDtor(cast<CFGMemberDtor>(D), builder); + ProcessMemberDtor(cast<CFGMemberDtor>(D), Pred, Dst); break; case CFGElement::TemporaryDtor: - ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), builder); + ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), Pred, Dst); break; default: llvm_unreachable("Unexpected dtor kind."); } + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currentBuilderContext->getBlock(), currentStmtIdx); } -void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, - StmtNodeBuilder &builder) { - ExplodedNode *pred = builder.getPredecessor(); - const ProgramState *state = pred->getState(); - const VarDecl *varDecl = dtor.getVarDecl(); +void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ProgramStateRef state = Pred->getState(); + const VarDecl *varDecl = Dtor.getVarDecl(); QualType varType = varDecl->getType(); @@ -409,30 +446,29 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, assert(recordDecl && "get CXXRecordDecl fail"); const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor(); - Loc dest = state->getLValue(varDecl, pred->getLocationContext()); + Loc dest = state->getLValue(varDecl, Pred->getLocationContext()); - ExplodedNodeSet dstSet; VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(), - dtor.getTriggerStmt(), pred, dstSet); + Dtor.getTriggerStmt(), Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, - StmtNodeBuilder &builder) { -} + ExplodedNode *Pred, ExplodedNodeSet &Dst) {} void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, - StmtNodeBuilder &builder) { -} + ExplodedNode *Pred, ExplodedNodeSet &Dst) {} void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, - StmtNodeBuilder &builder) { -} + ExplodedNode *Pred, + ExplodedNodeSet &Dst) {} void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &DstTop) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating statement"); + ExplodedNodeSet Dst; + StmtNodeBuilder Bldr(Pred, DstTop, *currentBuilderContext); // Expressions to ignore. if (const Expr *Ex = dyn_cast<Expr>(S)) @@ -442,19 +478,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // this check when we KNOW that there is no block-level subexpression. // The motivation is that this check requires a hashtable lookup. - if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { - Dst.Add(Pred); + if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) return; - } switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: - case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXCatchStmtClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXPseudoDestructorExprClass: - case Stmt::CXXThrowExprClass: case Stmt::CXXTryStmtClass: case Stmt::CXXTypeidExprClass: case Stmt::CXXUuidofExprClass: @@ -463,6 +494,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::DependentScopeDeclRefExprClass: case Stmt::UnaryTypeTraitExprClass: case Stmt::BinaryTypeTraitExprClass: + case Stmt::TypeTraitExprClass: case Stmt::ArrayTypeTraitExprClass: case Stmt::ExpressionTraitExprClass: case Stmt::UnresolvedLookupExprClass: @@ -472,22 +504,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: - case Stmt::SEHFinallyStmtClass: - { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - const ExplodedNode *node = MakeNode(Dst, S, Pred, Pred->getState()); - Engine.addAbortedBlock(node, Builder->getBlock()); + case Stmt::LambdaExprClass: + case Stmt::SEHFinallyStmtClass: { + const ExplodedNode *node = Bldr.generateNode(S, Pred, Pred->getState(), + /* sink */ true); + Engine.addAbortedBlock(node, currentBuilderContext->getBlock()); break; } // We don't handle default arguments either yet, but we can fake it // for now by just skipping them. case Stmt::SubstNonTypeTemplateParmExprClass: - case Stmt::CXXDefaultArgExprClass: { - Dst.Add(Pred); + case Stmt::CXXDefaultArgExprClass: break; - } case Stmt::ParenExprClass: llvm_unreachable("ParenExprs already handled."); @@ -511,38 +540,44 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::NullStmtClass: case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: + case Expr::MSDependentExistsStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); - break; case Stmt::GNUNullExprClass: { // GNU __null is a pointer-width integer, not an actual pointer. - const ProgramState *state = Pred->getState(); - state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false)); - MakeNode(Dst, S, Pred, state); + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(S, Pred->getLocationContext(), + svalBuilder.makeIntValWithPtrWidth(0, false)); + Bldr.generateNode(S, Pred, state); break; } case Stmt::ObjCAtSynchronizedStmtClass: + Bldr.takeNodes(Pred); VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; + // FIXME. + case Stmt::ObjCSubscriptRefExprClass: + break; + case Stmt::ObjCPropertyRefExprClass: // Implicitly handled by Environment::getSVal(). - Dst.Add(Pred); break; case Stmt::ImplicitValueInitExprClass: { - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); SVal val = svalBuilder.makeZeroVal(ty); - MakeNode(Dst, S, Pred, state->BindExpr(S, val)); + Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), + val)); break; } - case Stmt::ExprWithCleanupsClass: { - Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst); + case Stmt::ExprWithCleanupsClass: + // Handled due to fully linearised CFG. break; - } // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: @@ -556,7 +591,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: - case Stmt::ObjCStringLiteralClass: + case Expr::ObjCNumericLiteralClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: @@ -565,50 +600,101 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: case Stmt::AtomicExprClass: - // Fall through. + // Fall through. + // Currently all handling of 'throw' just falls to the CFG. We + // can consider doing more if necessary. + case Stmt::CXXThrowExprClass: + // Fall through. + // Cases we intentionally don't evaluate, since they don't need // to be explicitly evaluated. case Stmt::AddrLabelExprClass: case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: case Stmt::FloatingLiteralClass: case Stmt::SizeOfPackExprClass: - case Stmt::CXXNullPtrLiteralExprClass: - Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. + case Stmt::StringLiteralClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXNullPtrLiteralExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this); + Bldr.addNodes(Dst); break; + } + + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: { + Bldr.takeNodes(Pred); + + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + + // FIXME: explicitly model with a region and the actual contents + // of the container. For now, conjure a symbol. + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext); + + for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); + it != et; ++it) { + ExplodedNode *N = *it; + const Expr *Ex = cast<Expr>(S); + QualType resultType = Ex->getType(); + const LocationContext *LCtx = N->getLocationContext(); + SVal result = + svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType, + currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result); + Bldr2.generateNode(S, N, state); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } case Stmt::ArraySubscriptExprClass: + Bldr.takeNodes(Pred); VisitLvalArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::AsmStmtClass: + Bldr.takeNodes(Pred); VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; - case Stmt::BlockDeclRefExprClass: { - const BlockDeclRefExpr *BE = cast<BlockDeclRefExpr>(S); - VisitCommonDeclRefExpr(BE, BE->getDecl(), Pred, Dst); - break; - } - case Stmt::BlockExprClass: + Bldr.takeNodes(Pred); VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BinaryOperatorClass: { const BinaryOperator* B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { + Bldr.takeNodes(Pred); VisitLogicalExpr(B, Pred, Dst); + Bldr.addNodes(Dst); break; } else if (B->getOpcode() == BO_Comma) { - const ProgramState *state = Pred->getState(); - MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(B, Pred, + state->BindExpr(B, Pred->getLocationContext(), + state->getSVal(B->getRHS(), + Pred->getLocationContext()))); break; } + Bldr.takeNodes(Pred); + if (AMgr.shouldEagerlyAssume() && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; @@ -618,13 +704,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, else VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CallExprClass: case Stmt::CXXOperatorCallExprClass: - case Stmt::CXXMemberCallExprClass: { + case Stmt::CXXMemberCallExprClass: + case Stmt::UserDefinedLiteralClass: { + Bldr.takeNodes(Pred); VisitCallExpr(cast<CallExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::CXXCatchStmtClass: { + Bldr.takeNodes(Pred); + VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; } @@ -633,58 +730,78 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, const CXXConstructExpr *C = cast<CXXConstructExpr>(S); // For block-level CXXConstructExpr, we don't have a destination region. // Let VisitCXXConstructExpr() create one. + Bldr.takeNodes(Pred); VisitCXXConstructExpr(C, 0, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXNewExprClass: { + Bldr.takeNodes(Pred); const CXXNewExpr *NE = cast<CXXNewExpr>(S); VisitCXXNewExpr(NE, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXDeleteExprClass: { + Bldr.takeNodes(Pred); const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); VisitCXXDeleteExpr(CDE, Pred, Dst); + Bldr.addNodes(Dst); break; } // FIXME: ChooseExpr is really a constant. We need to fix // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr + Bldr.takeNodes(Pred); const ChooseExpr *C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CompoundAssignOperatorClass: + Bldr.takeNodes(Pred); VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::CompoundLiteralExprClass: + Bldr.takeNodes(Pred); VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // '?' operator + Bldr.takeNodes(Pred); const AbstractConditionalOperator *C = cast<AbstractConditionalOperator>(S); VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::CXXThisExprClass: + Bldr.takeNodes(Pred); VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::DeclRefExprClass: { + Bldr.takeNodes(Pred); const DeclRefExpr *DE = cast<DeclRefExpr>(S); VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::DeclStmtClass: + Bldr.takeNodes(Pred); VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ImplicitCastExprClass: @@ -695,6 +812,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { + Bldr.takeNodes(Pred); const CastExpr *C = cast<CastExpr>(S); // Handle the previsit checks. ExplodedNodeSet dstPrevisit; @@ -709,58 +827,98 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Handle the postvisit checks. getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); + Bldr.addNodes(Dst); break; } case Expr::MaterializeTemporaryExprClass: { + Bldr.takeNodes(Pred); const MaterializeTemporaryExpr *Materialize = cast<MaterializeTemporaryExpr>(S); - if (!Materialize->getType()->isRecordType()) - CreateCXXTemporaryObject(Materialize, Pred, Dst); + if (Materialize->getType()->isRecordType()) + Dst.Add(Pred); else - Visit(Materialize->GetTemporaryExpr(), Pred, Dst); + CreateCXXTemporaryObject(Materialize, Pred, Dst); + Bldr.addNodes(Dst); break; } case Stmt::InitListExprClass: + Bldr.takeNodes(Pred); VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::MemberExprClass: + Bldr.takeNodes(Pred); VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; + case Stmt::ObjCIvarRefExprClass: + Bldr.takeNodes(Pred); VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::ObjCForCollectionStmtClass: + Bldr.takeNodes(Pred); VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; - case Stmt::ObjCMessageExprClass: - VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); + case Stmt::ObjCMessageExprClass: { + Bldr.takeNodes(Pred); + // Is this a property access? + const ParentMap &PM = Pred->getLocationContext()->getParentMap(); + const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S); + bool evaluated = false; + + if (const PseudoObjectExpr *PO = + dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) { + const Expr *syntactic = PO->getSyntacticForm(); + if (const ObjCPropertyRefExpr *PR = + dyn_cast<ObjCPropertyRefExpr>(syntactic)) { + bool isSetter = ME->getNumArgs() > 0; + VisitObjCMessage(ObjCMessage(ME, PR, isSetter), Pred, Dst); + evaluated = true; + } + else if (isa<BinaryOperator>(syntactic)) { + VisitObjCMessage(ObjCMessage(ME, 0, true), Pred, Dst); + } + } + + if (!evaluated) + VisitObjCMessage(ME, Pred, Dst); + + Bldr.addNodes(Dst); break; + } case Stmt::ObjCAtThrowStmtClass: { // FIXME: This is not complete. We basically treat @throw as // an abort. - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, S, Pred, Pred->getState()); + Bldr.generateNode(S, Pred, Pred->getState()); break; } case Stmt::ReturnStmtClass: + Bldr.takeNodes(Pred); VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::OffsetOfExprClass: + Bldr.takeNodes(Pred); VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::UnaryExprOrTypeTraitExprClass: + Bldr.takeNodes(Pred); VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); break; case Stmt::StmtExprClass: { @@ -770,81 +928,154 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Empty statement expression. assert(SE->getType() == getContext().VoidTy && "Empty statement expression must have void type."); - Dst.Add(Pred); break; } if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { - const ProgramState *state = Pred->getState(); - MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(SE, Pred, + state->BindExpr(SE, Pred->getLocationContext(), + state->getSVal(LastExpr, + Pred->getLocationContext()))); } - else - Dst.Add(Pred); - break; } - case Stmt::StringLiteralClass: { - const ProgramState *state = Pred->getState(); - SVal V = state->getLValue(cast<StringLiteral>(S)); - MakeNode(Dst, S, Pred, state->BindExpr(S, V)); - return; - } - case Stmt::UnaryOperatorClass: { + Bldr.takeNodes(Pred); const UnaryOperator *U = cast<UnaryOperator>(S); - if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) { + if (AMgr.shouldEagerlyAssume() && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); evalEagerlyAssume(Dst, Tmp, U); } else VisitUnaryOperator(U, Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::PseudoObjectExprClass: { + Bldr.takeNodes(Pred); + ProgramStateRef state = Pred->getState(); + const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S); + if (const Expr *Result = PE->getResultExpr()) { + SVal V = state->getSVal(Result, Pred->getLocationContext()); + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), V)); + } + else + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), + UnknownVal())); + + Bldr.addNodes(Dst); break; } } } -//===----------------------------------------------------------------------===// -// Block entrance. (Update counters). -//===----------------------------------------------------------------------===// +bool ExprEngine::replayWithoutInlining(ExplodedNode *N, + const LocationContext *CalleeLC) { + const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); + assert(CalleeSF && CallerSF); + ExplodedNode *BeforeProcessingCall = 0; + + // Find the first node before we started processing the call expression. + while (N) { + ProgramPoint L = N->getLocation(); + BeforeProcessingCall = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + + // Skip the nodes corresponding to the inlined code. + if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) + continue; + // We reached the caller. Find the node right before we started + // processing the CallExpr. + if (isa<PostPurgeDeadSymbols>(L)) + continue; + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) + if (SP->getStmt() == CalleeSF->getCallSite()) + continue; + break; + } + + if (!BeforeProcessingCall) + return false; + + // TODO: Clean up the unneeded nodes. + + // Build an Epsilon node from which we will restart the analyzes. + const Stmt *CE = CalleeSF->getCallSite(); + ProgramPoint NewNodeLoc = + EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); + // Add the special flag to GDM to signal retrying with no inlining. + // Note, changing the state ensures that we are not going to cache out. + ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); + NewNodeState = NewNodeState->set<ReplayWithoutInlining>((void*)CE); + + // Make the new node a successor of BeforeProcessingCall. + bool IsNew = false; + ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew); + // We cached out at this point. Caching out is common due to us backtracking + // from the inlined function, which might spawn several paths. + if (!IsNew) + return true; + + NewNode->addPredecessor(BeforeProcessingCall, G); -void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes, - GenericNodeBuilder<BlockEntrance> &nodeBuilder){ + // Add the new node to the work list. + Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(), + CalleeSF->getIndex()); + NumTimesRetriedWithoutInlining++; + return true; +} + +/// Block entrance. (Update counters). +void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, + NodeBuilderWithSinks &nodeBuilder) { // FIXME: Refactor this into a checker. - const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock(); - ExplodedNode *pred = nodeBuilder.getPredecessor(); + ExplodedNode *pred = nodeBuilder.getContext().getPred(); - if (nodeBuilder.getBlockCounter().getNumVisited( - pred->getLocationContext()->getCurrentStackFrame(), - block->getBlockID()) >= AMgr.getMaxVisit()) { + if (nodeBuilder.getContext().getCurrentBlockCount() >= AMgr.getMaxVisit()) { static SimpleProgramPointTag tag("ExprEngine : Block count exceeded"); - nodeBuilder.generateNode(pred->getState(), pred, &tag, true); - } -} - -//===----------------------------------------------------------------------===// -// Generic node creation. -//===----------------------------------------------------------------------===// + const ExplodedNode *Sink = + nodeBuilder.generateNode(pred->getState(), pred, &tag, true); + + // Check if we stopped at the top level function or not. + // Root node should have the location context of the top most function. + const LocationContext *CalleeLC = pred->getLocation().getLocationContext(); + const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const LocationContext *RootLC = + (*G.roots_begin())->getLocation().getLocationContext(); + if (RootLC->getCurrentStackFrame() != CalleeSF) { + Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); + + // Re-run the call evaluation without inlining it, by storing the + // no-inlining policy in the state and enqueuing the new work item on + // the list. Replay should almost never fail. Use the stats to catch it + // if it does. + if ((!AMgr.NoRetryExhausted && replayWithoutInlining(pred, CalleeLC))) + return; + NumMaxBlockCountReachedInInlined++; + } else + NumMaxBlockCountReached++; -ExplodedNode *ExprEngine::MakeNode(ExplodedNodeSet &Dst, const Stmt *S, - ExplodedNode *Pred, const ProgramState *St, - ProgramPoint::Kind K, - const ProgramPointTag *tag) { - assert (Builder && "StmtNodeBuilder not present."); - SaveAndRestore<const ProgramPointTag*> OldTag(Builder->Tag); - Builder->Tag = tag; - return Builder->MakeNode(Dst, S, Pred, St, K); + // Make sink nodes as exhausted(for stats) only if retry failed. + Engine.blocksExhausted.push_back(std::make_pair(L, Sink)); + } } //===----------------------------------------------------------------------===// // Branch processing. //===----------------------------------------------------------------------===// -const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, - const Stmt *Terminator, - bool branchTaken) { +ProgramStateRef ExprEngine::MarkBranch(ProgramStateRef state, + const Stmt *Terminator, + const LocationContext *LCtx, + bool branchTaken) { switch (Terminator->getStmtClass()) { default: @@ -867,7 +1098,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, (Op == BO_LOr && !branchTaken) ? B->getRHS() : B->getLHS(); - return state->BindExpr(B, UndefinedVal(Ex)); + return state->BindExpr(B, LCtx, UndefinedVal(Ex)); } case Stmt::BinaryConditionalOperatorClass: @@ -885,7 +1116,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, else Ex = C->getFalseExpr(); - return state->BindExpr(C, UndefinedVal(Ex)); + return state->BindExpr(C, LCtx, UndefinedVal(Ex)); } case Stmt::ChooseExprClass: { // ?: @@ -893,7 +1124,7 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, const ChooseExpr *C = cast<ChooseExpr>(Terminator); const Expr *Ex = branchTaken ? C->getLHS() : C->getRHS(); - return state->BindExpr(C, UndefinedVal(Ex)); + return state->BindExpr(C, LCtx, UndefinedVal(Ex)); } } } @@ -904,8 +1135,9 @@ const ProgramState *ExprEngine::MarkBranch(const ProgramState *state, /// This function returns the SVal bound to Condition->IgnoreCasts if all the // cast(s) did was sign-extend the original value. static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, - const ProgramState *state, + ProgramStateRef state, const Stmt *Condition, + const LocationContext *LCtx, ASTContext &Ctx) { const Expr *Ex = dyn_cast<Expr>(Condition); @@ -936,15 +1168,22 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) return UnknownVal(); - return state->getSVal(Ex); + return state->getSVal(Ex, LCtx); } void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, - BranchNodeBuilder& builder) { + NodeBuilderContext& BldCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + currentBuilderContext = &BldCtx; // Check for NULL conditions; e.g. "for(;;)" if (!Condition) { - builder.markInfeasible(false); + BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF); + NullCondBldr.markInfeasible(false); + NullCondBldr.generateNode(Pred->getState(), true, Pred); return; } @@ -952,65 +1191,84 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, Condition->getLocStart(), "Error evaluating branch"); - getCheckerManager().runCheckersForBranchCondition(Condition, builder, *this); - - // If the branch condition is undefined, return; - if (!builder.isFeasible(true) && !builder.isFeasible(false)) + ExplodedNodeSet CheckersOutSet; + getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet, + Pred, *this); + // We generated only sinks. + if (CheckersOutSet.empty()) return; - const ProgramState *PrevState = builder.getState(); - SVal X = PrevState->getSVal(Condition); - - if (X.isUnknownOrUndef()) { - // 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; + BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); + for (NodeBuilder::iterator I = CheckersOutSet.begin(), + E = CheckersOutSet.end(); E != I; ++I) { + ExplodedNode *PredI = *I; + + if (PredI->isSink()) + continue; + + ProgramStateRef PrevState = Pred->getState(); + SVal X = PrevState->getSVal(Condition, Pred->getLocationContext()); + + if (X.isUnknownOrUndef()) { + // 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(), + PrevState, Condition, + Pred->getLocationContext(), + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } } } } + + const LocationContext *LCtx = PredI->getLocationContext(); + // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) { - builder.generateNode(MarkBranch(PrevState, Term, true), true); - builder.generateNode(MarkBranch(PrevState, Term, false), false); - return; + builder.generateNode(MarkBranch(PrevState, Term, LCtx, true), + true, PredI); + builder.generateNode(MarkBranch(PrevState, Term, LCtx, false), + false, PredI); + continue; } - } - DefinedSVal V = cast<DefinedSVal>(X); + DefinedSVal V = cast<DefinedSVal>(X); - // Process the true branch. - if (builder.isFeasible(true)) { - if (const ProgramState *state = PrevState->assume(V, true)) - builder.generateNode(MarkBranch(state, Term, true), true); - else - builder.markInfeasible(true); - } + // Process the true branch. + if (builder.isFeasible(true)) { + if (ProgramStateRef state = PrevState->assume(V, true)) + builder.generateNode(MarkBranch(state, Term, LCtx, true), + true, PredI); + else + builder.markInfeasible(true); + } - // Process the false branch. - if (builder.isFeasible(false)) { - if (const ProgramState *state = PrevState->assume(V, false)) - builder.generateNode(MarkBranch(state, Term, false), false); - else - builder.markInfeasible(false); + // Process the false branch. + if (builder.isFeasible(false)) { + if (ProgramStateRef state = PrevState->assume(V, false)) + builder.generateNode(MarkBranch(state, Term, LCtx, false), + false, PredI); + else + builder.markInfeasible(false); + } } + currentBuilderContext = 0; } /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { - const ProgramState *state = builder.getState(); - SVal V = state->getSVal(builder.getTarget()); + ProgramStateRef state = builder.getState(); + SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); // Three possibilities: // @@ -1051,18 +1309,20 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. -void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) { - StateMgr.EndPath(builder.getState()); - getCheckerManager().runCheckersForEndPath(builder, *this); +void ExprEngine::processEndOfFunction(NodeBuilderContext& BC) { + StateMgr.EndPath(BC.Pred->getState()); + ExplodedNodeSet Dst; + getCheckerManager().runCheckersForEndPath(BC, Dst, *this); + Engine.enqueueEndOfFunction(Dst); } /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { typedef SwitchNodeBuilder::iterator iterator; - const ProgramState *state = builder.getState(); + ProgramStateRef state = builder.getState(); const Expr *CondE = builder.getCondition(); - SVal CondV_untested = state->getSVal(CondE); + SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); if (CondV_untested.isUndef()) { //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); @@ -1073,7 +1333,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { } DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); - const ProgramState *DefaultSt = state; + ProgramStateRef DefaultSt = state; iterator I = builder.begin(), EI = builder.end(); bool defaultIsFeasible = I == EI; @@ -1106,7 +1366,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { CondV, CaseVal); // Now "assume" that the case matches. - if (const ProgramState *stateNew = state->assume(Res, true)) { + if (ProgramStateRef stateNew = state->assume(Res, true)) { builder.generateCaseStmtNode(I, stateNew); // If CondV evaluates to a constant, then we know that this @@ -1119,7 +1379,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // Now "assume" that the case doesn't match. Add this state // to the default state (if it is feasible). if (DefaultSt) { - if (const ProgramState *stateNew = DefaultSt->assume(Res, false)) { + if (ProgramStateRef stateNew = DefaultSt->assume(Res, false)) { defaultIsFeasible = true; DefaultSt = stateNew; } @@ -1166,7 +1426,10 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - const ProgramState *state = Pred->getState(); + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { assert(Ex->isLValue()); @@ -1181,22 +1444,29 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, V = UnknownVal(); } - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0, + ProgramPoint::PostLValueKind); return; } if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { assert(!Ex->isLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; } if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { SVal V = svalBuilder.getFunctionPointer(FD); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), false, 0, + ProgramPoint::PostLValueKind); + return; + } + if (isa<FieldDecl>(D)) { + // FIXME: Compute lvalue of fields. + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, UnknownVal()), + false, 0, ProgramPoint::PostLValueKind); return; } + assert (false && "ValueDecl support for this ValueDecl not implemented."); } @@ -1213,24 +1483,33 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, ExplodedNodeSet checkerPreStmt; getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this); + StmtNodeBuilder Bldr(checkerPreStmt, Dst, *currentBuilderContext); + for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(), ei = checkerPreStmt.end(); it != ei; ++it) { - const ProgramState *state = (*it)->getState(); - SVal V = state->getLValue(A->getType(), state->getSVal(Idx), - state->getSVal(Base)); + const LocationContext *LCtx = (*it)->getLocationContext(); + ProgramStateRef state = (*it)->getState(); + SVal V = state->getLValue(A->getType(), + state->getSVal(Idx, LCtx), + state->getSVal(Base, LCtx)); assert(A->isLValue()); - MakeNode(Dst, A, *it, state->BindExpr(A, V), ProgramPoint::PostLValueKind); + Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), + false, 0, ProgramPoint::PostLValueKind); } } /// VisitMemberExpr - Transfer function for member expressions. void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &TopDst) { + StmtNodeBuilder Bldr(Pred, TopDst, *currentBuilderContext); + ExplodedNodeSet Dst; Decl *member = M->getMemberDecl(); if (VarDecl *VD = dyn_cast<VarDecl>(member)) { assert(M->isLValue()); + Bldr.takeNodes(Pred); VisitCommonDeclRefExpr(M, VD, Pred, Dst); + Bldr.addNodes(Dst); return; } @@ -1239,15 +1518,16 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, return; Expr *baseExpr = M->getBase()->IgnoreParens(); - const ProgramState *state = Pred->getState(); - SVal baseExprVal = state->getSVal(baseExpr); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal baseExprVal = state->getSVal(baseExpr, Pred->getLocationContext()); if (isa<nonloc::LazyCompoundVal>(baseExprVal) || isa<nonloc::CompoundVal>(baseExprVal) || // FIXME: This can originate by conjuring a symbol for an unknown // temporary struct object, see test/Analysis/fields.c: // (p = getit()).x isa<nonloc::SymbolVal>(baseExprVal)) { - MakeNode(Dst, M, Pred, state->BindExpr(M, UnknownVal())); + Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, UnknownVal())); return; } @@ -1258,9 +1538,13 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // For all other cases, compute an lvalue. SVal L = state->getLValue(field, baseExprVal); if (M->isLValue()) - MakeNode(Dst, M, Pred, state->BindExpr(M, L), ProgramPoint::PostLValueKind); - else - evalLoad(Dst, M, Pred, state, L); + Bldr.generateNode(M, Pred, state->BindExpr(M, LCtx, L), false, 0, + ProgramPoint::PostLValueKind); + else { + Bldr.takeNodes(Pred); + evalLoad(Dst, M, M, Pred, state, L); + Bldr.addNodes(Dst); + } } /// evalBind - Handle the semantics of binding a value to a specific location. @@ -1272,12 +1556,17 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, // Do a previsit of the bind. ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, - StoreE, *this); + StoreE, *this, + ProgramPoint::PostStmtKind); + ExplodedNodeSet TmpDst; + StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currentBuilderContext); + + const LocationContext *LC = Pred->getLocationContext(); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { - - const ProgramState *state = (*I)->getState(); + ExplodedNode *PredI = *I; + ProgramStateRef state = PredI->getState(); if (atDeclInit) { const VarRegion *VR = @@ -1288,8 +1577,15 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, state = state->bindLoc(location, Val); } - MakeNode(Dst, StoreE, *I, state); + const MemRegion *LocReg = 0; + if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location)) + LocReg = LocRegVal->getRegion(); + + const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0); + Bldr.generateNode(L, PredI, state, false); } + + Dst.insert(TmpDst); } /// evalStore - Handle the semantics of a store via an assignment. @@ -1303,24 +1599,19 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *LocationE, ExplodedNode *Pred, - const ProgramState *state, SVal location, SVal Val, + ProgramStateRef state, SVal location, SVal Val, const ProgramPointTag *tag) { - - assert(Builder && "StmtNodeBuilder must be defined."); - // Proceed with the store. We use AssignE as the anchor for the PostStore // ProgramPoint if it is non-NULL, and LocationE otherwise. const Expr *StoreE = AssignE ? AssignE : LocationE; if (isa<loc::ObjCPropRef>(location)) { - loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); - return VisitObjCMessage(ObjCPropertySetter(prop.getPropRefExpr(), - StoreE, Val), Pred, Dst); + assert(false); } // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; - evalLocation(Tmp, LocationE, Pred, state, location, tag, false); + evalLocation(Tmp, AssignE, LocationE, Pred, state, location, tag, false); if (Tmp.empty()) return; @@ -1328,24 +1619,21 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, if (location.isUndef()) return; - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind, - ProgramPoint::PostStoreKind); - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - evalBind(Dst, StoreE, *NI, location, Val); + evalBind(Dst, StoreE, *NI, location, Val, false); } -void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex, - ExplodedNode *Pred, - const ProgramState *state, SVal location, - const ProgramPointTag *tag, QualType LoadTy) { +void ExprEngine::evalLoad(ExplodedNodeSet &Dst, + const Expr *NodeEx, + const Expr *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + QualType LoadTy) +{ assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); - - if (isa<loc::ObjCPropRef>(location)) { - loc::ObjCPropRef prop = cast<loc::ObjCPropRef>(location); - return VisitObjCMessage(ObjCPropertyGetter(prop.getPropRefExpr(), Ex), - Pred, Dst); - } + assert(!isa<loc::ObjCPropRef>(location)); // Are we loading from a region? This actually results in two loads; one // to fetch the address of the referenced value and one to fetch the @@ -1358,72 +1646,84 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const Expr *Ex, static SimpleProgramPointTag loadReferenceTag("ExprEngine : Load Reference"); ExplodedNodeSet Tmp; - evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, + evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state, + location, &loadReferenceTag, getContext().getPointerType(RT->getPointeeType())); // Perform the load from the referenced value. for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { state = (*I)->getState(); - location = state->getSVal(Ex); - evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); + location = state->getSVal(BoundEx, (*I)->getLocationContext()); + evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy); } return; } } - evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); + evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy); } -void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, const Expr *Ex, - ExplodedNode *Pred, - const ProgramState *state, SVal location, - const ProgramPointTag *tag, QualType LoadTy) { - +void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, + const Expr *NodeEx, + const Expr *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + QualType LoadTy) { + assert(NodeEx); + assert(BoundEx); // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; - evalLocation(Tmp, Ex, Pred, state, location, tag, true); - + evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, tag, true); if (Tmp.empty()) return; + StmtNodeBuilder Bldr(Tmp, Dst, *currentBuilderContext); if (location.isUndef()) return; - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); - // Proceed with the load. for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { state = (*NI)->getState(); + const LocationContext *LCtx = (*NI)->getLocationContext(); if (location.isUnknown()) { // This is important. We must nuke the old binding. - MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), - ProgramPoint::PostLoadKind, tag); + Bldr.generateNode(NodeEx, *NI, + state->BindExpr(BoundEx, LCtx, UnknownVal()), + false, tag, + ProgramPoint::PostLoadKind); } else { if (LoadTy.isNull()) - LoadTy = Ex->getType(); + LoadTy = BoundEx->getType(); SVal V = state->getSVal(cast<Loc>(location), LoadTy); - MakeNode(Dst, Ex, *NI, state->bindExprAndLocation(Ex, location, V), - ProgramPoint::PostLoadKind, tag); + Bldr.generateNode(NodeEx, *NI, + state->bindExprAndLocation(BoundEx, LCtx, location, V), + false, tag, ProgramPoint::PostLoadKind); } } } -void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, - ExplodedNode *Pred, - const ProgramState *state, SVal location, - const ProgramPointTag *tag, bool isLoad) { +void ExprEngine::evalLocation(ExplodedNodeSet &Dst, + const Stmt *NodeEx, + const Stmt *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + bool isLoad) { + StmtNodeBuilder BldrTop(Pred, Dst, *currentBuilderContext); // Early checks for performance reason. if (location.isUnknown()) { - Dst.Add(Pred); return; } ExplodedNodeSet Src; - if (Pred->getState() == state) { - Src.Add(Pred); - } else { + BldrTop.takeNodes(Pred); + StmtNodeBuilder Bldr(Pred, Src, *currentBuilderContext); + if (Pred->getState() != state) { // Associate this new state with an ExplodedNode. // FIXME: If I pass null tag, the graph is incorrect, e.g for // int *p; @@ -1435,88 +1735,12 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, // FIXME: why is 'tag' not used instead of etag? static SimpleProgramPointTag etag("ExprEngine: Location"); - - ExplodedNode *N = Builder->generateNode(S, state, Pred, &etag); - Src.Add(N ? N : Pred); + Bldr.generateNode(NodeEx, Pred, state, false, &etag); } - getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S, - *this); -} - -bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, - ExplodedNode *Pred) { - return false; - - // Inlining isn't correct right now because we: - // (a) don't generate CallExit nodes. - // (b) we need a way to postpone doing post-visits of CallExprs until - // the CallExit. This means we need CallExits for the non-inline - // cases as well. - -#if 0 - const ProgramState *state = Pred->getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - // Specially handle CXXMethods. - const CXXMethodDecl *methodDecl = 0; - - switch (CE->getStmtClass()) { - default: break; - case Stmt::CXXOperatorCallExprClass: { - const CXXOperatorCallExpr *opCall = cast<CXXOperatorCallExpr>(CE); - methodDecl = - dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl()); - break; - } - case Stmt::CXXMemberCallExprClass: { - const CXXMemberCallExpr *memberCall = cast<CXXMemberCallExpr>(CE); - const MemberExpr *memberExpr = - cast<MemberExpr>(memberCall->getCallee()->IgnoreParens()); - methodDecl = cast<CXXMethodDecl>(memberExpr->getMemberDecl()); - break; - } - } - - - - - // Check if the function definition is in the same translation unit. - if (FD->hasBody(FD)) { - const StackFrameContext *stackFrame = - AMgr.getStackFrame(AMgr.getAnalysisContext(FD), - Pred->getLocationContext(), - CE, Builder->getBlock(), Builder->getIndex()); - // Now we have the definition of the callee, create a CallEnter node. - CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); - - ExplodedNode *N = Builder->generateNode(Loc, state, Pred); - Dst.Add(N); - return true; - } - - // Check if we can find the function definition in other translation units. - if (AMgr.hasIndexer()) { - AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD); - if (C == 0) - return false; - const StackFrameContext *stackFrame = - AMgr.getStackFrame(C, Pred->getLocationContext(), - CE, Builder->getBlock(), Builder->getIndex()); - CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); - ExplodedNode *N = Builder->generateNode(Loc, state, Pred); - Dst.Add(N); - return true; - } - - // Generate the CallExit node. - - return false; -#endif + ExplodedNodeSet Tmp; + getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad, + NodeEx, BoundEx, *this); + BldrTop.addNodes(Tmp); } std::pair<const ProgramPointTag *, const ProgramPointTag*> @@ -1528,108 +1752,67 @@ ExprEngine::getEagerlyAssumeTags() { } void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, - const Expr *Ex) { - + const Expr *Ex) { + StmtNodeBuilder Bldr(Src, Dst, *currentBuilderContext); for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { ExplodedNode *Pred = *I; - // Test if the previous node was as the same expression. This can happen // when the expression fails to evaluate to anything meaningful and // (as an optimization) we don't generate a node. ProgramPoint P = Pred->getLocation(); if (!isa<PostStmt>(P) || cast<PostStmt>(P).getStmt() != Ex) { - Dst.Add(Pred); continue; } - const ProgramState *state = Pred->getState(); - SVal V = state->getSVal(Ex); - if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) { + ProgramStateRef state = Pred->getState(); + SVal V = state->getSVal(Ex, Pred->getLocationContext()); + nonloc::SymbolVal *SEV = dyn_cast<nonloc::SymbolVal>(&V); + if (SEV && SEV->isExpression()) { const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = getEagerlyAssumeTags(); // First assume that the condition is true. - if (const ProgramState *StateTrue = state->assume(*SEV, true)) { + if (ProgramStateRef StateTrue = state->assume(*SEV, true)) { SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); - StateTrue = StateTrue->BindExpr(Ex, Val); - Dst.Add(Builder->generateNode(Ex, StateTrue, Pred, tags.first)); + StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); + Bldr.generateNode(Ex, Pred, StateTrue, false, tags.first); } // Next, assume that the condition is false. - if (const ProgramState *StateFalse = state->assume(*SEV, false)) { + if (ProgramStateRef StateFalse = state->assume(*SEV, false)) { SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); - StateFalse = StateFalse->BindExpr(Ex, Val); - Dst.Add(Builder->generateNode(Ex, StateFalse, Pred, tags.second)); + StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); + Bldr.generateNode(Ex, Pred, StateFalse, false, tags.second); } } - else - Dst.Add(Pred); } } void ExprEngine::VisitAsmStmt(const AsmStmt *A, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); -} - -void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt *A, - AsmStmt::const_outputs_iterator I, - AsmStmt::const_outputs_iterator E, - ExplodedNode *Pred, ExplodedNodeSet &Dst) { - if (I == E) { - VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); - return; - } - - ExplodedNodeSet Tmp; - Visit(*I, Pred, Tmp); - ++I; + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + // We have processed both the inputs and the outputs. All of the outputs + // should evaluate to Locs. Nuke all of their values. - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) - VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); -} - -void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt *A, - AsmStmt::const_inputs_iterator I, - AsmStmt::const_inputs_iterator E, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - if (I == E) { - - // We have processed both the inputs and the outputs. All of the outputs - // should evaluate to Locs. Nuke all of their values. - - // FIXME: Some day in the future it would be nice to allow a "plug-in" - // which interprets the inline asm and stores proper results in the - // outputs. + // FIXME: Some day in the future it would be nice to allow a "plug-in" + // which interprets the inline asm and stores proper results in the + // outputs. - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); - for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), - OE = A->end_outputs(); OI != OE; ++OI) { - - SVal X = state->getSVal(*OI); - assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. - - if (isa<Loc>(X)) - state = state->bindLoc(cast<Loc>(X), UnknownVal()); - } + for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), + OE = A->end_outputs(); OI != OE; ++OI) { + SVal X = state->getSVal(*OI, Pred->getLocationContext()); + assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. - MakeNode(Dst, A, Pred, state); - return; + if (isa<Loc>(X)) + state = state->bindLoc(cast<Loc>(X), UnknownVal()); } - ExplodedNodeSet Tmp; - Visit(*I, Pred, Tmp); - - ++I; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI) - VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); + Bldr.generateNode(A, Pred, state); } - //===----------------------------------------------------------------------===// // Visualization. //===----------------------------------------------------------------------===// @@ -1694,6 +1877,10 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "CallExit"; break; + case ProgramPoint::EpsilonKind: + Out << "Epsilon Point"; + break; + default: { if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { const Stmt *S = L->getStmt(); @@ -1811,10 +1998,10 @@ struct DOTGraphTraits<ExplodedNode*> : } } - const ProgramState *state = N->getState(); - Out << "\\|StateID: " << (void*) state + ProgramStateRef state = N->getState(); + Out << "\\|StateID: " << (void*) state.getPtr() << " NodeID: " << (void*) N << "\\|"; - state->printDOT(Out, *N->getLocationContext()->getCFG()); + state->printDOT(Out); Out << "\\l"; @@ -1852,9 +2039,7 @@ void ExprEngine::ViewGraph(bool trim) { // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { - BugReportEquivClass& EQ = *EI; - const BugReport &R = **EQ.begin(); - ExplodedNode *N = const_cast<ExplodedNode*>(R.getErrorNode()); + ExplodedNode *N = const_cast<ExplodedNode*>(EI->begin()->getErrorNode()); if (N) Src.push_back(N); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 68ccc59..93e598a 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -13,7 +13,6 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/Analysis/Support/SaveAndRestore.h" using namespace clang; using namespace ento; @@ -35,38 +34,40 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end(); it != ei; ++it) { - const ProgramState *state = (*it)->getState(); - SVal LeftV = state->getSVal(LHS); - SVal RightV = state->getSVal(RHS); + ProgramStateRef state = (*it)->getState(); + const LocationContext *LCtx = (*it)->getLocationContext(); + SVal LeftV = state->getSVal(LHS, LCtx); + SVal RightV = state->getSVal(RHS, LCtx); BinaryOperator::Opcode Op = B->getOpcode(); if (Op == BO_Assign) { // EXPERIMENTAL: "Conjured" symbols. // FIXME: Handle structs. - if (RightV.isUnknown() || - !getConstraintManager().canReasonAbout(RightV)) { - unsigned Count = Builder->getCurrentBlockCount(); - RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count); + if (RightV.isUnknown()) { + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LCtx, Count); } // Simulate the effects of a "store": bind the value of the RHS // to the L-Value represented by the LHS. SVal ExprVal = B->isLValue() ? LeftV : RightV; - evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, ExprVal), LeftV, RightV); + evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal), + LeftV, RightV); continue; } if (!B->isAssignmentOp()) { + StmtNodeBuilder Bldr(*it, Tmp2, *currentBuilderContext); // Process non-assignments except commas or short-circuited // logical expressions (LAnd and LOr). SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); if (Result.isUnknown()) { - MakeNode(Tmp2, B, *it, state); + Bldr.generateNode(B, *it, state); continue; } - state = state->BindExpr(B, Result); - MakeNode(Tmp2, B, *it, state); + state = state->BindExpr(B, LCtx, Result); + Bldr.generateNode(B, *it, state); continue; } @@ -91,13 +92,14 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // null dereferences, and so on. ExplodedNodeSet Tmp; SVal location = LeftV; - evalLoad(Tmp, LHS, *it, state, location); + evalLoad(Tmp, B, LHS, *it, state, location); for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { state = (*I)->getState(); - SVal V = state->getSVal(LHS); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal V = state->getSVal(LHS, LCtx); // Get the computation type. QualType CTy = @@ -122,16 +124,15 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, SVal LHSVal; - if (Result.isUnknown() || - !getConstraintManager().canReasonAbout(Result)) { + if (Result.isUnknown()) { - unsigned Count = Builder->getCurrentBlockCount(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); // The symbolic value is actually for the type of the left-hand side // expression, not the computation type, as this is the value the // LValue on the LHS will bind to. - LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LTy, - Count); + LHSVal = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), LCtx, + LTy, Count); // However, we need to convert the symbol to the computation type. Result = svalBuilder.evalCast(LHSVal, CTy, LTy); @@ -145,9 +146,9 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // In C++, assignment and compound assignment operators return an // lvalue. if (B->isLValue()) - state = state->BindExpr(B, location); + state = state->BindExpr(B, LCtx, location); else - state = state->BindExpr(B, Result); + state = state->BindExpr(B, LCtx, Result); evalStore(Tmp2, B, LHS, *I, state, location, LHSVal); } @@ -165,8 +166,12 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, Pred->getLocationContext()); ExplodedNodeSet Tmp; - MakeNode(Tmp, BE, Pred, Pred->getState()->BindExpr(BE, V), - ProgramPoint::PostLValueKind); + StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext); + Bldr.generateNode(BE, Pred, + Pred->getState()->BindExpr(BE, Pred->getLocationContext(), + V), + false, 0, + ProgramPoint::PostLValueKind); // FIXME: Move all post/pre visits to ::Visit(). getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); @@ -178,13 +183,13 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNodeSet dstPreStmt; getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this); - if (CastE->getCastKind() == CK_LValueToRValue || - CastE->getCastKind() == CK_GetObjCProperty) { + if (CastE->getCastKind() == CK_LValueToRValue) { for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); I!=E; ++I) { ExplodedNode *subExprNode = *I; - const ProgramState *state = subExprNode->getState(); - evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex)); + ProgramStateRef state = subExprNode->getState(); + const LocationContext *LCtx = subExprNode->getLocationContext(); + evalLoad(Dst, CastE, CastE, subExprNode, state, state->getSVal(Ex, LCtx)); } return; } @@ -196,6 +201,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) T = ExCast->getTypeAsWritten(); + StmtNodeBuilder Bldr(dstPreStmt, Dst, *currentBuilderContext); for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); I != E; ++I) { @@ -204,10 +210,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, switch (CastE->getCastKind()) { case CK_LValueToRValue: llvm_unreachable("LValueToRValue casts handled earlier."); - case CK_GetObjCProperty: - llvm_unreachable("GetObjCProperty casts handled earlier."); case CK_ToVoid: - Dst.Add(Pred); continue; // The analyzer doesn't do anything special with these casts, // since it understands retain/release semantics already. @@ -215,14 +218,21 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_ARCConsumeObject: case CK_ARCReclaimReturnedObject: case CK_ARCExtendBlockObject: // Fall-through. + case CK_CopyAndAutoreleaseBlockObject: + // The analyser can ignore atomic casts for now, although some future + // checkers may want to make certain that you're not modifying the same + // value through atomic and nonatomic pointers. + case CK_AtomicToNonAtomic: + case CK_NonAtomicToAtomic: // True no-ops. case CK_NoOp: case CK_FunctionToPointerDecay: { // Copy the SVal of Ex to CastE. - const ProgramState *state = Pred->getState(); - SVal V = state->getSVal(Ex); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, Pred, state); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(Ex, LCtx); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); continue; } case CK_Dependent: @@ -254,30 +264,76 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_AnyPointerToBlockPointerCast: case CK_ObjCObjectLValueCast: { // Delegate to SValBuilder to process. - const ProgramState *state = Pred->getState(); - SVal V = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(Ex, LCtx); V = svalBuilder.evalCast(V, T, ExTy); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, Pred, state); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); continue; } case CK_DerivedToBase: case CK_UncheckedDerivedToBase: { // For DerivedToBase cast, delegate to the store manager. - const ProgramState *state = Pred->getState(); - SVal val = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal val = state->getSVal(Ex, LCtx); val = getStoreManager().evalDerivedToBase(val, T); - state = state->BindExpr(CastE, val); - MakeNode(Dst, CastE, Pred, state); + state = state->BindExpr(CastE, LCtx, val); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Handle C++ dyn_cast. + case CK_Dynamic: { + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal val = state->getSVal(Ex, LCtx); + + // Compute the type of the result. + QualType resultType = CastE->getType(); + if (CastE->isLValue()) + resultType = getContext().getPointerType(resultType); + + bool Failed = false; + + // Check if the value being cast evaluates to 0. + if (val.isZeroConstant()) + Failed = true; + // Else, evaluate the cast. + else + val = getStoreManager().evalDynamicCast(val, T, Failed); + + if (Failed) { + if (T->isReferenceType()) { + // A bad_cast exception is thrown if input value is a reference. + // Currently, we model this, by generating a sink. + Bldr.generateNode(CastE, Pred, state, true); + continue; + } else { + // If the cast fails on a pointer, bind to 0. + state = state->BindExpr(CastE, LCtx, svalBuilder.makeNull()); + } + } else { + // If we don't know if the cast succeeded, conjure a new symbol. + if (val.isUnknown()) { + DefinedOrUnknownSVal NewSym = svalBuilder.getConjuredSymbolVal(NULL, + CastE, LCtx, resultType, + currentBuilderContext->getCurrentBlockCount()); + state = state->BindExpr(CastE, LCtx, NewSym); + } else + // Else, bind to the derived region value. + state = state->BindExpr(CastE, LCtx, val); + } + Bldr.generateNode(CastE, Pred, state); continue; } - // Various C++ casts that are not handled yet. - case CK_Dynamic: + // Various C++ casts that are not handled yet. case CK_ToUnion: case CK_BaseToDerived: case CK_NullToMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: + case CK_ReinterpretMemberPointer: case CK_UserDefinedConversion: case CK_ConstructorConversion: case CK_VectorSplat: @@ -286,13 +342,12 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, QualType resultType = CastE->getType(); if (CastE->isLValue()) resultType = getContext().getPointerType(resultType); - - SVal result = - svalBuilder.getConjuredSymbolVal(NULL, CastE, resultType, - Builder->getCurrentBlockCount()); - - const ProgramState *state = Pred->getState()->BindExpr(CastE, result); - MakeNode(Dst, CastE, Pred, state); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal result = svalBuilder.getConjuredSymbolVal(NULL, CastE, LCtx, + resultType, currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = Pred->getState()->BindExpr(CastE, LCtx, + result); + Bldr.generateNode(CastE, Pred, state); continue; } } @@ -302,18 +357,20 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); + const InitListExpr *ILE = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); - const ProgramState *state = Pred->getState(); - SVal ILV = state->getSVal(ILE); + ProgramStateRef state = Pred->getState(); + SVal ILV = state->getSVal(ILE, Pred->getLocationContext()); const LocationContext *LC = Pred->getLocationContext(); state = state->bindCompoundLiteral(CL, LC, ILV); if (CL->isLValue()) - MakeNode(Dst, CL, Pred, state->BindExpr(CL, state->getLValue(CL, LC))); + B.generateNode(CL, Pred, state->BindExpr(CL, LC, state->getLValue(CL, LC))); else - MakeNode(Dst, CL, Pred, state->BindExpr(CL, ILV)); + B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV)); } void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, @@ -326,29 +383,32 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, // Assumption: The CFG has one DeclStmt per Decl. const Decl *D = *DS->decl_begin(); - if (!D || !isa<VarDecl>(D)) + if (!D || !isa<VarDecl>(D)) { + //TODO:AZ: remove explicit insertion after refactoring is done. + Dst.insert(Pred); return; + } // FIXME: all pre/post visits should eventually be handled by ::Visit(). ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); + StmtNodeBuilder B(dstPreVisit, Dst, *currentBuilderContext); const VarDecl *VD = dyn_cast<VarDecl>(D); - for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); I!=E; ++I) { ExplodedNode *N = *I; - const ProgramState *state = N->getState(); + ProgramStateRef state = N->getState(); // Decls without InitExpr are not initialized explicitly. const LocationContext *LC = N->getLocationContext(); if (const Expr *InitEx = VD->getInit()) { - SVal InitVal = state->getSVal(InitEx); + SVal InitVal = state->getSVal(InitEx, Pred->getLocationContext()); // We bound the temp obj region to the CXXConstructExpr. Now recover // the lazy compound value when the variable is not a reference. - if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() && + if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && !VD->getType()->isReferenceType() && isa<loc::MemRegionVal>(InitVal)){ InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); assert(isa<nonloc::LazyCompoundVal>(InitVal)); @@ -356,40 +416,46 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, // Recover some path-sensitivity if a scalar value evaluated to // UnknownVal. - if ((InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) && - !VD->getType()->isReferenceType()) { - InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); + if (InitVal.isUnknown()) { + QualType Ty = InitEx->getType(); + if (InitEx->isLValue()) { + Ty = getContext().getPointerType(Ty); + } + + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, LC, Ty, + currentBuilderContext->getCurrentBlockCount()); } - - evalBind(Dst, DS, N, state->getLValue(VD, LC), InitVal, true); + B.takeNodes(N); + ExplodedNodeSet Dst2; + evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); + B.addNodes(Dst2); } else { - MakeNode(Dst, DS, N, state->bindDeclWithNoInit(state->getRegion(VD, LC))); + B.generateNode(DS, N,state->bindDeclWithNoInit(state->getRegion(VD, LC))); } } } void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - assert(B->getOpcode() == BO_LAnd || B->getOpcode() == BO_LOr); - - const ProgramState *state = Pred->getState(); - SVal X = state->getSVal(B); + + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal X = state->getSVal(B, LCtx); assert(X.isUndef()); const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); assert(Ex); if (Ex == B->getRHS()) { - X = state->getSVal(Ex); + X = state->getSVal(Ex, LCtx); // Handle undefined values. if (X.isUndef()) { - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); return; } @@ -401,13 +467,15 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // value later when necessary. We don't have the machinery in place for // this right now, and since most logical expressions are used for branches, // the payoff is not likely to be large. Instead, we do eager evaluation. - if (const ProgramState *newState = state->assume(XD, true)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType()))); + if (ProgramStateRef newState = state->assume(XD, true)) + Bldr.generateNode(B, Pred, + newState->BindExpr(B, LCtx, + svalBuilder.makeIntVal(1U, B->getType()))); - if (const ProgramState *newState = state->assume(XD, false)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType()))); + if (ProgramStateRef newState = state->assume(XD, false)) + Bldr.generateNode(B, Pred, + newState->BindExpr(B, LCtx, + svalBuilder.makeIntVal(0U, B->getType()))); } else { // We took the LHS expression. Depending on whether we are '&&' or @@ -415,15 +483,17 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // the short-circuiting. X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, B->getType()); - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + Bldr.generateNode(B, Pred, state->BindExpr(B, LCtx, X)); } } void ExprEngine::VisitInitListExpr(const InitListExpr *IE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); QualType T = getContext().getCanonicalType(IE->getType()); unsigned NumInitElements = IE->getNumInits(); @@ -434,24 +504,27 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, // e.g: static int* myArray[] = {}; if (NumInitElements == 0) { SVal V = svalBuilder.makeCompoundVal(T, vals); - MakeNode(Dst, IE, Pred, state->BindExpr(IE, V)); + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); return; } for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { - vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it)), vals); + vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it), LCtx), + vals); } - MakeNode(Dst, IE, Pred, - state->BindExpr(IE, svalBuilder.makeCompoundVal(T, vals))); + B.generateNode(IE, Pred, + state->BindExpr(IE, LCtx, + svalBuilder.makeCompoundVal(T, vals))); return; } if (Loc::isLocType(T) || T->isIntegerType()) { assert(IE->getNumInits() == 1); const Expr *initEx = IE->getInit(0); - MakeNode(Dst, IE, Pred, state->BindExpr(IE, state->getSVal(initEx))); + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, + state->getSVal(initEx, LCtx))); return; } @@ -463,33 +536,35 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); - const ProgramState *state = Pred->getState(); - SVal X = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal X = state->getSVal(Ex, LCtx); assert (X.isUndef()); const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); assert(SE); - X = state->getSVal(SE); + X = state->getSVal(SE, LCtx); // Make sure that we invalidate the previous binding. - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); + B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, X, true)); } void ExprEngine:: VisitOffsetOfExpr(const OffsetOfExpr *OOE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - Expr::EvalResult Res; - if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); + StmtNodeBuilder B(Pred, Dst, *currentBuilderContext); + APSInt IV; + if (OOE->EvaluateAsInt(IV, getContext())) { assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); assert(OOE->getType()->isIntegerType()); assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType()); SVal X = svalBuilder.makeIntVal(IV); - MakeNode(Dst, OOE, Pred, Pred->getState()->BindExpr(OOE, X)); - return; + B.generateNode(OOE, Pred, + Pred->getState()->BindExpr(OOE, Pred->getLocationContext(), + X)); } // FIXME: Handle the case where __builtin_offsetof is not a constant. - Dst.Add(Pred); } @@ -497,6 +572,7 @@ void ExprEngine:: VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); QualType T = Ex->getTypeOfArgument(); @@ -506,78 +582,69 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, // FIXME: Add support for VLA type arguments and VLA expressions. // When that happens, we should probably refactor VLASizeChecker's code. - Dst.Add(Pred); return; } else if (T->getAs<ObjCObjectType>()) { // Some code tries to take the sizeof an ObjCObjectType, relying that // the compiler has laid out its representation. Just report Unknown // for these. - Dst.Add(Pred); return; } } - Expr::EvalResult Result; - Ex->Evaluate(Result, getContext()); - CharUnits amt = CharUnits::fromQuantity(Result.Val.getInt().getZExtValue()); + APSInt Value = Ex->EvaluateKnownConstInt(getContext()); + CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue()); - const ProgramState *state = Pred->getState(); - state = state->BindExpr(Ex, svalBuilder.makeIntVal(amt.getQuantity(), + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(Ex, Pred->getLocationContext(), + svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType())); - MakeNode(Dst, Ex, Pred, state); + Bldr.generateNode(Ex, Pred, state); } void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); switch (U->getOpcode()) { - default: + default: { + Bldr.takeNodes(Pred); + ExplodedNodeSet Tmp; + VisitIncrementDecrementOperator(U, Pred, Tmp); + Bldr.addNodes(Tmp); + } break; case UO_Real: { const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - // For all other types, UO_Real is an identity operation. - assert (U->getType() == Ex->getType()); - const ProgramState *state = (*I)->getState(); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + break; } - - return; + + // For all other types, UO_Real is an identity operation. + assert (U->getType() == Ex->getType()); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); + break; } - case UO_Imag: { - + case UO_Imag: { const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - // FIXME: We don't have complex SValues yet. - if (Ex->getType()->isAnyComplexType()) { - // Just report "Unknown." - Dst.Add(*I); - continue; - } - - // For all other types, UO_Imag returns 0. - const ProgramState *state = (*I)->getState(); - SVal X = svalBuilder.makeZeroVal(Ex->getType()); - MakeNode(Dst, U, *I, state->BindExpr(U, X)); + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + break; } - - return; + // For all other types, UO_Imag returns 0. + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal X = svalBuilder.makeZeroVal(Ex->getType()); + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, X)); + break; } case UO_Plus: @@ -586,22 +653,19 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, case UO_Deref: case UO_AddrOf: case UO_Extension: { - + // FIXME: We can probably just have some magic in Environment::getSVal() + // that propagates values, instead of creating a new node here. + // // Unary "+" is a no-op, similar to a parentheses. We still have places // where it may be a block-level expression, so we need to // generate an extra node that just propagates the value of the - // subexpression. - + // subexpression. const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const ProgramState *state = (*I)->getState(); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); + break; } case UO_LNot: @@ -609,144 +673,139 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, case UO_Not: { assert (!U->isLValue()); const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const ProgramState *state = (*I)->getState(); - - // Get the value of the subexpression. - SVal V = state->getSVal(Ex); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); - if (V.isUnknownOrUndef()) { - MakeNode(Dst, U, *I, state->BindExpr(U, V)); - continue; - } + // Get the value of the subexpression. + SVal V = state->getSVal(Ex, LCtx); - switch (U->getOpcode()) { - default: - llvm_unreachable("Invalid Opcode."); - - case UO_Not: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, evalComplement(cast<NonLoc>(V))); - break; - - case UO_Minus: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, evalMinus(cast<NonLoc>(V))); - break; - - case UO_LNot: - - // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." - // - // Note: technically we do "E == 0", but this is the same in the - // transfer functions as "0 == E". - SVal Result; - - if (isa<Loc>(V)) { - Loc X = svalBuilder.makeNull(); - Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, - U->getType()); - } - else { - nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, - U->getType()); - } - - state = state->BindExpr(U, Result); - - break; - } + if (V.isUnknownOrUndef()) { + Bldr.generateNode(U, Pred, state->BindExpr(U, LCtx, V)); + break; + } - MakeNode(Dst, U, *I, state); + switch (U->getOpcode()) { + default: + llvm_unreachable("Invalid Opcode."); + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, LCtx, evalComplement(cast<NonLoc>(V))); + break; + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, LCtx, evalMinus(cast<NonLoc>(V))); + break; + case UO_LNot: + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + if (isa<Loc>(V)) { + Loc X = svalBuilder.makeNull(); + Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, + U->getType()); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, + U->getType()); + } + + state = state->BindExpr(U, LCtx, Result); + break; } - - return; + Bldr.generateNode(U, Pred, state); + break; } } - + +} + +void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { // Handle ++ and -- (both pre- and post-increment). assert (U->isIncrementDecrementOp()); - ExplodedNodeSet Tmp; const Expr *Ex = U->getSubExpr()->IgnoreParens(); - Visit(Ex, Pred, Tmp); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState(); + SVal loc = state->getSVal(Ex, LCtx); + + // Perform a load. + ExplodedNodeSet Tmp; + evalLoad(Tmp, U, Ex, Pred, state, loc); + + ExplodedNodeSet Dst2; + StmtNodeBuilder Bldr(Tmp, Dst2, *currentBuilderContext); + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) { - const ProgramState *state = (*I)->getState(); - SVal loc = state->getSVal(Ex); + state = (*I)->getState(); + assert(LCtx == (*I)->getLocationContext()); + SVal V2_untested = state->getSVal(Ex, LCtx); - // Perform a load. - ExplodedNodeSet Tmp2; - evalLoad(Tmp2, Ex, *I, state, loc); + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested)); + continue; + } + DefinedSVal V2 = cast<DefinedSVal>(V2_untested); - for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { - - state = (*I2)->getState(); - SVal V2_untested = state->getSVal(Ex); - - // Propagate unknown and undefined values. - if (V2_untested.isUnknownOrUndef()) { - MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); - continue; - } - DefinedSVal V2 = cast<DefinedSVal>(V2_untested); - - // Handle all other values. - BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add - : BO_Sub; - - // If the UnaryOperator has non-location type, use its type to create the - // constant value. If the UnaryOperator has location type, create the - // constant with int type and pointer width. - SVal RHS; - - if (U->getType()->isAnyPointerType()) - RHS = svalBuilder.makeArrayIndex(1); - else - RHS = svalBuilder.makeIntVal(1, U->getType()); - - SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); - - // Conjure a new symbol if necessary to recover precision. - if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ - DefinedOrUnknownSVal SymVal = - svalBuilder.getConjuredSymbolVal(NULL, Ex, - Builder->getCurrentBlockCount()); - Result = SymVal; + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub; + + // If the UnaryOperator has non-location type, use its type to create the + // constant value. If the UnaryOperator has location type, create the + // constant with int type and pointer width. + SVal RHS; + + if (U->getType()->isAnyPointerType()) + RHS = svalBuilder.makeArrayIndex(1); + else + RHS = svalBuilder.makeIntVal(1, U->getType()); + + SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown()){ + DefinedOrUnknownSVal SymVal = + svalBuilder.getConjuredSymbolVal(NULL, Ex, LCtx, + currentBuilderContext->getCurrentBlockCount()); + Result = SymVal; + + // If the value is a location, ++/-- should always preserve + // non-nullness. Check if the original value was non-null, and if so + // propagate that constraint. + if (Loc::isLocType(U->getType())) { + DefinedOrUnknownSVal Constraint = + svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); - // If the value is a location, ++/-- should always preserve - // non-nullness. Check if the original value was non-null, and if so - // propagate that constraint. - if (Loc::isLocType(U->getType())) { - DefinedOrUnknownSVal Constraint = - svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); + if (!state->assume(Constraint, true)) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = svalBuilder.evalEQ(state, SymVal, + svalBuilder.makeZeroVal(U->getType())); - if (!state->assume(Constraint, true)) { - // It isn't feasible for the original value to be null. - // Propagate this constraint. - Constraint = svalBuilder.evalEQ(state, SymVal, - svalBuilder.makeZeroVal(U->getType())); - - - state = state->assume(Constraint, false); - assert(state); - } + + state = state->assume(Constraint, false); + assert(state); } } - - // Since the lvalue-to-rvalue conversion is explicit in the AST, - // we bind an l-value if the operator is prefix and an lvalue (in C++). - if (U->isLValue()) - state = state->BindExpr(U, loc); - else - state = state->BindExpr(U, U->isPostfix() ? V2 : Result); - - // Perform the store. - evalStore(Dst, NULL, U, *I2, state, loc, Result); } + + // Since the lvalue-to-rvalue conversion is explicit in the AST, + // we bind an l-value if the operator is prefix and an lvalue (in C++). + if (U->isLValue()) + state = state->BindExpr(U, LCtx, loc); + else + state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result); + + // Perform the store. + Bldr.takeNodes(*I); + ExplodedNodeSet Dst3; + evalStore(Dst3, U, U, *I, state, loc, Result); + Bldr.addNodes(Dst3); } + Dst.insert(Dst2); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index acb0074..a14a491 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -16,81 +16,11 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/StmtCXX.h" using namespace clang; using namespace ento; -namespace { -class CallExprWLItem { -public: - CallExpr::const_arg_iterator I; - ExplodedNode *N; - - CallExprWLItem(const CallExpr::const_arg_iterator &i, ExplodedNode *n) - : I(i), N(n) {} -}; -} - -void ExprEngine::evalArguments(ConstExprIterator AI, ConstExprIterator AE, - const FunctionProtoType *FnType, - ExplodedNode *Pred, ExplodedNodeSet &Dst, - bool FstArgAsLValue) { - - - SmallVector<CallExprWLItem, 20> WorkList; - WorkList.reserve(AE - AI); - WorkList.push_back(CallExprWLItem(AI, Pred)); - - while (!WorkList.empty()) { - CallExprWLItem Item = WorkList.back(); - WorkList.pop_back(); - - if (Item.I == AE) { - Dst.insert(Item.N); - continue; - } - - // Evaluate the argument. - ExplodedNodeSet Tmp; - if (FstArgAsLValue) { - FstArgAsLValue = false; - } - - Visit(*Item.I, Item.N, Tmp); - ++(Item.I); - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI != NE; ++NI) - WorkList.push_back(CallExprWLItem(Item.I, *NI)); - } -} - -void ExprEngine::evalCallee(const CallExpr *callExpr, - const ExplodedNodeSet &src, - ExplodedNodeSet &dest) { - - const Expr *callee = 0; - - switch (callExpr->getStmtClass()) { - case Stmt::CXXMemberCallExprClass: { - // Evaluate the implicit object argument that is the recipient of the - // call. - callee = cast<CXXMemberCallExpr>(callExpr)->getImplicitObjectArgument(); - - // FIXME: handle member pointers. - if (!callee) - return; - - break; - } - default: { - callee = callExpr->getCallee()->IgnoreParens(); - break; - } - } - - for (ExplodedNodeSet::iterator i = src.begin(), e = src.end(); i != e; ++i) - Visit(callee, *i, dest); -} - const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D, const StackFrameContext *SFC) { const Type *T = D->getTypeForDecl(); @@ -107,19 +37,26 @@ const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl, void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens(); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); // Bind the temporary object to the value of the expression. Then bind // the expression to the location of the object. - SVal V = state->getSVal(tempExpr); + SVal V = state->getSVal(tempExpr, Pred->getLocationContext()); const MemRegion *R = - svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, - Pred->getLocationContext()); + svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx); state = state->bindLoc(loc::MemRegionVal(R), V); - MakeNode(Dst, ME, Pred, state->BindExpr(ME, loc::MemRegionVal(R))); + Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R))); +} + +void ExprEngine::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + VisitCXXConstructExpr(expr, 0, Pred, Dst); } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, @@ -127,8 +64,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { +#if 0 const CXXConstructorDecl *CD = E->getConstructor(); assert(CD); +#endif #if 0 if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) @@ -136,26 +75,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, return; #endif - // Evaluate other arguments. - ExplodedNodeSet argsEvaluated; - const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); - evalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, argsEvaluated); - #if 0 // Is the constructor elidable? if (E->isElidable()) { - VisitAggExpr(E->getArg(0), destNodes, Pred, Dst); - // FIXME: this is here to force propagation if VisitAggExpr doesn't - if (destNodes.empty()) - destNodes.Add(Pred); + destNodes.Add(Pred); return; } #endif // Perform the previsit of the constructor. - ExplodedNodeSet destPreVisit; - getCheckerManager().runCheckersForPreStmt(destPreVisit, argsEvaluated, E, - *this); + ExplodedNodeSet SrcNodes; + SrcNodes.Add(Pred); + ExplodedNodeSet TmpNodes; + getCheckerManager().runCheckersForPreStmt(TmpNodes, SrcNodes, E, *this); // Evaluate the constructor. Currently we don't now allow checker-specific // implementations of specific constructors (as we do with ordinary @@ -174,7 +106,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, // parameter region. const StackFrameContext *SFC = AMgr.getStackFrame(CD, Pred->getLocationContext(), - E, Builder->getBlock(), Builder->getIndex()); + E, currentBuilderContext->getBlock(), + currentStmtIdx); // Create the 'this' region. const CXXThisRegion *ThisR = @@ -182,34 +115,33 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, CallEnter Loc(E, SFC, Pred->getLocationContext()); - - for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), - NE = argsEvaluated.end(); NI != NE; ++NI) { - const ProgramState *state = (*NI)->getState(); + StmtNodeBuilder Bldr(SrcNodes, TmpNodes, *currentBuilderContext); + for (ExplodedNodeSet::iterator NI = SrcNodes.begin(), + NE = SrcNodes.end(); NI != NE; ++NI) { + ProgramStateRef state = (*NI)->getState(); // Setup 'this' region, so that the ctor is evaluated on the object pointed // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - if (ExplodedNode *N = Builder->generateNode(Loc, state, *NI)) - destNodes.Add(N); + Bldr.generateNode(Loc, *NI, state); } } #endif // Default semantics: invalidate all regions passed as arguments. ExplodedNodeSet destCall; - - for (ExplodedNodeSet::iterator - i = destPreVisit.begin(), e = destPreVisit.end(); - i != e; ++i) { - ExplodedNode *Pred = *i; - const LocationContext *LC = Pred->getLocationContext(); - const ProgramState *state = Pred->getState(); + StmtNodeBuilder Bldr(TmpNodes, destCall, *currentBuilderContext); + for (ExplodedNodeSet::iterator i = TmpNodes.begin(), e = TmpNodes.end(); + i != e; ++i) + { + ExplodedNode *Pred = *i; + const LocationContext *LC = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState(); - state = invalidateArguments(state, CallOrObjCMessage(E, state), LC); - Builder->MakeNode(destCall, E, Pred, state); + state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC); + Bldr.generateNode(E, Pred, state); + } } - // Do the post visit. getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this); } @@ -219,31 +151,33 @@ void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) return; + // Create the context for 'this' region. - const StackFrameContext *SFC = AMgr.getStackFrame(DD, - Pred->getLocationContext(), - S, Builder->getBlock(), - Builder->getIndex()); + const StackFrameContext *SFC = + AnalysisDeclContexts.getContext(DD)-> + getStackFrame(Pred->getLocationContext(), S, + currentBuilderContext->getBlock(), currentStmtIdx); const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC); CallEnter PP(S, SFC, Pred->getLocationContext()); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); - ExplodedNode *N = Builder->generateNode(PP, state, Pred); - if (N) - Dst.Add(N); + Bldr.generateNode(PP, Pred, state); } void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); - unsigned blockCount = Builder->getCurrentBlockCount(); + unsigned blockCount = currentBuilderContext->getCurrentBlockCount(); + const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = - svalBuilder.getConjuredSymbolVal(NULL, CNE, CNE->getType(), blockCount); + svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount); const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = @@ -252,26 +186,31 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const ProgramState *state = Pred->getState(); - state = state->BindExpr(CNE, loc::MemRegionVal(EleReg)); - MakeNode(Dst, CNE, Pred, state); + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(CNE, Pred->getLocationContext(), + loc::MemRegionVal(EleReg)); + Bldr.generateNode(CNE, Pred, state); return; } + // FIXME: Update for AST changes. +#if 0 // Evaluate constructor arguments. const FunctionProtoType *FnType = NULL; const CXXConstructorDecl *CD = CNE->getConstructor(); if (CD) FnType = CD->getType()->getAs<FunctionProtoType>(); ExplodedNodeSet argsEvaluated; + Bldr.takeNodes(Pred); evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(), FnType, Pred, argsEvaluated); + Bldr.addNodes(argsEvaluated); // Initialize the object region and bind the 'new' expression. for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), E = argsEvaluated.end(); I != E; ++I) { - const ProgramState *state = (*I)->getState(); + ProgramStateRef state = (*I)->getState(); // Accumulate list of regions that are invalidated. // FIXME: Eventually we should unify the logic for constructor @@ -281,7 +220,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end(); ai != ae; ++ai) { - SVal val = state->getSVal(*ai); + SVal val = state->getSVal(*ai, (*I)->getLocationContext()); if (const MemRegion *region = val.getAsRegion()) regionsToInvalidate.push_back(region); } @@ -289,18 +228,21 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (ObjTy->isRecordType()) { regionsToInvalidate.push_back(EleReg); // Invalidate the regions. + // TODO: Pass the call to new information as the last argument, to limit + // the globals which will get invalidated. state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, - /* invalidateGlobals = */ true); + CNE, blockCount, 0, 0); } else { // Invalidate the regions. + // TODO: Pass the call to new information as the last argument, to limit + // the globals which will get invalidated. state = state->invalidateRegions(regionsToInvalidate, - CNE, blockCount, 0, - /* invalidateGlobals = */ true); + CNE, blockCount, 0, 0); if (CNE->hasInitializer()) { - SVal V = state->getSVal(*CNE->constructor_arg_begin()); + SVal V = state->getSVal(*CNE->constructor_arg_begin(), + (*I)->getLocationContext()); state = state->bindLoc(loc::MemRegionVal(EleReg), V); } else { // Explicitly set to undefined, because currently we retrieve symbolic @@ -308,32 +250,51 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal()); } } - state = state->BindExpr(CNE, loc::MemRegionVal(EleReg)); - MakeNode(Dst, CNE, *I, state); + state = state->BindExpr(CNE, (*I)->getLocationContext(), + loc::MemRegionVal(EleReg)); + Bldr.generateNode(CNE, *I, state); } +#endif } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, - ExplodedNode *Pred,ExplodedNodeSet &Dst) { - // Should do more checking. - ExplodedNodeSet Argevaluated; - Visit(CDE->getArgument(), Pred, Argevaluated); - for (ExplodedNodeSet::iterator I = Argevaluated.begin(), - E = Argevaluated.end(); I != E; ++I) { - const ProgramState *state = (*I)->getState(); - MakeNode(Dst, CDE, *I, state); + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(CDE, Pred, state); +} + +void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const VarDecl *VD = CS->getExceptionDecl(); + if (!VD) { + Dst.Add(Pred); + return; } + + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = svalBuilder.getConjuredSymbolVal(CS, LCtx, VD->getType(), + currentBuilderContext->getCurrentBlockCount()); + ProgramStateRef state = Pred->getState(); + state = state->bindLoc(state->getLValue(VD, LCtx), V); + + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + Bldr.generateNode(CS, Pred, state); } void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + // Get the this object region from StoreManager. + const LocationContext *LCtx = Pred->getLocationContext(); const MemRegion *R = svalBuilder.getRegionManager().getCXXThisRegion( getContext().getCanonicalType(TE->getType()), - Pred->getLocationContext()); + LCtx); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(loc::MemRegionVal(R)); - MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); + Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 6d377b9..b99bd54 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -15,40 +15,72 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/AST/DeclCXX.h" -#include "clang/Analysis/Support/SaveAndRestore.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace ento; -namespace { - // Trait class for recording returned expression in the state. - struct ReturnExpr { - static int TagInt; - typedef const Stmt *data_type; - }; - int ReturnExpr::TagInt; +void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { + // Get the entry block in the CFG of the callee. + const StackFrameContext *calleeCtx = CE.getCalleeContext(); + const CFG *CalleeCFG = calleeCtx->getCFG(); + const CFGBlock *Entry = &(CalleeCFG->getEntry()); + + // Validate the CFG. + assert(Entry->empty()); + assert(Entry->succ_size() == 1); + + // Get the solitary sucessor. + const CFGBlock *Succ = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the callee. + BlockEdge Loc(Entry, Succ, calleeCtx); + + // Construct a new state which contains the mapping from actual to + // formal arguments. + const LocationContext *callerCtx = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState()->enterStackFrame(callerCtx, + calleeCtx); + + // Construct a new node and add it to the worklist. + bool isNew; + ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); + Node->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(Node); } -void ExprEngine::processCallEnter(CallEnterNodeBuilder &B) { - const ProgramState *state = - B.getState()->enterStackFrame(B.getCalleeContext()); - B.generateNode(state); +static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) { + while (Node) { + const ProgramPoint &PP = Node->getLocation(); + // Skip any BlockEdges. + if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) { + assert(Node->pred_size() == 1); + Node = *Node->pred_begin(); + continue; + } + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { + const Stmt *S = SP->getStmt(); + return dyn_cast<ReturnStmt>(S); + } + break; + } + return 0; } -void ExprEngine::processCallExit(CallExitNodeBuilder &B) { - const ProgramState *state = B.getState(); - const ExplodedNode *Pred = B.getPredecessor(); +void ExprEngine::processCallExit(ExplodedNode *Pred) { + ProgramStateRef state = Pred->getState(); const StackFrameContext *calleeCtx = - cast<StackFrameContext>(Pred->getLocationContext()); + Pred->getLocationContext()->getCurrentStackFrame(); + const LocationContext *callerCtx = calleeCtx->getParent(); const Stmt *CE = calleeCtx->getCallSite(); // If the callee returns an expression, bind its value to CallExpr. - const Stmt *ReturnedExpr = state->get<ReturnExpr>(); - if (ReturnedExpr) { - SVal RetVal = state->getSVal(ReturnedExpr); - state = state->BindExpr(CE, RetVal); - // Clear the return expr GDM. - state = state->remove<ReturnExpr>(); + if (const ReturnStmt *RS = getReturnStmt(Pred)) { + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(RS, LCtx); + state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. @@ -58,14 +90,197 @@ void ExprEngine::processCallExit(CallExitNodeBuilder &B) { SVal ThisV = state->getSVal(ThisR); // Always bind the region to the CXXConstructExpr. - state = state->BindExpr(CCE, ThisV); + state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV); } - B.generateNode(state); + static SimpleProgramPointTag returnTag("ExprEngine : Call Return"); + PostStmt Loc(CE, callerCtx, &returnTag); + bool isNew; + ExplodedNode *N = G.getNode(Loc, state, false, &isNew); + N->addPredecessor(Pred, G); + if (!isNew) + return; + + // Perform the post-condition check of the CallExpr. + ExplodedNodeSet Dst; + NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N); + SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, + &Ctx); + SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); + + getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this, + /* wasInlined */ true); + + // Enqueue the next element in the block. + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) { + Engine.getWorkList()->enqueue(*I, + calleeCtx->getCallSiteBlock(), + calleeCtx->getIndex()+1); + } } -const ProgramState * -ExprEngine::invalidateArguments(const ProgramState *State, +static unsigned getNumberStackFrames(const LocationContext *LCtx) { + unsigned count = 0; + while (LCtx) { + if (isa<StackFrameContext>(LCtx)) + ++count; + LCtx = LCtx->getParent(); + } + return count; +} + +// Determine if we should inline the call. +bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); + const CFG *CalleeCFG = CalleeADC->getCFG(); + + if (getNumberStackFrames(Pred->getLocationContext()) + == AMgr.InlineMaxStackDepth) + return false; + + if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD)) + return false; + + if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize) + return false; + + return true; +} + +// For now, skip inlining variadic functions. +// We also don't inline blocks. +static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { + if (!E->getAnalysisManager().shouldInlineCall()) + return false; + QualType callee = CE->getCallee()->getType(); + const FunctionProtoType *FT = 0; + if (const PointerType *PT = callee->getAs<PointerType>()) + FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); + else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { + // FIXME: inline blocks. + // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); + (void) BT; + return false; + } + // If we have no prototype, assume the function is okay. + if (!FT) + return true; + + // Skip inlining of variadic functions. + return !FT->isVariadic(); +} + +bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, + const CallExpr *CE, + ExplodedNode *Pred) { + if (!shouldInlineCallExpr(CE, this)) + return false; + + ProgramStateRef state = Pred->getState(); + const Expr *Callee = CE->getCallee(); + const FunctionDecl *FD = + state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl(); + if (!FD || !FD->hasBody(FD)) + return false; + + switch (CE->getStmtClass()) { + default: + // FIXME: Handle C++. + break; + case Stmt::CallExprClass: { + if (!shouldInlineDecl(FD, Pred)) + return false; + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); + const StackFrameContext *CallerSFC = + Pred->getLocationContext()->getCurrentStackFrame(); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(CallerSFC, CE, + currentBuilderContext->getBlock(), + currentStmtIdx); + + CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } + return true; + } + } + return false; +} + +static bool isPointerToConst(const ParmVarDecl *ParamDecl) { + QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType(); + if (PointeeTy != QualType() && PointeeTy.isConstQualified() && + !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) { + return true; + } + return false; +} + +// Try to retrieve the function declaration and find the function parameter +// types which are pointers/references to a non-pointer const. +// We do not invalidate the corresponding argument regions. +static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, + const CallOrObjCMessage &Call) { + const Decl *CallDecl = Call.getDecl(); + if (!CallDecl) + return; + + if (const FunctionDecl *FDecl = dyn_cast<FunctionDecl>(CallDecl)) { + const IdentifierInfo *II = FDecl->getIdentifier(); + + // List the cases, where the region should be invalidated even if the + // argument is const. + if (II) { + StringRef FName = II->getName(); + // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a + // value into thread local storage. The value can later be retrieved with + // 'void *ptheread_getspecific(pthread_key)'. So even thought the + // parameter is 'const void *', the region escapes through the call. + // - funopen - sets a buffer for future IO calls. + // - ObjC functions that end with "NoCopy" can free memory, of the passed + // in buffer. + // - Many CF containers allow objects to escape through custom + // allocators/deallocators upon container construction. + // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName == "pthread_setspecific" || + FName == "funopen" || + FName.endswith("NoCopy") || + (FName.startswith("NS") && + (FName.find("Insert") != StringRef::npos)) || + Call.isCFCGAllowingEscape(FName)) + return; + } + + for (unsigned Idx = 0, E = Call.getNumArgs(); Idx != E; ++Idx) { + if (FDecl && Idx < FDecl->getNumParams()) { + if (isPointerToConst(FDecl->getParamDecl(Idx))) + PreserveArgs.insert(Idx); + } + } + return; + } + + if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) { + assert(MDecl->param_size() <= Call.getNumArgs()); + unsigned Idx = 0; + for (clang::ObjCMethodDecl::param_const_iterator + I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) { + if (isPointerToConst(*I)) + PreserveArgs.insert(Idx); + } + return; + } +} + +ProgramStateRef +ExprEngine::invalidateArguments(ProgramStateRef State, const CallOrObjCMessage &Call, const LocationContext *LC) { SmallVector<const MemRegion *, 8> RegionsToInvalidate; @@ -85,13 +300,21 @@ ExprEngine::invalidateArguments(const ProgramState *State, } else if (Call.isFunctionCall()) { // Block calls invalidate all captured-by-reference values. - if (const MemRegion *Callee = Call.getFunctionCallee().getAsRegion()) { + SVal CalleeVal = Call.getFunctionCallee(); + if (const MemRegion *Callee = CalleeVal.getAsRegion()) { if (isa<BlockDataRegion>(Callee)) RegionsToInvalidate.push_back(Callee); } } + // Indexes of arguments whose values will be preserved by the call. + llvm::SmallSet<unsigned, 1> PreserveArgs; + findPtrToConstParams(PreserveArgs, Call); + for (unsigned idx = 0, e = Call.getNumArgs(); idx != e; ++idx) { + if (PreserveArgs.count(idx)) + continue; + SVal V = Call.getArgSVal(idx); // If we are passing a location wrapped as an integer, unwrap it and @@ -105,7 +328,7 @@ ExprEngine::invalidateArguments(const ProgramState *State, // Invalidate the value of the variable passed by reference. // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underying region + // a basic integer type (e.g., char, int) and the underlying region // is a variable region then strip off the ElementRegion. // FIXME: We really need to think about this for the general case // as sometimes we are reasoning about arrays and other times @@ -116,7 +339,7 @@ ExprEngine::invalidateArguments(const ProgramState *State, // we'll leave it in for now until we have a systematic way of // handling all of these cases. Eventually we need to come up // with an interface to StoreManager so that this logic can be - // approriately delegated to the respective StoreManagers while + // appropriately delegated to the respective StoreManagers while // still allowing us to do checker-specific logic (e.g., // invalidating reference counts), probably via callbacks. if (ER->getElementType()->isIntegralOrEnumerationType()) { @@ -146,18 +369,29 @@ ExprEngine::invalidateArguments(const ProgramState *State, // to identify conjured symbols by an expression pair: the enclosing // expression (the context) and the expression itself. This should // disambiguate conjured symbols. - assert(Builder && "Invalidating arguments outside of a statement context"); - unsigned Count = Builder->getCurrentBlockCount(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); StoreManager::InvalidatedSymbols IS; // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate // global variables. return State->invalidateRegions(RegionsToInvalidate, - Call.getOriginExpr(), Count, - &IS, doesInvalidateGlobals(Call)); + Call.getOriginExpr(), Count, LC, + &IS, &Call); } +static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N, + const CallExpr *CE) { + void *ReplayState = N->getState()->get<ReplayWithoutInlining>(); + if (!ReplayState) + return 0; + const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState); + if (CE == ReplayCE) { + return N->getState()->remove<ReplayWithoutInlining>(); + } + return 0; +} + void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &dst) { // Perform the previsit of the CallExpr. @@ -173,20 +407,21 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, DefaultEval(ExprEngine &eng, const CallExpr *ce) : Eng(eng), CE(ce) {} virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - // Should we inline the call? - if (Eng.getAnalysisManager().shouldInlineCall() && - Eng.InlineCall(Dst, CE, Pred)) { + + ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); + + // First, try to inline the call. + if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) return; - } // First handle the return value. - StmtNodeBuilder &Builder = Eng.getBuilder(); - assert(&Builder && "StmtNodeBuilder must be defined."); + StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); // Get the callee. const Expr *Callee = CE->getCallee()->IgnoreParens(); - const ProgramState *state = Pred->getState(); - SVal L = state->getSVal(Callee); + if (state == 0) + state = Pred->getState(); + SVal L = state->getSVal(Callee, Pred->getLocationContext()); // Figure out the result type. We do this dance to handle references. QualType ResultTy; @@ -200,18 +435,19 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // Conjure a symbol value to use as the result. SValBuilder &SVB = Eng.getSValBuilder(); - unsigned Count = Builder.getCurrentBlockCount(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count); + unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); // Generate a new state with the return value set. - state = state->BindExpr(CE, RetVal); + state = state->BindExpr(CE, LCtx, RetVal); // Invalidate the arguments. - const LocationContext *LC = Pred->getLocationContext(); - state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state), LC); + state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx), + LCtx); // And make the result node. - Eng.MakeNode(Dst, CE, Pred, state); + Bldr.generateNode(CE, Pred, state); } }; @@ -231,23 +467,16 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ExplodedNodeSet Src; - if (const Expr *RetE = RS->getRetValue()) { - // Record the returned expression in the state. It will be used in - // processCallExit to bind the return value to the call expr. - { - static SimpleProgramPointTag tag("ExprEngine: ReturnStmt"); - const ProgramState *state = Pred->getState(); - state = state->set<ReturnExpr>(RetE); - Pred = Builder->generateNode(RetE, state, Pred, &tag); + + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this); + + StmtNodeBuilder B(dstPreVisit, Dst, *currentBuilderContext); + + if (RS->getRetValue()) { + for (ExplodedNodeSet::iterator it = dstPreVisit.begin(), + ei = dstPreVisit.end(); it != ei; ++it) { + B.generateNode(RS, *it, (*it)->getState()); } - // We may get a NULL Pred because we generated a cached node. - if (Pred) - Visit(RetE, Pred, Src); } - else { - Src.Add(Pred); - } - - getCheckerManager().runCheckersForPreStmt(Dst, Src, RS, *this); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index e0560fd..c8ad70a 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -11,10 +11,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/StmtObjC.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" -#include "clang/Analysis/Support/SaveAndRestore.h" using namespace clang; using namespace ento; @@ -22,13 +22,14 @@ using namespace ento; void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - - const ProgramState *state = Pred->getState(); - SVal baseVal = state->getSVal(Ex->getBase()); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal baseVal = state->getSVal(Ex->getBase(), LCtx); SVal location = state->getLValue(Ex->getDecl(), baseVal); ExplodedNodeSet dstIvar; - MakeNode(dstIvar, Ex, Pred, state->BindExpr(Ex, location)); + StmtNodeBuilder Bldr(Pred, dstIvar, *currentBuilderContext); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location)); // Perform the post-condition check of the ObjCIvarRefExpr and store // the created nodes in 'Dst'. @@ -69,10 +70,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // For now: simulate (1) by assigning either a symbol or nil if the // container is empty. Thus this transfer function will by default // result in state splitting. - + const Stmt *elem = S->getElement(); - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); SVal elementV; + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); @@ -80,27 +82,27 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, elementV = state->getLValue(elemD, Pred->getLocationContext()); } else { - elementV = state->getSVal(elem); + elementV = state->getSVal(elem, Pred->getLocationContext()); } ExplodedNodeSet dstLocation; - evalLocation(dstLocation, elem, Pred, state, elementV, NULL, false); - - if (dstLocation.empty()) - return; + Bldr.takeNodes(Pred); + evalLocation(dstLocation, S, elem, Pred, state, elementV, NULL, false); + Bldr.addNodes(dstLocation); for (ExplodedNodeSet::iterator NI = dstLocation.begin(), NE = dstLocation.end(); NI!=NE; ++NI) { Pred = *NI; - const ProgramState *state = Pred->getState(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); // Handle the case where the container still has elements. SVal TrueV = svalBuilder.makeTruthVal(1); - const ProgramState *hasElems = state->BindExpr(S, TrueV); + ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV); // Handle the case where the container has no elements. SVal FalseV = svalBuilder.makeTruthVal(0); - const ProgramState *noElems = state->BindExpr(S, FalseV); + ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV); if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV)) if (const TypedValueRegion *R = @@ -110,8 +112,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // For now, just 'conjure' up a symbolic value. QualType T = R->getValueType(); assert(Loc::isLocType(T)); - unsigned Count = Builder->getCurrentBlockCount(); - SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, LCtx, T, Count); SVal V = svalBuilder.makeLoc(Sym); hasElems = hasElems->bindLoc(elementV, V); @@ -121,8 +123,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, } // Create the new nodes. - MakeNode(Dst, S, Pred, hasElems); - MakeNode(Dst, S, Pred, noElems); + Bldr.generateNode(S, Pred, hasElems); + Bldr.generateNode(S, Pred, noElems); } } @@ -137,29 +139,27 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; - + StmtNodeBuilder Bldr(dstPrevisit, dstEval, *currentBuilderContext); + for (ExplodedNodeSet::iterator DI = dstPrevisit.begin(), DE = dstPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; bool RaisesException = false; - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->hasGeneratedNode); if (const Expr *Receiver = msg.getInstanceReceiver()) { - const ProgramState *state = Pred->getState(); - SVal recVal = state->getSVal(Receiver); + ProgramStateRef state = Pred->getState(); + SVal recVal = state->getSVal(Receiver, Pred->getLocationContext()); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - const ProgramState *notNilState, *nilState; + ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = state->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. if (nilState && !notNilState) { - dstEval.insert(Pred); continue; } @@ -168,13 +168,10 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, if (msg.getSelector() == RaiseSel) RaisesException = true; - // Check if we raise an exception. For now treat these as sinks. + // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - // Dispatch to plug-in transfer function. - evalObjCMessage(dstEval, msg, Pred, notNilState); + evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException); } } else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) { @@ -217,16 +214,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, } } - // Check if we raise an exception. For now treat these as sinks. + // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - // Dispatch to plug-in transfer function. - evalObjCMessage(dstEval, msg, Pred, Pred->getState()); + evalObjCMessage(Bldr, msg, Pred, Pred->getState(), RaisesException); } - - assert(Builder->BuildSinks || Builder->hasGeneratedNode); } // Finally, perform the post-condition check of the ObjCMessageExpr and store @@ -234,11 +226,11 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); } -void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, +void ExprEngine::evalObjCMessage(StmtNodeBuilder &Bldr, + const ObjCMessage &msg, ExplodedNode *Pred, - const ProgramState *state) { - assert (Builder && "StmtNodeBuilder must be defined."); - + ProgramStateRef state, + bool GenSink) { // First handle the return value. SVal ReturnValue = UnknownVal(); @@ -252,7 +244,7 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, // These methods return their receivers. const Expr *ReceiverE = msg.getInstanceReceiver(); if (ReceiverE) - ReturnValue = state->getSVal(ReceiverE); + ReturnValue = state->getSVal(ReceiverE, Pred->getLocationContext()); break; } } @@ -261,19 +253,21 @@ void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, if (ReturnValue.isUnknown()) { SValBuilder &SVB = getSValBuilder(); QualType ResultTy = msg.getResultType(getContext()); - unsigned Count = Builder->getCurrentBlockCount(); + unsigned Count = currentBuilderContext->getCurrentBlockCount(); const Expr *CurrentE = cast<Expr>(currentStmt); - ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, ResultTy, Count); + const LocationContext *LCtx = Pred->getLocationContext(); + ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, LCtx, ResultTy, Count); } // Bind the return value. - state = state->BindExpr(currentStmt, ReturnValue); + const LocationContext *LCtx = Pred->getLocationContext(); + state = state->BindExpr(currentStmt, LCtx, ReturnValue); // Invalidate the arguments (and the receiver) - const LocationContext *LC = Pred->getLocationContext(); - state = invalidateArguments(state, CallOrObjCMessage(msg, state), LC); + state = invalidateArguments(state, CallOrObjCMessage(msg, state, LCtx), LCtx); // And create the new node. - MakeNode(Dst, msg.getOriginExpr(), Pred, state); + Bldr.generateNode(msg.getMessageExpr(), Pred, state, GenSink); + assert(Bldr.hasGeneratedNodes()); } diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp new file mode 100644 index 0000000..c227aac --- /dev/null +++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -0,0 +1,38 @@ +//== FunctionSummary.h - Stores summaries of functions. ------------*- 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 a summary of a function gathered/used by static analyzes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" +using namespace clang; +using namespace ento; + +FunctionSummariesTy::~FunctionSummariesTy() { + for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { + delete(I->second); + } +} + +unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { + unsigned Total = 0; + for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { + Total += I->second->TotalBasicBlocks; + } + return Total; +} + +unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() { + unsigned Total = 0; + for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { + Total += I->second->VisitedBasicBlocks.count(); + } + return Total; +} diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 0c4e427..629f1ea 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -39,15 +39,13 @@ class HTMLDiagnostics : public PathDiagnosticConsumer { llvm::sys::Path Directory, FilePrefix; bool createdDir, noDir; const Preprocessor &PP; - std::vector<const PathDiagnostic*> BatchedDiags; public: HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp); virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } - virtual void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); - - virtual void HandlePathDiagnosticImpl(const PathDiagnostic* D); + virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "HTMLDiagnostics"; @@ -88,34 +86,49 @@ ento::createHTMLDiagnosticConsumer(const std::string& prefix, // Report processing. //===----------------------------------------------------------------------===// -void HTMLDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; +void HTMLDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade) { + for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), + et = Diags.end(); it != et; ++it) { + ReportDiag(**it, FilesMade); } - - const_cast<PathDiagnostic*>(D)->flattenLocations(); - BatchedDiags.push_back(D); } -void -HTMLDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) -{ - while (!BatchedDiags.empty()) { - const PathDiagnostic* D = BatchedDiags.back(); - BatchedDiags.pop_back(); - ReportDiag(*D, FilesMade); - delete D; +static void flattenPath(PathPieces &primaryPath, PathPieces ¤tPath, + const PathPieces &oldPath) { + for (PathPieces::const_iterator it = oldPath.begin(), et = oldPath.end(); + it != et; ++it ) { + PathDiagnosticPiece *piece = it->getPtr(); + if (const PathDiagnosticCallPiece *call = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = + call->getCallEnterEvent(); + if (callEnter) + currentPath.push_back(callEnter); + flattenPath(primaryPath, primaryPath, call->path); + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = + call->getCallExitEvent(); + if (callExit) + currentPath.push_back(callExit); + continue; + } + if (PathDiagnosticMacroPiece *macro = + dyn_cast<PathDiagnosticMacroPiece>(piece)) { + currentPath.push_back(piece); + PathPieces newPath; + flattenPath(primaryPath, newPath, macro->subPieces); + macro->subPieces = newPath; + continue; + } + + currentPath.push_back(piece); } - - BatchedDiags.clear(); } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, - SmallVectorImpl<std::string> *FilesMade){ + SmallVectorImpl<std::string> *FilesMade) { + // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; @@ -138,47 +151,29 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, if (noDir) return; - const SourceManager &SMgr = D.begin()->getLocation().getManager(); - FileID FID; - - // Verify that the entire path is from the same FileID. - for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) { - FullSourceLoc L = I->getLocation().asLocation().getExpansionLoc(); - - if (FID.isInvalid()) { - FID = SMgr.getFileID(L); - } else if (SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? - - // Check the source ranges. - for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), - RE=I->ranges_end(); RI!=RE; ++RI) { - - SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); - - if (!L.isFileID() || SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? - - L = SMgr.getExpansionLoc(RI->getEnd()); - - if (!L.isFileID() || SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? - } - } + // First flatten out the entire path to make it easier to use. + PathPieces path; + flattenPath(path, path, D.path); - if (FID.isInvalid()) - return; // FIXME: Emit a warning? + // The path as already been prechecked that all parts of the path are + // from the same file and that it is non-empty. + const SourceManager &SMgr = (*path.begin())->getLocation().getManager(); + assert(!path.empty()); + FileID FID = + (*path.begin())->getLocation().asLocation().getExpansionLoc().getFileID(); + assert(!FID.isInvalid()); // Create a new rewriter to generate HTML. - Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOptions()); + Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts()); // Process the path. - unsigned n = D.size(); + unsigned n = path.size(); unsigned max = n; - for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend(); - I!=E; ++I, --n) - HandlePiece(R, FID, *I, n, max); + for (PathPieces::const_reverse_iterator I = path.rbegin(), + E = path.rend(); + I != E; ++I, --n) + HandlePiece(R, FID, **I, n, max); // Add line numbers, header, footer, etc. @@ -221,9 +216,9 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, << html::EscapeText(Entry->getName()) << "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>" "<a href=\"#EndPath\">line " - << (*D.rbegin()).getLocation().asLocation().getExpansionLineNumber() + << (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber() << ", column " - << (*D.rbegin()).getLocation().asLocation().getExpansionColumnNumber() + << (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber() << "</a></td></tr>\n" "<tr><td class=\"rowname\">Description:</td><td>" << D.getDescription() << "</td></tr>\n"; @@ -261,10 +256,10 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; os << "\n<!-- BUGLINE " - << D.back()->getLocation().asLocation().getExpansionLineNumber() + << path.back()->getLocation().asLocation().getExpansionLineNumber() << " -->\n"; - os << "\n<!-- BUGPATHLENGTH " << D.size() << " -->\n"; + os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n"; // Mark the end of the tags. os << "\n<!-- BUGMETAEND -->\n"; @@ -353,6 +348,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, const char *Kind = 0; switch (P.getKind()) { + case PathDiagnosticPiece::Call: + llvm_unreachable("Calls should already be handled"); case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. @@ -445,7 +442,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); - Lexer rawLexer(L, PP.getLangOptions(), BufferInfo.begin(), + Lexer rawLexer(L, PP.getLangOpts(), BufferInfo.begin(), MacroName, BufferInfo.end()); Token TheTok; @@ -518,7 +515,7 @@ unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num) { - for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); + for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); I!=E; ++I) { if (const PathDiagnosticMacroPiece *MP = diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 6f92da8..ed94c79 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -18,7 +18,9 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" +#include "clang/Basic/SourceManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -219,6 +221,17 @@ DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const { svalBuilder.getArrayIndexType()); } +ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg) + : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} + +const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { + return cast<ObjCIvarDecl>(D); +} + +QualType ObjCIvarRegion::getValueType() const { + return getDecl()->getType(); +} + QualType CXXBaseObjectRegion::getValueType() const { return QualType(decl->getTypeForDecl(), 0); } @@ -249,6 +262,14 @@ void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, ID.AddPointer(superRegion); } +void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const ObjCStringLiteral* Str, + const MemRegion* superRegion) { + ID.AddInteger((unsigned) ObjCStringRegionKind); + ID.AddPointer(Str); + ID.AddPointer(superRegion); +} + void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned cnt, const MemRegion *) { @@ -285,6 +306,12 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); } +void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const ObjCIvarDecl *ivd, + const MemRegion* superRegion) { + DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind); +} + void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, const MemRegion* superRegion, Kind k) { ID.AddInteger((unsigned) k); @@ -337,7 +364,7 @@ void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, CanQualType, - const AnalysisContext *AC, + const AnalysisDeclContext *AC, const MemRegion*) { ID.AddInteger(MemRegion::BlockTextRegionKind); ID.AddPointer(BD); @@ -384,6 +411,20 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { } //===----------------------------------------------------------------------===// +// Region anchors. +//===----------------------------------------------------------------------===// + +void GlobalsSpaceRegion::anchor() { } +void HeapSpaceRegion::anchor() { } +void UnknownSpaceRegion::anchor() { } +void StackLocalsSpaceRegion::anchor() { } +void StackArgumentsSpaceRegion::anchor() { } +void TypedRegion::anchor() { } +void TypedValueRegion::anchor() { } +void CodeTextRegion::anchor() { } +void SubRegion::anchor() { } + +//===----------------------------------------------------------------------===// // Region pretty-printing. //===----------------------------------------------------------------------===// @@ -445,16 +486,16 @@ void FieldRegion::dumpToStream(raw_ostream &os) const { os << superRegion << "->" << *getDecl(); } -void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { - os << "NonStaticGlobalSpaceRegion"; -} - void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { os << "ivar{" << superRegion << ',' << *getDecl() << '}'; } void StringRegion::dumpToStream(raw_ostream &os) const { - Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); + Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOpts())); +} + +void ObjCStringRegion::dumpToStream(raw_ostream &os) const { + Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOpts())); } void SymbolicRegion::dumpToStream(raw_ostream &os) const { @@ -477,6 +518,35 @@ void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } +void NonStaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "NonStaticGlobalSpaceRegion"; +} + +void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalInternalSpaceRegion"; +} + +void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalSystemSpaceRegion"; +} + +void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalImmutableSpaceRegion"; +} + +void MemRegion::dumpPretty(raw_ostream &os) const { + return; +} + +void VarRegion::dumpPretty(raw_ostream &os) const { + os << getDecl()->getName(); +} + +void FieldRegion::dumpPretty(raw_ostream &os) const { + superRegion->dumpPretty(os); + os << "->" << getDecl(); +} + //===----------------------------------------------------------------------===// // MemRegionManager methods. //===----------------------------------------------------------------------===// @@ -528,10 +598,18 @@ MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { } const GlobalsSpaceRegion -*MemRegionManager::getGlobalsRegion(const CodeTextRegion *CR) { - if (!CR) - return LazyAllocate(globals); +*MemRegionManager::getGlobalsRegion(MemRegion::Kind K, + const CodeTextRegion *CR) { + if (!CR) { + if (K == MemRegion::GlobalSystemSpaceRegionKind) + return LazyAllocate(SystemGlobals); + if (K == MemRegion::GlobalImmutableSpaceRegionKind) + return LazyAllocate(ImmutableGlobals); + assert(K == MemRegion::GlobalInternalSpaceRegionKind); + return LazyAllocate(InternalGlobals); + } + assert(K == MemRegion::StaticGlobalSpaceRegionKind); StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR]; if (R) return R; @@ -556,18 +634,44 @@ const MemSpaceRegion *MemRegionManager::getCodeRegion() { //===----------------------------------------------------------------------===// // Constructing regions. //===----------------------------------------------------------------------===// - const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){ return getSubRegion<StringRegion>(Str, getGlobalsRegion()); } +const ObjCStringRegion * +MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ + return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion()); +} + const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = 0; - if (D->hasGlobalStorage() && !D->isStaticLocal()) - sReg = getGlobalsRegion(); - else { + if (D->hasGlobalStorage() && !D->isStaticLocal()) { + + // First handle the globals defined in system headers. + if (C.getSourceManager().isInSystemHeader(D->getLocation())) { + // Whitelist the system globals which often DO GET modified, assume the + // rest are immutable. + if (D->getName().find("errno") != StringRef::npos) + sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); + else + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + + // Treat other globals as GlobalInternal unless they are constants. + } else { + QualType GQT = D->getType(); + const Type *GT = GQT.getTypePtrOrNull(); + // TODO: We could walk the complex types here and see if everything is + // constified. + if (GT && GQT.isConstQualified() && GT->isArithmeticType()) + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + else + sReg = getGlobalsRegion(); + } + + // Finally handle static locals. + } else { // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); @@ -585,13 +689,15 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, assert(D->isStaticLocal()); const Decl *D = STC->getDecl(); if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) - sReg = getGlobalsRegion(getFunctionTextRegion(FD)); + sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, + getFunctionTextRegion(FD)); else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { const BlockTextRegion *BTR = getBlockTextRegion(BD, C.getCanonicalType(BD->getSignatureAsWritten()->getType()), - STC->getAnalysisContext()); - sReg = getGlobalsRegion(BTR); + STC->getAnalysisDeclContext()); + sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, + BTR); } else { // FIXME: For ObjC-methods, we need a new CodeTextRegion. For now @@ -614,18 +720,24 @@ const BlockDataRegion * MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, const LocationContext *LC) { const MemRegion *sReg = 0; - - if (LC) { - // FIXME: Once we implement scope handling, we want the parent region - // to be the scope. - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - sReg = getStackLocalsRegion(STC); + const BlockDecl *BD = BC->getDecl(); + if (!BD->hasCaptures()) { + // This handles 'static' blocks. + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); } else { - // We allow 'LC' to be NULL for cases where want BlockDataRegions - // without context-sensitivity. - sReg = getUnknownRegion(); + if (LC) { + // FIXME: Once we implement scope handling, we want the parent region + // to be the scope. + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + else { + // We allow 'LC' to be NULL for cases where want BlockDataRegions + // without context-sensitivity. + sReg = getUnknownRegion(); + } } return getSubRegion<BlockDataRegion>(BC, LC, sReg); @@ -678,7 +790,7 @@ MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { const BlockTextRegion * MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, - AnalysisContext *AC) { + AnalysisDeclContext *AC) { return getSubRegion<BlockTextRegion>(BD, locTy, AC, getCodeRegion()); } @@ -928,8 +1040,8 @@ void BlockDataRegion::LazyInitializeReferencedVars() { if (ReferencedVars) return; - AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); - AnalysisContext::referenced_decls_iterator I, E; + AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext(); + AnalysisDeclContext::referenced_decls_iterator I, E; llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); if (I == E) { diff --git a/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/lib/StaticAnalyzer/Core/ObjCMessage.cpp index 0974fe8..65cdcd9 100644 --- a/lib/StaticAnalyzer/Core/ObjCMessage.cpp +++ b/lib/StaticAnalyzer/Core/ObjCMessage.cpp @@ -13,108 +13,16 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" +#include "clang/AST/DeclCXX.h" using namespace clang; using namespace ento; -QualType ObjCMessage::getType(ASTContext &ctx) const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getType(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (isPropertySetter()) - return ctx.VoidTy; - return propE->getType(); -} - -Selector ObjCMessage::getSelector() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getSelector(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (isPropertySetter()) - return propE->getSetterSelector(); - return propE->getGetterSelector(); -} - -ObjCMethodFamily ObjCMessage::getMethodFamily() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - // Case 1. Explicit message send. - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getMethodFamily(); - - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - - // Case 2. Reference to implicit property. - if (propE->isImplicitProperty()) { - if (isPropertySetter()) - return propE->getImplicitPropertySetter()->getMethodFamily(); - else - return propE->getImplicitPropertyGetter()->getMethodFamily(); - } - - // Case 3. Reference to explicit property. - const ObjCPropertyDecl *prop = propE->getExplicitProperty(); - if (isPropertySetter()) { - if (prop->getSetterMethodDecl()) - return prop->getSetterMethodDecl()->getMethodFamily(); - return prop->getSetterName().getMethodFamily(); - } else { - if (prop->getGetterMethodDecl()) - return prop->getGetterMethodDecl()->getMethodFamily(); - return prop->getGetterName().getMethodFamily(); - } -} - -const ObjCMethodDecl *ObjCMessage::getMethodDecl() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getMethodDecl(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (propE->isImplicitProperty()) - return isPropertySetter() ? propE->getImplicitPropertySetter() - : propE->getImplicitPropertyGetter(); - return 0; -} - -const ObjCInterfaceDecl *ObjCMessage::getReceiverInterface() const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getReceiverInterface(); - const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE); - if (propE->isClassReceiver()) - return propE->getClassReceiver(); - QualType recT; - if (const Expr *recE = getInstanceReceiver()) - recT = recE->getType(); - else { - assert(propE->isSuperReceiver()); - recT = propE->getSuperReceiverType(); - } - if (const ObjCObjectPointerType *Ptr = recT->getAs<ObjCObjectPointerType>()) - return Ptr->getInterfaceDecl(); - return 0; -} - -const Expr *ObjCMessage::getArgExpr(unsigned i) const { - assert(isValid() && "This ObjCMessage is uninitialized!"); - assert(i < getNumArgs() && "Invalid index for argument"); - if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE)) - return msgE->getArg(i); - assert(isPropertySetter()); - if (const BinaryOperator *bop = dyn_cast<BinaryOperator>(OriginE)) - if (bop->isAssignmentOp()) - return bop->getRHS(); - return 0; -} - QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { QualType resultTy; bool isLVal = false; if (isObjCMessage()) { - isLVal = isa<ObjCMessageExpr>(Msg.getOriginExpr()) && - Msg.getOriginExpr()->isLValue(); resultTy = Msg.getResultType(ctx); } else if (const CXXConstructExpr *Ctor = CallE.dyn_cast<const CXXConstructExpr *>()) { @@ -124,7 +32,7 @@ QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const { isLVal = FunctionCall->isLValue(); const Expr *Callee = FunctionCall->getCallee(); - if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl()) + if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl()) resultTy = FD->getResultType(); else resultTy = FunctionCall->getType(); @@ -140,7 +48,7 @@ SVal CallOrObjCMessage::getFunctionCallee() const { assert(isFunctionCall()); assert(!isCXXCall()); const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens(); - return State->getSVal(Fun); + return State->getSVal(Fun, LCtx); } SVal CallOrObjCMessage::getCXXCallee() const { @@ -154,7 +62,7 @@ SVal CallOrObjCMessage::getCXXCallee() const { if (!callee) return UnknownVal(); - return State->getSVal(callee); + return State->getSVal(callee, LCtx); } SVal @@ -162,3 +70,21 @@ CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const { assert(isObjCMessage()); return Msg.getInstanceReceiverSVal(State, LC); } + +const Decl *CallOrObjCMessage::getDecl() const { + if (isCXXCall()) { + const CXXMemberCallExpr *CE = + cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>()); + assert(CE); + return CE->getMethodDecl(); + } else if (isObjCMessage()) { + return Msg.getMethodDecl(); + } else if (isFunctionCall()) { + // In case of a C style call, use the path sensitive information to find + // the function declaration. + SVal CalleeVal = getFunctionCallee(); + return CalleeVal.getAsFunctionDecl(); + } + return 0; +} + diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3a87903..01dd965 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/Basic/SourceManager.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" @@ -24,15 +25,14 @@ using namespace clang; using namespace ento; bool PathDiagnosticMacroPiece::containsEvent() const { - for (const_iterator I = begin(), E = end(); I!=E; ++I) { + for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); + I!=E; ++I) { if (isa<PathDiagnosticEventPiece>(*I)) return true; - if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I)) if (MP->containsEvent()) return true; } - return false; } @@ -52,41 +52,189 @@ PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) PathDiagnosticPiece::~PathDiagnosticPiece() {} PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} +PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {} PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { - for (iterator I = begin(), E = end(); I != E; ++I) delete *I; -} -PathDiagnostic::PathDiagnostic() : Size(0) {} +PathPieces::~PathPieces() {} +PathDiagnostic::~PathDiagnostic() {} + +PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, + StringRef bugtype, StringRef desc, + StringRef category) + : DeclWithIssue(declWithIssue), + BugType(StripTrailingDots(bugtype)), + Desc(StripTrailingDots(desc)), + Category(StripTrailingDots(category)), + path(pathImpl) {} + +void PathDiagnosticConsumer::anchor() { } -PathDiagnostic::~PathDiagnostic() { - for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; +PathDiagnosticConsumer::~PathDiagnosticConsumer() { + // Delete the contents of the FoldingSet if it isn't empty already. + for (llvm::FoldingSet<PathDiagnostic>::iterator it = + Diags.begin(), et = Diags.end() ; it != et ; ++it) { + delete &*it; + } } -void PathDiagnostic::resetPath(bool deletePieces) { - Size = 0; +void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { + llvm::OwningPtr<PathDiagnostic> OwningD(D); + + if (!D || D->path.empty()) + return; + + // We need to flatten the locations (convert Stmt* to locations) because + // the referenced statements may be freed by the time the diagnostics + // are emitted. + D->flattenLocations(); + + // If the PathDiagnosticConsumer does not support diagnostics that + // cross file boundaries, prune out such diagnostics now. + if (!supportsCrossFileDiagnostics()) { + // Verify that the entire path is from the same FileID. + FileID FID; + const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); + llvm::SmallVector<const PathPieces *, 5> WorkList; + WorkList.push_back(&D->path); + + while (!WorkList.empty()) { + const PathPieces &path = *WorkList.back(); + WorkList.pop_back(); + + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I != E; ++I) { + const PathDiagnosticPiece *piece = I->getPtr(); + FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); + + if (FID.isInvalid()) { + FID = SMgr.getFileID(L); + } else if (SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + + // Check the source ranges. + for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), + RE = piece->ranges_end(); + RI != RE; ++RI) { + SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); + if (!L.isFileID() || SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + L = SMgr.getExpansionLoc(RI->getEnd()); + if (!L.isFileID() || SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + } + + if (const PathDiagnosticCallPiece *call = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + WorkList.push_back(&call->path); + } + else if (const PathDiagnosticMacroPiece *macro = + dyn_cast<PathDiagnosticMacroPiece>(piece)) { + WorkList.push_back(¯o->subPieces); + } + } + } + + if (FID.isInvalid()) + return; // FIXME: Emit a warning? + } - if (deletePieces) - for (iterator I=begin(), E=end(); I!=E; ++I) - delete &*I; + // Profile the node to see if we already have something matching it + llvm::FoldingSetNodeID profile; + D->Profile(profile); + void *InsertPos = 0; + + if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { + // Keep the PathDiagnostic with the shorter path. + const unsigned orig_size = orig->full_size(); + const unsigned new_size = D->full_size(); + + if (orig_size <= new_size) { + bool shouldKeepOriginal = true; + if (orig_size == new_size) { + // Here we break ties in a fairly arbitrary, but deterministic, way. + llvm::FoldingSetNodeID fullProfile, fullProfileOrig; + D->FullProfile(fullProfile); + orig->FullProfile(fullProfileOrig); + if (fullProfile.ComputeHash() < fullProfileOrig.ComputeHash()) + shouldKeepOriginal = false; + } - path.clear(); + if (shouldKeepOriginal) + return; + } + Diags.RemoveNode(orig); + delete orig; + } + + Diags.InsertNode(OwningD.take()); } -PathDiagnostic::PathDiagnostic(StringRef bugtype, StringRef desc, - StringRef category) - : Size(0), - BugType(StripTrailingDots(bugtype)), - Desc(StripTrailingDots(desc)), - Category(StripTrailingDots(category)) {} +namespace { +struct CompareDiagnostics { + // Compare if 'X' is "<" than 'Y'. + bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { + // First compare by location + const FullSourceLoc &XLoc = X->getLocation().asLocation(); + const FullSourceLoc &YLoc = Y->getLocation().asLocation(); + if (XLoc < YLoc) + return true; + if (XLoc != YLoc) + return false; + + // Next, compare by bug type. + StringRef XBugType = X->getBugType(); + StringRef YBugType = Y->getBugType(); + if (XBugType < YBugType) + return true; + if (XBugType != YBugType) + return false; + + // Next, compare by bug description. + StringRef XDesc = X->getDescription(); + StringRef YDesc = Y->getDescription(); + if (XDesc < YDesc) + return true; + if (XDesc != YDesc) + return false; + + // FIXME: Further refine by comparing PathDiagnosticPieces? + return false; + } +}; +} -void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) { - // For now this simply forwards to HandlePathDiagnosticImpl. In the future - // we can use this indirection to control for multi-threaded access to - // the PathDiagnosticConsumer from multiple bug reporters. - HandlePathDiagnosticImpl(D); +void +PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { + if (flushed) + return; + + flushed = true; + + std::vector<const PathDiagnostic *> BatchDiags; + for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(), + et = Diags.end(); it != et; ++it) { + BatchDiags.push_back(&*it); + } + + // Clear out the FoldingSet. + Diags.clear(); + + // Sort the diagnostics so that they are always emitted in a deterministic + // order. + if (!BatchDiags.empty()) + std::sort(BatchDiags.begin(), BatchDiags.end(), CompareDiagnostics()); + + FlushDiagnosticsImpl(BatchDiags, Files); + + // Delete the flushed diagnostics. + for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(), + et = BatchDiags.end(); it != et; ++it) { + const PathDiagnostic *D = *it; + delete D; + } } //===----------------------------------------------------------------------===// @@ -94,9 +242,9 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(const PathDiagnostic *D) { //===----------------------------------------------------------------------===// static SourceLocation getValidSourceLocation(const Stmt* S, - LocationOrAnalysisContext LAC) { + LocationOrAnalysisDeclContext LAC) { SourceLocation L = S->getLocStart(); - assert(!LAC.isNull() && "A valid LocationContext or AnalysisContext should " + assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " "be passed to PathDiagnosticLocation upon creation."); // S might be a temporary statement that does not have a location in the @@ -107,7 +255,7 @@ static SourceLocation getValidSourceLocation(const Stmt* S, if (LAC.is<const LocationContext*>()) PM = &LAC.get<const LocationContext*>()->getParentMap(); else - PM = &LAC.get<AnalysisContext*>()->getParentMap(); + PM = &LAC.get<AnalysisDeclContext*>()->getParentMap(); while (!L.isValid()) { S = PM->getParent(S); @@ -127,7 +275,7 @@ PathDiagnosticLocation PathDiagnosticLocation PathDiagnosticLocation::createBegin(const Stmt *S, const SourceManager &SM, - LocationOrAnalysisContext LAC) { + LocationOrAnalysisDeclContext LAC) { return PathDiagnosticLocation(getValidSourceLocation(S, LAC), SM, SingleLocK); } @@ -193,9 +341,6 @@ PathDiagnosticLocation } return PathDiagnosticLocation(S, SMng, P.getLocationContext()); - - if (!S) - return PathDiagnosticLocation(); } PathDiagnosticLocation @@ -212,8 +357,9 @@ PathDiagnosticLocation return PathDiagnosticLocation(PS->getStmt(), SM, LC); else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { const Stmt *Term = BE->getSrc()->getTerminator(); - assert(Term); - return PathDiagnosticLocation(Term, SM, LC); + if (Term) { + return PathDiagnosticLocation(Term, SM, LC); + } } NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); } @@ -229,7 +375,7 @@ PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( FullSourceLoc PathDiagnosticLocation::genLocation(SourceLocation L, - LocationOrAnalysisContext LAC) const { + LocationOrAnalysisDeclContext LAC) const { assert(isValid()); // Note that we want a 'switch' here so that the compiler can warn us in // case we add more cases. @@ -238,9 +384,15 @@ FullSourceLoc case RangeK: break; case StmtK: + // Defensive checking. + if (!S) + break; return FullSourceLoc(getValidSourceLocation(S, LAC), const_cast<SourceManager&>(*SM)); case DeclK: + // Defensive checking. + if (!D) + break; return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); } @@ -248,7 +400,7 @@ FullSourceLoc } PathDiagnosticRange - PathDiagnosticLocation::genRange(LocationOrAnalysisContext LAC) const { + PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { assert(isValid()); // Note that we want a 'switch' here so that the compiler can warn us in // case we add more cases. @@ -321,6 +473,132 @@ void PathDiagnosticLocation::flatten() { } } +PathDiagnosticLocation PathDiagnostic::getLocation() const { + assert(path.size() > 0 && + "getLocation() requires a non-empty PathDiagnostic."); + + PathDiagnosticPiece *p = path.rbegin()->getPtr(); + + while (true) { + if (PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(p)) { + assert(!cp->path.empty()); + p = cp->path.rbegin()->getPtr(); + continue; + } + break; + } + + return p->getLocation(); +} + +//===----------------------------------------------------------------------===// +// Manipulation of PathDiagnosticCallPieces. +//===----------------------------------------------------------------------===// + +static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N, + const SourceManager &SM) { + while (N) { + ProgramPoint PP = N->getLocation(); + if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) + return PathDiagnosticLocation(SP->getStmt(), SM, PP.getLocationContext()); + if (N->pred_empty()) + break; + N = *N->pred_begin(); + } + return PathDiagnosticLocation(); +} + +PathDiagnosticCallPiece * +PathDiagnosticCallPiece::construct(const ExplodedNode *N, + const CallExit &CE, + const SourceManager &SM) { + const Decl *caller = CE.getLocationContext()->getParent()->getDecl(); + PathDiagnosticLocation pos = getLastStmtLoc(N, SM); + return new PathDiagnosticCallPiece(caller, pos); +} + +PathDiagnosticCallPiece * +PathDiagnosticCallPiece::construct(PathPieces &path, + const Decl *caller) { + PathDiagnosticCallPiece *C = new PathDiagnosticCallPiece(path, caller); + path.clear(); + path.push_front(C); + return C; +} + +void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, + const SourceManager &SM) { + const Decl *D = CE.getCalleeContext()->getDecl(); + Callee = D; + callEnter = PathDiagnosticLocation(CE.getCallExpr(), SM, + CE.getLocationContext()); + callEnterWithin = PathDiagnosticLocation::createBegin(D, SM); +} + +IntrusiveRefCntPtr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterEvent() const { + if (!Callee) + return 0; + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + if (isa<BlockDecl>(Callee)) + Out << "Calling anonymous block"; + else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee)) + Out << "Calling '" << *ND << "'"; + StringRef msg = Out.str(); + if (msg.empty()) + return 0; + return new PathDiagnosticEventPiece(callEnter, msg); +} + +IntrusiveRefCntPtr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller)) + Out << "Entered call from '" << *ND << "'"; + else + Out << "Entered call"; + StringRef msg = Out.str(); + if (msg.empty()) + return 0; + return new PathDiagnosticEventPiece(callEnterWithin, msg); +} + +IntrusiveRefCntPtr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallExitEvent() const { + if (NoExit) + return 0; + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + if (!CallStackMessage.empty()) + Out << CallStackMessage; + else if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Callee)) + Out << "Returning from '" << *ND << "'"; + else + Out << "Returning to caller"; + return new PathDiagnosticEventPiece(callReturn, Out.str()); +} + +static void compute_path_size(const PathPieces &pieces, unsigned &size) { + for (PathPieces::const_iterator it = pieces.begin(), + et = pieces.end(); it != et; ++it) { + const PathDiagnosticPiece *piece = it->getPtr(); + if (const PathDiagnosticCallPiece *cp = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + compute_path_size(cp->path, size); + } + else + ++size; + } +} + +unsigned PathDiagnostic::full_size() { + unsigned size = 0; + compute_path_size(path, size); + return size; +} + //===----------------------------------------------------------------------===// // FoldingSet profiling methods. //===----------------------------------------------------------------------===// @@ -343,6 +621,14 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { } } +void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + for (PathPieces::const_iterator it = path.begin(), + et = path.end(); it != et; ++it) { + ID.Add(**it); + } +} + void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticPiece::Profile(ID); ID.Add(Pos); @@ -356,18 +642,114 @@ void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticSpotPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) + for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); + I != E; ++I) ID.Add(**I); } void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Size); + if (!path.empty()) + getLocation().Profile(ID); ID.AddString(BugType); ID.AddString(Desc); ID.AddString(Category); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); - +} + +void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { + Profile(ID); + for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) + ID.Add(**I); for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) ID.AddString(*I); } + +StackHintGenerator::~StackHintGenerator() {} + +std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + ProgramPoint P = N->getLocation(); + const CallExit *CExit = dyn_cast<CallExit>(&P); + assert(CExit && "Stack Hints should be constructed at CallExit points."); + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt()); + if (!CE) + return ""; + + // Get the successor node to make sure the return statement is evaluated and + // CE is set to the result value. + N = *N->succ_begin(); + if (!N) + return getMessageForSymbolNotFound(); + + // Check if one of the parameters are set to the interesting symbol. + ProgramStateRef State = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); + unsigned ArgIndex = 0; + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I, ++ArgIndex){ + SVal SV = State->getSVal(*I, LCtx); + + // Check if the variable corresponding to the symbol is passed by value. + SymbolRef AS = SV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + + // Check if the parameter is a pointer to the symbol. + if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) { + SVal PSV = State->getSVal(Reg->getRegion()); + SymbolRef AS = PSV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + } + } + + // Check if we are returning the interesting symbol. + SVal SV = State->getSVal(CE, LCtx); + SymbolRef RetSym = SV.getAsLocSymbol(); + if (RetSym == Sym) { + return getMessageForReturn(CE); + } + + return getMessageForSymbolNotFound(); +} + +/// TODO: This is copied from clang diagnostics. Maybe we could just move it to +/// some common place. (Same as HandleOrdinalModifier.) +void StackHintGeneratorForSymbol::printOrdinal(unsigned ValNo, + llvm::raw_svector_ostream &Out) { + assert(ValNo != 0 && "ValNo must be strictly positive!"); + + // We could use text forms for the first N ordinals, but the numeric + // forms are actually nicer in diagnostics because they stand out. + Out << ValNo; + + // It is critically important that we do this perfectly for + // user-written sequences with over 100 elements. + switch (ValNo % 100) { + case 11: + case 12: + case 13: + Out << "th"; return; + default: + switch (ValNo % 10) { + case 1: Out << "st"; return; + case 2: Out << "nd"; return; + case 3: Out << "rd"; return; + default: Out << "th"; return; + } + } +} + +std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << Msg << " via "; + // Printed parameters start at 1, not 0. + printOrdinal(++ArgIndex, os); + os << " parameter"; + + return os.str(); +} diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 5ae95c6..323cede 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -25,56 +25,23 @@ using namespace ento; typedef llvm::DenseMap<FileID, unsigned> FIDMap; -namespace { -struct CompareDiagnostics { - // Compare if 'X' is "<" than 'Y'. - bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { - // First compare by location - const FullSourceLoc &XLoc = X->getLocation().asLocation(); - const FullSourceLoc &YLoc = Y->getLocation().asLocation(); - if (XLoc < YLoc) - return true; - if (XLoc != YLoc) - return false; - - // Next, compare by bug type. - StringRef XBugType = X->getBugType(); - StringRef YBugType = Y->getBugType(); - if (XBugType < YBugType) - return true; - if (XBugType != YBugType) - return false; - - // Next, compare by bug description. - StringRef XDesc = X->getDescription(); - StringRef YDesc = Y->getDescription(); - if (XDesc < YDesc) - return true; - if (XDesc != YDesc) - return false; - - // FIXME: Further refine by comparing PathDiagnosticPieces? - return false; - } -}; -} namespace { class PlistDiagnostics : public PathDiagnosticConsumer { - std::vector<const PathDiagnostic*> BatchedDiags; const std::string OutputFile; const LangOptions &LangOpts; - llvm::OwningPtr<PathDiagnosticConsumer> SubPD; + OwningPtr<PathDiagnosticConsumer> SubPD; bool flushed; + const bool SupportsCrossFileDiagnostics; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, + bool supportsMultipleFiles, PathDiagnosticConsumer *subPD); - ~PlistDiagnostics() { FlushDiagnostics(NULL); } + virtual ~PlistDiagnostics() {} - void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); - - void HandlePathDiagnosticImpl(const PathDiagnostic* D); + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "PlistDiagnostics"; @@ -84,18 +51,29 @@ namespace { bool supportsLogicalOpControlFlow() const { return true; } bool supportsAllBlockEdges() const { return true; } virtual bool useVerboseDescription() const { return false; } + virtual bool supportsCrossFileDiagnostics() const { + return SupportsCrossFileDiagnostics; + } }; } // end anonymous namespace PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, + bool supportsMultipleFiles, PathDiagnosticConsumer *subPD) - : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {} + : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} PathDiagnosticConsumer* ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, PathDiagnosticConsumer *subPD) { - return new PlistDiagnostics(s, PP.getLangOptions(), subPD); + return new PlistDiagnostics(s, PP.getLangOpts(), false, subPD); +} + +PathDiagnosticConsumer* +ento::createPlistMultiFileDiagnosticConsumer(const std::string &s, + const Preprocessor &PP) { + return new PlistDiagnostics(s, PP.getLangOpts(), true, 0); } PathDiagnosticConsumer::PathGenerationScheme @@ -167,10 +145,9 @@ static void EmitRange(raw_ostream &o, const SourceManager &SM, Indent(o, indent) << "</array>\n"; } -static raw_ostream &EmitString(raw_ostream &o, - const std::string& s) { +static raw_ostream &EmitString(raw_ostream &o, StringRef s) { o << "<string>"; - for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { + for (StringRef::const_iterator I = s.begin(), E = s.end(); I != E; ++I) { char c = *I; switch (c) { default: o << c; break; @@ -232,7 +209,8 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, - unsigned indent) { + unsigned indent, + unsigned depth) { Indent(o, indent) << "<dict>\n"; ++indent; @@ -258,6 +236,10 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, --indent; Indent(o, indent) << "</array>\n"; } + + // Output the call depth. + Indent(o, indent) << "<key>depth</key>" + << "<integer>" << depth << "</integer>\n"; // Output the text. assert(!P.getString().empty()); @@ -269,108 +251,143 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, // FIXME: Really use a short string. Indent(o, indent) << "<key>message</key>\n"; EmitString(o, P.getString()) << '\n'; - + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; } +static void ReportPiece(raw_ostream &o, + const PathDiagnosticPiece &P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth, + bool includeControlFlow); + +static void ReportCall(raw_ostream &o, + const PathDiagnosticCallPiece &P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth) { + + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = + P.getCallEnterEvent(); + + if (callEnter) + ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true); + + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = + P.getCallEnterWithinCallerEvent(); + + ++depth; + + if (callEnterWithinCaller) + ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, + indent, depth, true); + + for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) + ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); + + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = + P.getCallExitEvent(); + + if (callExit) + ReportPiece(o, *callExit, FM, SM, LangOpts, indent, depth, true); +} + static void ReportMacro(raw_ostream &o, const PathDiagnosticMacroPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, - unsigned indent) { + unsigned indent, + unsigned depth) { - for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); + for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); I!=E; ++I) { - - switch ((*I)->getKind()) { - default: - break; - case PathDiagnosticPiece::Event: - ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, LangOpts, - indent); - break; - case PathDiagnosticPiece::Macro: - ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, LangOpts, - indent); - break; - } + ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, false); } } static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts) { - - unsigned indent = 4; - - switch (P.getKind()) { - case PathDiagnosticPiece::ControlFlow: - ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, - LangOpts, indent); - break; - case PathDiagnosticPiece::Event: - ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, - indent); - break; - case PathDiagnosticPiece::Macro: - ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, - indent); - break; - } + ReportPiece(o, P, FM, SM, LangOpts, 4, 0, true); } -void PlistDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; +static void ReportPiece(raw_ostream &o, + const PathDiagnosticPiece &P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth, + bool includeControlFlow) { + switch (P.getKind()) { + case PathDiagnosticPiece::ControlFlow: + if (includeControlFlow) + ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM, + LangOpts, indent); + break; + case PathDiagnosticPiece::Call: + ReportCall(o, cast<PathDiagnosticCallPiece>(P), FM, SM, LangOpts, + indent, depth); + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, + indent, depth); + break; + case PathDiagnosticPiece::Macro: + ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, + indent, depth); + break; } - - // We need to flatten the locations (convert Stmt* to locations) because - // the referenced statements may be freed by the time the diagnostics - // are emitted. - const_cast<PathDiagnostic*>(D)->flattenLocations(); - BatchedDiags.push_back(D); } -void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> - *FilesMade) { - - if (flushed) - return; - - flushed = true; - - // Sort the diagnostics so that they are always emitted in a deterministic - // order. - if (!BatchedDiags.empty()) - std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics()); - +void PlistDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = 0; - if (!BatchedDiags.empty()) - SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager(); + if (!Diags.empty()) + SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); - for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(), - DE = BatchedDiags.end(); DI != DE; ++DI) { + + for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), + DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) { - AddFID(FM, Fids, SM, I->getLocation().asLocation()); + llvm::SmallVector<const PathPieces *, 5> WorkList; + WorkList.push_back(&D->path); - for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), - RE=I->ranges_end(); RI!=RE; ++RI) { - AddFID(FM, Fids, SM, RI->getBegin()); - AddFID(FM, Fids, SM, RI->getEnd()); + while (!WorkList.empty()) { + const PathPieces &path = *WorkList.back(); + WorkList.pop_back(); + + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I!=E; ++I) { + const PathDiagnosticPiece *piece = I->getPtr(); + AddFID(FM, Fids, SM, piece->getLocation().asLocation()); + + for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), + RE= piece->ranges_end(); RI != RE; ++RI) { + AddFID(FM, Fids, SM, RI->getBegin()); + AddFID(FM, Fids, SM, RI->getEnd()); + } + + if (const PathDiagnosticCallPiece *call = + dyn_cast<PathDiagnosticCallPiece>(piece)) { + WorkList.push_back(&call->path); + } + else if (const PathDiagnosticMacroPiece *macro = + dyn_cast<PathDiagnosticMacroPiece>(piece)) { + WorkList.push_back(¯o->subPieces); + } } } } @@ -379,7 +396,7 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> std::string ErrMsg; llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); if (!ErrMsg.empty()) { - llvm::errs() << "warning: could not creat file: " << OutputFile << '\n'; + llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; return; } @@ -406,21 +423,19 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> " <key>diagnostics</key>\n" " <array>\n"; - for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(), - DE = BatchedDiags.end(); DI!=DE; ++DI) { + for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), + DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; - // Create an owning smart pointer for 'D' just so that we auto-free it - // when we exit this method. - llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D)); o << " <array>\n"; - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) - ReportDiag(o, *I, FM, *SM, LangOpts); + for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + I != E; ++I) + ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; @@ -431,6 +446,38 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; + + // Output information about the semantic context where + // the issue occurred. + if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { + // FIXME: handle blocks, which have no name. + if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { + StringRef declKind; + switch (ND->getKind()) { + case Decl::CXXRecord: + declKind = "C++ class"; + break; + case Decl::CXXMethod: + declKind = "C++ method"; + break; + case Decl::ObjCMethod: + declKind = "Objective-C method"; + break; + case Decl::Function: + declKind = "function"; + break; + default: + break; + } + if (!declKind.empty()) { + const std::string &declName = ND->getDeclName().getAsString(); + o << " <key>issue_context_kind</key>"; + EmitString(o, declKind) << '\n'; + o << " <key>issue_context</key>"; + EmitString(o, declName) << '\n'; + } + } + } // Output the location of the bug. o << " <key>location</key>\n"; @@ -438,9 +485,10 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> // Output the diagnostic to the sub-diagnostic client, if any. if (SubPD) { - SubPD->HandlePathDiagnostic(OwnedD.take()); + std::vector<const PathDiagnostic *> SubDiags; + SubDiags.push_back(D); SmallVector<std::string, 1> SubFilesMade; - SubPD->FlushDiagnostics(SubFilesMade); + SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade); if (!SubFilesMade.empty()) { o << " <key>" << SubPD->getName() << "_files</key>\n"; @@ -462,6 +510,4 @@ void PlistDiagnostics::FlushDiagnostics(SmallVectorImpl<std::string> if (FilesMade) FilesMade->push_back(OutputFile); - - BatchedDiags.clear(); } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 73788cc..b9cfa27 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -24,6 +25,26 @@ using namespace ento; // FIXME: Move this elsewhere. ConstraintManager::~ConstraintManager() {} +namespace clang { namespace ento { +/// Increments the number of times this state is referenced. + +void ProgramStateRetain(const ProgramState *state) { + ++const_cast<ProgramState*>(state)->refCount; +} + +/// Decrement the number of times this state is referenced. +void ProgramStateRelease(const ProgramState *state) { + assert(state->refCount > 0); + ProgramState *s = const_cast<ProgramState*>(state); + if (--s->refCount == 0) { + ProgramStateManager &Mgr = s->getStateManager(); + Mgr.StateSet.RemoveNode(s); + s->~ProgramState(); + Mgr.freeStates.push_back(s); + } +} +}} + ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env, StoreRef st, GenericDataMap gdm) : stateMgr(mgr), @@ -55,8 +76,8 @@ ProgramStateManager::~ProgramStateManager() { I->second.second(I->second.first); } -const ProgramState* -ProgramStateManager::removeDeadBindings(const ProgramState *state, +ProgramStateRef +ProgramStateManager::removeDeadBindings(ProgramStateRef state, const StackFrameContext *LCtx, SymbolReaper& SymReaper) { @@ -79,7 +100,7 @@ ProgramStateManager::removeDeadBindings(const ProgramState *state, return getPersistentState(NewState); } -const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state, +ProgramStateRef ProgramStateManager::MarshalState(ProgramStateRef state, const StackFrameContext *InitLoc) { // make up an empty state for now. ProgramState State(this, @@ -90,7 +111,7 @@ const ProgramState *ProgramStateManager::MarshalState(const ProgramState *state, return getPersistentState(State); } -const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL, +ProgramStateRef ProgramState::bindCompoundLiteral(const CompoundLiteralExpr *CL, const LocationContext *LC, SVal V) const { const StoreRef &newStore = @@ -98,21 +119,21 @@ const ProgramState *ProgramState::bindCompoundLiteral(const CompoundLiteralExpr return makeWithStore(newStore); } -const ProgramState *ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const { +ProgramStateRef ProgramState::bindDecl(const VarRegion* VR, SVal IVal) const { const StoreRef &newStore = getStateManager().StoreMgr->BindDecl(getStore(), VR, IVal); return makeWithStore(newStore); } -const ProgramState *ProgramState::bindDeclWithNoInit(const VarRegion* VR) const { +ProgramStateRef ProgramState::bindDeclWithNoInit(const VarRegion* VR) const { const StoreRef &newStore = getStateManager().StoreMgr->BindDeclWithNoInit(getStore(), VR); return makeWithStore(newStore); } -const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const { +ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V) const { ProgramStateManager &Mgr = getStateManager(); - const ProgramState *newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), + ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V)); const MemRegion *MR = LV.getAsRegion(); if (MR && Mgr.getOwningEngine()) @@ -121,53 +142,55 @@ const ProgramState *ProgramState::bindLoc(Loc LV, SVal V) const { return newState; } -const ProgramState *ProgramState::bindDefault(SVal loc, SVal V) const { +ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { ProgramStateManager &Mgr = getStateManager(); const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); - const ProgramState *new_state = makeWithStore(newStore); + ProgramStateRef new_state = makeWithStore(newStore); return Mgr.getOwningEngine() ? Mgr.getOwningEngine()->processRegionChange(new_state, R) : new_state; } -const ProgramState * +ProgramStateRef ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, StoreManager::InvalidatedSymbols *IS, - bool invalidateGlobals) const { + const CallOrObjCMessage *Call) const { if (!IS) { StoreManager::InvalidatedSymbols invalidated; - return invalidateRegionsImpl(Regions, E, Count, - invalidated, invalidateGlobals); + return invalidateRegionsImpl(Regions, E, Count, LCtx, + invalidated, Call); } - return invalidateRegionsImpl(Regions, E, Count, *IS, invalidateGlobals); + return invalidateRegionsImpl(Regions, E, Count, LCtx, *IS, Call); } -const ProgramState * +ProgramStateRef ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, StoreManager::InvalidatedSymbols &IS, - bool invalidateGlobals) const { + const CallOrObjCMessage *Call) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); if (Eng && Eng->wantsRegionChangeUpdate(this)) { StoreManager::InvalidatedRegions Invalidated; const StoreRef &newStore - = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS, - invalidateGlobals, &Invalidated); - const ProgramState *newState = makeWithStore(newStore); - return Eng->processRegionChanges(newState, &IS, Regions, Invalidated); + = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, + Call, &Invalidated); + ProgramStateRef newState = makeWithStore(newStore); + return Eng->processRegionChanges(newState, &IS, Regions, Invalidated, Call); } const StoreRef &newStore = - Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, IS, - invalidateGlobals, NULL); + Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, + Call, NULL); return makeWithStore(newStore); } -const ProgramState *ProgramState::unbindLoc(Loc LV) const { +ProgramStateRef ProgramState::unbindLoc(Loc LV) const { assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead."); Store OldStore = getStore(); @@ -179,9 +202,11 @@ const ProgramState *ProgramState::unbindLoc(Loc LV) const { return makeWithStore(newStore); } -const ProgramState *ProgramState::enterStackFrame(const StackFrameContext *frame) const { +ProgramStateRef +ProgramState::enterStackFrame(const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) const { const StoreRef &new_store = - getStateManager().StoreMgr->enterStackFrame(this, frame); + getStateManager().StoreMgr->enterStackFrame(this, callerCtx, calleeCtx); return makeWithStore(new_store); } @@ -238,9 +263,12 @@ SVal ProgramState::getSVal(Loc location, QualType T) const { return V; } -const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidate) const{ - Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V, - Invalidate); +ProgramStateRef ProgramState::BindExpr(const Stmt *S, + const LocationContext *LCtx, + SVal V, bool Invalidate) const{ + Environment NewEnv = + getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V, + Invalidate); if (NewEnv == Env) return this; @@ -249,10 +277,14 @@ const ProgramState *ProgramState::BindExpr(const Stmt *S, SVal V, bool Invalidat return getStateManager().getPersistentState(NewSt); } -const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal location, - SVal V) const { +ProgramStateRef +ProgramState::bindExprAndLocation(const Stmt *S, const LocationContext *LCtx, + SVal location, + SVal V) const { Environment NewEnv = - getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V); + getStateManager().EnvMgr.bindExprAndLocation(Env, + EnvironmentEntry(S, LCtx), + location, V); if (NewEnv == Env) return this; @@ -262,9 +294,10 @@ const ProgramState *ProgramState::bindExprAndLocation(const Stmt *S, SVal locati return getStateManager().getPersistentState(NewSt); } -const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, +ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, DefinedOrUnknownSVal UpperBound, - bool Assumption) const { + bool Assumption, + QualType indexTy) const { if (Idx.isUnknown() || UpperBound.isUnknown()) return this; @@ -278,7 +311,8 @@ const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, // Get the offset: the minimum value of the array index type. BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); // FIXME: This should be using ValueManager::ArrayindexTy...somehow. - QualType indexTy = Ctx.IntTy; + if (indexTy.isNull()) + indexTy = Ctx.IntTy; nonloc::ConcreteInt Min(BVF.getMinValue(indexTy)); // Adjust the index. @@ -307,7 +341,7 @@ const ProgramState *ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, return CM.assume(this, cast<DefinedSVal>(inBound), Assumption); } -const ProgramState *ProgramStateManager::getInitialState(const LocationContext *InitLoc) { +ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { ProgramState State(this, EnvMgr.getInitialEnvironment(), StoreMgr->getInitialStore(InitLoc), @@ -316,28 +350,15 @@ const ProgramState *ProgramStateManager::getInitialState(const LocationContext * return getPersistentState(State); } -void ProgramStateManager::recycleUnusedStates() { - for (std::vector<ProgramState*>::iterator i = recentlyAllocatedStates.begin(), - e = recentlyAllocatedStates.end(); i != e; ++i) { - ProgramState *state = *i; - if (state->referencedByExplodedNode()) - continue; - StateSet.RemoveNode(state); - freeStates.push_back(state); - state->~ProgramState(); - } - recentlyAllocatedStates.clear(); -} - -const ProgramState *ProgramStateManager::getPersistentStateWithGDM( - const ProgramState *FromState, - const ProgramState *GDMState) { - ProgramState NewState = *FromState; +ProgramStateRef ProgramStateManager::getPersistentStateWithGDM( + ProgramStateRef FromState, + ProgramStateRef GDMState) { + ProgramState NewState(*FromState); NewState.GDM = GDMState->GDM; return getPersistentState(NewState); } -const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) { +ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) { llvm::FoldingSetNodeID ID; State.Profile(ID); @@ -356,12 +377,11 @@ const ProgramState *ProgramStateManager::getPersistentState(ProgramState &State) } new (newState) ProgramState(State); StateSet.InsertNode(newState, InsertPos); - recentlyAllocatedStates.push_back(newState); return newState; } -const ProgramState *ProgramState::makeWithStore(const StoreRef &store) const { - ProgramState NewSt = *this; +ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const { + ProgramState NewSt(*this); NewSt.setStore(store); return getStateManager().getPersistentState(NewSt); } @@ -379,92 +399,44 @@ void ProgramState::setStore(const StoreRef &newStore) { // State pretty-printing. //===----------------------------------------------------------------------===// -static bool IsEnvLoc(const Stmt *S) { - // FIXME: This is a layering violation. Should be in environment. - return (bool) (((uintptr_t) S) & 0x1); -} - -void ProgramState::print(raw_ostream &Out, CFG &C, +void ProgramState::print(raw_ostream &Out, const char *NL, const char *Sep) const { // Print the store. ProgramStateManager &Mgr = getStateManager(); Mgr.getStoreManager().print(getStore(), Out, NL, Sep); - // Print Subexpression bindings. - bool isFirst = true; + // Print out the environment. + Env.print(Out, NL, Sep); - // FIXME: All environment printing should be moved inside Environment. - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (C.isBlkExpr(I.getKey()) || IsEnvLoc(I.getKey())) - continue; - - if (isFirst) { - Out << NL << NL << "Sub-Expressions:" << NL; - isFirst = false; - } else { - Out << NL; - } + // Print out the constraints. + Mgr.getConstraintManager().print(this, Out, NL, Sep); - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } + // Print checker-specific data. + Mgr.getOwningEngine()->printState(Out, this, NL, Sep); +} - // Print block-expression bindings. - isFirst = true; +void ProgramState::printDOT(raw_ostream &Out) const { + print(Out, "\\l", "\\|"); +} - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (!C.isBlkExpr(I.getKey())) - continue; +void ProgramState::dump() const { + print(llvm::errs()); +} - if (isFirst) { - Out << NL << NL << "Block-level Expressions:" << NL; - isFirst = false; - } else { - Out << NL; - } +void ProgramState::printTaint(raw_ostream &Out, + const char *NL, const char *Sep) const { + TaintMapImpl TM = get<TaintMap>(); - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } - - // Print locations. - isFirst = true; - - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (!IsEnvLoc(I.getKey())) - continue; - - if (isFirst) { - Out << NL << NL << "Load/store locations:" << NL; - isFirst = false; - } else { - Out << NL; - } + if (!TM.isEmpty()) + Out <<"Tainted Symbols:" << NL; - const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1)); - - Out << " (" << (void*) S << ") "; - LangOptions LO; // FIXME. - S->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); + for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { + Out << I->first << " : " << I->second << NL; } - - Mgr.getConstraintManager().print(this, Out, NL, Sep); - - // Print checker-specific data. - Mgr.getOwningEngine()->printState(Out, this, NL, Sep); -} - -void ProgramState::printDOT(raw_ostream &Out, CFG &C) const { - print(Out, C, "\\l", "\\|"); } -void ProgramState::printStdErr(CFG &C) const { - print(llvm::errs(), C); +void ProgramState::dumpTaint() const { + printTaint(llvm::errs()); } //===----------------------------------------------------------------------===// @@ -489,7 +461,7 @@ ProgramStateManager::FindGDMContext(void *K, return p.first; } -const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Key, void *Data){ +ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, void *Data){ ProgramState::GenericDataMap M1 = St->getGDM(); ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); @@ -501,7 +473,7 @@ const ProgramState *ProgramStateManager::addGDM(const ProgramState *St, void *Ke return getPersistentState(NewSt); } -const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, void *Key) { +ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) { ProgramState::GenericDataMap OldM = state->getGDM(); ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); @@ -513,6 +485,8 @@ const ProgramState *ProgramStateManager::removeGDM(const ProgramState *state, vo return getPersistentState(NewState); } +void ScanReachableSymbols::anchor() { } + bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) if (!scan(*I)) @@ -527,10 +501,10 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { return true; isVisited = 1; - if (const SymbolData *sData = dyn_cast<SymbolData>(sym)) - if (!visitor.VisitSymbol(sData)) - return false; + if (!visitor.VisitSymbol(sym)) + return false; + // TODO: should be rewritten using SymExpr::symbol_iterator. switch (sym->getKind()) { case SymExpr::RegionValueKind: case SymExpr::ConjuredKind: @@ -538,8 +512,12 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { case SymExpr::ExtentKind: case SymExpr::MetadataKind: break; + case SymExpr::CastSymbolKind: + return scan(cast<SymbolCast>(sym)->getOperand()); case SymExpr::SymIntKind: return scan(cast<SymIntExpr>(sym)->getLHS()); + case SymExpr::IntSymKind: + return scan(cast<IntSymExpr>(sym)->getRHS()); case SymExpr::SymSymKind: { const SymSymExpr *x = cast<SymSymExpr>(sym); return scan(x->getLHS()) && scan(x->getRHS()); @@ -575,6 +553,10 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { if (isVisited) return true; isVisited = 1; + + + if (!visitor.VisitMemRegion(R)) + return false; // If this is a symbolic region, visit the symbol for the region. if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) @@ -623,3 +605,105 @@ bool ProgramState::scanReachableSymbols(const MemRegion * const *I, } return true; } + +ProgramStateRef ProgramState::addTaint(const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind) const { + if (const Expr *E = dyn_cast_or_null<Expr>(S)) + S = E->IgnoreParens(); + + SymbolRef Sym = getSVal(S, LCtx).getAsSymbol(); + if (Sym) + return addTaint(Sym, Kind); + + const MemRegion *R = getSVal(S, LCtx).getAsRegion(); + addTaint(R, Kind); + + // Cannot add taint, so just return the state. + return this; +} + +ProgramStateRef ProgramState::addTaint(const MemRegion *R, + TaintTagType Kind) const { + if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) + return addTaint(SR->getSymbol(), Kind); + return this; +} + +ProgramStateRef ProgramState::addTaint(SymbolRef Sym, + TaintTagType Kind) const { + // If this is a symbol cast, remove the cast before adding the taint. Taint + // is cast agnostic. + while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) + Sym = SC->getOperand(); + + ProgramStateRef NewState = set<TaintMap>(Sym, Kind); + assert(NewState); + return NewState; +} + +bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx, + TaintTagType Kind) const { + if (const Expr *E = dyn_cast_or_null<Expr>(S)) + S = E->IgnoreParens(); + + SVal val = getSVal(S, LCtx); + return isTainted(val, Kind); +} + +bool ProgramState::isTainted(SVal V, TaintTagType Kind) const { + if (const SymExpr *Sym = V.getAsSymExpr()) + return isTainted(Sym, Kind); + if (const MemRegion *Reg = V.getAsRegion()) + return isTainted(Reg, Kind); + return false; +} + +bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const { + if (!Reg) + return false; + + // Element region (array element) is tainted if either the base or the offset + // are tainted. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) + return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) + return isTainted(SR->getSymbol(), K); + + if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) + return isTainted(ER->getSuperRegion(), K); + + return false; +} + +bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { + if (!Sym) + return false; + + // Traverse all the symbols this symbol depends on to see if any are tainted. + bool Tainted = false; + for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); + SI != SE; ++SI) { + assert(isa<SymbolData>(*SI)); + const TaintTagType *Tag = get<TaintMap>(*SI); + Tainted = (Tag && *Tag == Kind); + + // If this is a SymbolDerived with a tainted parent, it's also tainted. + if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) + Tainted = Tainted || isTainted(SD->getParentSymbol(), Kind); + + // If memory region is tainted, data is also tainted. + if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) + Tainted = Tainted || isTainted(SRV->getRegion(), Kind); + + // If If this is a SymbolCast from a tainted value, it's also tainted. + if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) + Tainted = Tainted || isTainted(SC->getOperand(), Kind); + + if (Tainted) + return true; + } + + return Tainted; +} diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 9337788..98eb958 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -204,46 +204,46 @@ struct ProgramStateTrait<ConstraintRange> namespace { class RangeConstraintManager : public SimpleConstraintManager{ - RangeSet GetRange(const ProgramState *state, SymbolRef sym); + RangeSet GetRange(ProgramStateRef state, SymbolRef sym); public: RangeConstraintManager(SubEngine &subengine) : SimpleConstraintManager(subengine) {} - const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym, + ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment); - const llvm::APSInt* getSymVal(const ProgramState *St, SymbolRef sym) const; + const llvm::APSInt* getSymVal(ProgramStateRef St, SymbolRef sym) const; // FIXME: Refactor into SimpleConstraintManager? - bool isEqual(const ProgramState *St, SymbolRef sym, const llvm::APSInt& V) const { + bool isEqual(ProgramStateRef St, SymbolRef sym, const llvm::APSInt& V) const { const llvm::APSInt *i = getSymVal(St, sym); return i ? *i == V : false; } - const ProgramState *removeDeadBindings(const ProgramState *St, SymbolReaper& SymReaper); + ProgramStateRef removeDeadBindings(ProgramStateRef St, SymbolReaper& SymReaper); - void print(const ProgramState *St, raw_ostream &Out, + void print(ProgramStateRef St, raw_ostream &Out, const char* nl, const char *sep); private: @@ -257,7 +257,7 @@ ConstraintManager* ento::CreateRangeConstraintManager(ProgramStateManager&, return new RangeConstraintManager(subeng); } -const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St, +const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, SymbolRef sym) const { const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym); return T ? T->getConcreteValue() : NULL; @@ -265,8 +265,8 @@ const llvm::APSInt* RangeConstraintManager::getSymVal(const ProgramState *St, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -const ProgramState* -RangeConstraintManager::removeDeadBindings(const ProgramState *state, +ProgramStateRef +RangeConstraintManager::removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper) { ConstraintRangeTy CR = state->get<ConstraintRange>(); @@ -282,7 +282,7 @@ RangeConstraintManager::removeDeadBindings(const ProgramState *state, } RangeSet -RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) { +RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym)) return *V; @@ -305,8 +305,8 @@ RangeConstraintManager::GetRange(const ProgramState *state, SymbolRef sym) { // As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1, // UINT_MAX, 0, 1, and 2. -const ProgramState* -RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -322,8 +322,8 @@ RangeConstraintManager::assumeSymNE(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { // [Int-Adjustment, Int-Adjustment] @@ -333,8 +333,8 @@ RangeConstraintManager::assumeSymEQ(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -354,8 +354,8 @@ RangeConstraintManager::assumeSymLT(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -375,8 +375,8 @@ RangeConstraintManager::assumeSymGT(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -397,8 +397,8 @@ RangeConstraintManager::assumeSymGE(const ProgramState *state, SymbolRef sym, return New.isEmpty() ? NULL : state->set<ConstraintRange>(sym, New); } -const ProgramState* -RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym, +ProgramStateRef +RangeConstraintManager::assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, const llvm::APSInt& Adjustment) { BasicValueFactory &BV = state->getBasicVals(); @@ -423,18 +423,20 @@ RangeConstraintManager::assumeSymLE(const ProgramState *state, SymbolRef sym, // Pretty-printing. //===------------------------------------------------------------------------===/ -void RangeConstraintManager::print(const ProgramState *St, raw_ostream &Out, +void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, const char* nl, const char *sep) { ConstraintRangeTy Ranges = St->get<ConstraintRange>(); - if (Ranges.isEmpty()) + if (Ranges.isEmpty()) { + Out << nl << sep << "Ranges are empty." << nl; return; + } - Out << nl << sep << "ranges of symbol values:"; - + Out << nl << sep << "Ranges of symbol values:"; for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){ Out << nl << ' ' << I.getKey() << " : "; I.getData().print(Out); } + Out << nl; } diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 4b76cf1..cc3ea8c3 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -20,6 +20,7 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -228,6 +229,16 @@ public: /// For DerivedToBase casts, create a CXXBaseObjectRegion and return it. virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType); + /// \brief Evaluates C++ dynamic_cast cast. + /// The callback may result in the following 3 scenarios: + /// - Successful cast (ex: derived is subclass of base). + /// - Failed cast (ex: derived is definitely not a subclass of base). + /// - We don't know (base is a symbolic region and we don't have + /// enough info to determine if the cast will succeed at run time). + /// The function returns an SVal representing the derived class; it's + /// valid only if Failed flag is set to false. + virtual SVal evalDynamicCast(SVal base, QualType derivedPtrType,bool &Failed); + StoreRef getInitialStore(const LocationContext *InitLoc) { return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this); } @@ -235,11 +246,18 @@ public: //===-------------------------------------------------------------------===// // Binding values to regions. //===-------------------------------------------------------------------===// + RegionBindings invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindings B, + InvalidatedRegions *Invalidated); StoreRef invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, InvalidatedSymbols &IS, - bool invalidateGlobals, + const CallOrObjCMessage *Call, InvalidatedRegions *Invalidated); public: // Made public for helper classes. @@ -273,7 +291,8 @@ public: // Part of public interface to class. RegionBindings B = GetRegionBindings(store); assert(!lookup(B, R, BindingKey::Default)); assert(!lookup(B, R, BindingKey::Direct)); - return StoreRef(addBinding(B, R, BindingKey::Default, V).getRootWithoutRetain(), *this); + return StoreRef(addBinding(B, R, BindingKey::Default, V) + .getRootWithoutRetain(), *this); } StoreRef BindCompoundLiteral(Store store, const CompoundLiteralExpr *CL, @@ -308,12 +327,10 @@ public: // Part of public interface to class. bool includedInBindings(Store store, const MemRegion *region) const; - //===------------------------------------------------------------------===// - // Loading values from regions. - //===------------------------------------------------------------------===// - + /// \brief Return the value bound to specified location in a given state. + /// /// The high level logic for this method is this: - /// Retrieve (L) + /// getBinding (L) /// if L has binding /// return L's binding /// else if L is in killset @@ -323,39 +340,39 @@ public: // Part of public interface to class. /// return undefined /// else /// return symbolic - SVal Retrieve(Store store, Loc L, QualType T = QualType()); + SVal getBinding(Store store, Loc L, QualType T = QualType()); - SVal RetrieveElement(Store store, const ElementRegion *R); + SVal getBindingForElement(Store store, const ElementRegion *R); - SVal RetrieveField(Store store, const FieldRegion *R); + SVal getBindingForField(Store store, const FieldRegion *R); - SVal RetrieveObjCIvar(Store store, const ObjCIvarRegion *R); + SVal getBindingForObjCIvar(Store store, const ObjCIvarRegion *R); - SVal RetrieveVar(Store store, const VarRegion *R); + SVal getBindingForVar(Store store, const VarRegion *R); - SVal RetrieveLazySymbol(const TypedValueRegion *R); + SVal getBindingForLazySymbol(const TypedValueRegion *R); - SVal RetrieveFieldOrElementCommon(Store store, const TypedValueRegion *R, - QualType Ty, const MemRegion *superR); + SVal getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R, + QualType Ty, const MemRegion *superR); - SVal RetrieveLazyBinding(const MemRegion *lazyBindingRegion, - Store lazyBindingStore); + SVal getLazyBinding(const MemRegion *lazyBindingRegion, + Store lazyBindingStore); - /// Retrieve the values in a struct and return a CompoundVal, used when doing - /// struct copy: + /// Get bindings for the values in a struct and return a CompoundVal, used + /// when doing struct copy: /// struct s x, y; /// x = y; /// y's value is retrieved by this method. - SVal RetrieveStruct(Store store, const TypedValueRegion* R); + SVal getBindingForStruct(Store store, const TypedValueRegion* R); - SVal RetrieveArray(Store store, const TypedValueRegion* R); + SVal getBindingForArray(Store store, const TypedValueRegion* R); /// Used to lazily generate derived symbols for bindings that are defined /// implicitly by default bindings in a super region. - Optional<SVal> RetrieveDerivedDefaultValue(RegionBindings B, - const MemRegion *superR, - const TypedValueRegion *R, - QualType Ty); + Optional<SVal> getBindingForDerivedDefaultValue(RegionBindings B, + const MemRegion *superR, + const TypedValueRegion *R, + QualType Ty); /// Get the state and region whose binding this region R corresponds to. std::pair<Store, const MemRegion*> @@ -374,15 +391,16 @@ public: // Part of public interface to class. StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper); - StoreRef enterStackFrame(const ProgramState *state, - const StackFrameContext *frame); + StoreRef enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx); //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// // FIXME: This method will soon be eliminated; see the note in Store.h. - DefinedOrUnknownSVal getSizeInElements(const ProgramState *state, + DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state, const MemRegion* R, QualType EleTy); //===------------------------------------------------------------------===// @@ -422,7 +440,8 @@ StoreManager *ento::CreateRegionStoreManager(ProgramStateManager& StMgr) { return new RegionStoreManager(StMgr, F); } -StoreManager *ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { +StoreManager * +ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = minimal_features_tag(); F.enableFields(true); return new RegionStoreManager(StMgr, F); @@ -587,6 +606,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> { const Expr *Ex; unsigned Count; + const LocationContext *LCtx; StoreManager::InvalidatedSymbols &IS; StoreManager::InvalidatedRegions *Regions; public: @@ -594,11 +614,12 @@ public: ProgramStateManager &stateMgr, RegionBindings b, const Expr *ex, unsigned count, + const LocationContext *lctx, StoreManager::InvalidatedSymbols &is, StoreManager::InvalidatedRegions *r, bool includeGlobals) : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), - Ex(ex), Count(count), IS(is), Regions(r) {} + Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {} void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); void VisitBaseRegion(const MemRegion *baseR); @@ -674,7 +695,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = - svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count); + svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); B = RM.addBinding(B, baseR, BindingKey::Default, V); return; } @@ -690,7 +711,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = - svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count); + svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); B = RM.addBinding(B, baseR, BindingKey::Default, V); return; } @@ -698,7 +719,8 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { if (const ArrayType *AT = Ctx.getAsArrayType(T)) { // Set the default value of the array to conjured symbol. DefinedOrUnknownSVal V = - svalBuilder.getConjuredSymbolVal(baseR, Ex, AT->getElementType(), Count); + svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, + AT->getElementType(), Count); B = RM.addBinding(B, baseR, BindingKey::Default, V); return; } @@ -713,20 +735,47 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { } - DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, T, Count); + DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, LCtx, + T,Count); assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); B = RM.addBinding(B, baseR, BindingKey::Direct, V); } +RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindings B, + InvalidatedRegions *Invalidated) { + // Bind the globals memory space to a new symbol that we will use to derive + // the bindings for all globals. + const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K); + SVal V = + svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, Ex, LCtx, + /* symbol type, doesn't matter */ Ctx.IntTy, + Count); + + B = removeBinding(B, GS); + B = addBinding(B, BindingKey::Make(GS, BindingKey::Default), V); + + // Even if there are no bindings in the global scope, we still need to + // record that we touched it. + if (Invalidated) + Invalidated->push_back(GS); + + return B; +} + StoreRef RegionStoreManager::invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions, const Expr *Ex, unsigned Count, + const LocationContext *LCtx, InvalidatedSymbols &IS, - bool invalidateGlobals, + const CallOrObjCMessage *Call, InvalidatedRegions *Invalidated) { invalidateRegionsWorker W(*this, StateMgr, RegionStoreManager::GetRegionBindings(store), - Ex, Count, IS, Invalidated, invalidateGlobals); + Ex, Count, LCtx, IS, Invalidated, false); // Scan the bindings and generate the clusters. W.GenerateClusters(); @@ -741,20 +790,20 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, // Return the new bindings. RegionBindings B = W.getRegionBindings(); - if (invalidateGlobals) { - // Bind the non-static globals memory space to a new symbol that we will - // use to derive the bindings for all non-static globals. - const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(); - SVal V = - svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, Ex, - /* symbol type, doesn't matter */ Ctx.IntTy, - Count); - B = addBinding(B, BindingKey::Make(GS, BindingKey::Default), V); - - // Even if there are no bindings in the global scope, we still need to - // record that we touched it. - if (Invalidated) - Invalidated->push_back(GS); + // For all globals which are not static nor immutable: determine which global + // regions should be invalidated and invalidate them. + // TODO: This could possibly be more precise with modules. + // + // System calls invalidate only system globals. + if (Call && Call->isInSystemHeader()) { + B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + // Internal calls might invalidate both system and internal globals. + } else { + B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); } return StoreRef(B.getRootWithoutRetain(), *this); @@ -764,9 +813,10 @@ StoreRef RegionStoreManager::invalidateRegions(Store store, // Extents for regions. //===----------------------------------------------------------------------===// -DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const ProgramState *state, - const MemRegion *R, - QualType EleTy) { +DefinedOrUnknownSVal +RegionStoreManager::getSizeInElements(ProgramStateRef state, + const MemRegion *R, + QualType EleTy) { SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder); const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); if (!SizeInt) @@ -837,6 +887,75 @@ SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) { return loc::MemRegionVal(baseReg); } +SVal RegionStoreManager::evalDynamicCast(SVal base, QualType derivedType, + bool &Failed) { + Failed = false; + + loc::MemRegionVal *baseRegVal = dyn_cast<loc::MemRegionVal>(&base); + if (!baseRegVal) + return UnknownVal(); + const MemRegion *BaseRegion = baseRegVal->stripCasts(); + + // Assume the derived class is a pointer or a reference to a CXX record. + derivedType = derivedType->getPointeeType(); + assert(!derivedType.isNull()); + const CXXRecordDecl *DerivedDecl = derivedType->getAsCXXRecordDecl(); + if (!DerivedDecl && !derivedType->isVoidType()) + return UnknownVal(); + + // Drill down the CXXBaseObject chains, which represent upcasts (casts from + // derived to base). + const MemRegion *SR = BaseRegion; + while (const TypedRegion *TSR = dyn_cast_or_null<TypedRegion>(SR)) { + QualType BaseType = TSR->getLocationType()->getPointeeType(); + assert(!BaseType.isNull()); + const CXXRecordDecl *SRDecl = BaseType->getAsCXXRecordDecl(); + if (!SRDecl) + return UnknownVal(); + + // If found the derived class, the cast succeeds. + if (SRDecl == DerivedDecl) + return loc::MemRegionVal(TSR); + + // If the region type is a subclass of the derived type. + if (!derivedType->isVoidType() && SRDecl->isDerivedFrom(DerivedDecl)) { + // This occurs in two cases. + // 1) We are processing an upcast. + // 2) We are processing a downcast but we jumped directly from the + // ancestor to a child of the cast value, so conjure the + // appropriate region to represent value (the intermediate node). + return loc::MemRegionVal(MRMgr.getCXXBaseObjectRegion(DerivedDecl, + BaseRegion)); + } + + // If super region is not a parent of derived class, the cast definitely + // fails. + if (!derivedType->isVoidType() && + DerivedDecl->isProvablyNotDerivedFrom(SRDecl)) { + Failed = true; + return UnknownVal(); + } + + if (const CXXBaseObjectRegion *R = dyn_cast<CXXBaseObjectRegion>(TSR)) + // Drill down the chain to get the derived classes. + SR = R->getSuperRegion(); + else { + // We reached the bottom of the hierarchy. + + // If this is a cast to void*, return the region. + if (derivedType->isVoidType()) + return loc::MemRegionVal(TSR); + + // We did not find the derived class. We we must be casting the base to + // derived, so the cast should fail. + Failed = true; + return UnknownVal(); + } + } + + return UnknownVal(); +} + //===----------------------------------------------------------------------===// // Loading values from regions. //===----------------------------------------------------------------------===// @@ -863,7 +982,7 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, return Optional<SVal>(); } -SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { +SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { assert(!isa<UnknownVal>(L) && "location unknown"); assert(!isa<UndefinedVal>(L) && "location undefined"); @@ -882,18 +1001,20 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion(); - if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR)) { + if (isa<AllocaRegion>(MR) || + isa<SymbolicRegion>(MR) || + isa<CodeTextRegion>(MR)) { if (T.isNull()) { - const SymbolicRegion *SR = cast<SymbolicRegion>(MR); - T = SR->getSymbol()->getType(Ctx); + if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR)) + T = TR->getLocationType(); + else { + const SymbolicRegion *SR = cast<SymbolicRegion>(MR); + T = SR->getSymbol()->getType(Ctx); + } } MR = GetElementZeroRegion(MR, T); } - if (isa<CodeTextRegion>(MR)) { - llvm_unreachable("Why load from a code text region?"); - } - // FIXME: Perhaps this method should just take a 'const MemRegion*' argument // instead of 'Loc', and have the other Loc cases handled at a higher level. const TypedValueRegion *R = cast<TypedValueRegion>(MR); @@ -909,21 +1030,21 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // Such funny addressing will occur due to layering of regions. if (RTy->isStructureOrClassType()) - return RetrieveStruct(store, R); + return getBindingForStruct(store, R); // FIXME: Handle unions. if (RTy->isUnionType()) return UnknownVal(); if (RTy->isArrayType()) - return RetrieveArray(store, R); + return getBindingForArray(store, R); // FIXME: handle Vector types. if (RTy->isVectorType()) return UnknownVal(); if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return CastRetrievedVal(RetrieveField(store, FR), FR, T, false); + return CastRetrievedVal(getBindingForField(store, FR), FR, T, false); if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { // FIXME: Here we actually perform an implicit conversion from the loaded @@ -931,7 +1052,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // more intelligently. For example, an 'element' can encompass multiple // bound regions (e.g., several bound bytes), or could be a subset of // a larger value. - return CastRetrievedVal(RetrieveElement(store, ER), ER, T, false); + return CastRetrievedVal(getBindingForElement(store, ER), ER, T, false); } if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { @@ -941,7 +1062,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // reinterpretted, it is possible we stored a different value that could // fit within the ivar. Either we need to cast these when storing them // or reinterpret them lazily (as we do here). - return CastRetrievedVal(RetrieveObjCIvar(store, IVR), IVR, T, false); + return CastRetrievedVal(getBindingForObjCIvar(store, IVR), IVR, T, false); } if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { @@ -951,7 +1072,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // variable is reinterpretted, it is possible we stored a different value // that could fit within the variable. Either we need to cast these when // storing them or reinterpret them lazily (as we do here). - return CastRetrievedVal(RetrieveVar(store, VR), VR, T, false); + return CastRetrievedVal(getBindingForVar(store, VR), VR, T, false); } RegionBindings B = GetRegionBindings(store); @@ -1021,7 +1142,7 @@ RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, return std::make_pair((Store) 0, (const MemRegion *) 0); } -SVal RegionStoreManager::RetrieveElement(Store store, +SVal RegionStoreManager::getBindingForElement(Store store, const ElementRegion* R) { // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); @@ -1043,15 +1164,15 @@ SVal RegionStoreManager::RetrieveElement(Store store, if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { int64_t i = CI->getValue().getSExtValue(); // Abort on string underrun. This can be possible by arbitrary - // clients of RetrieveElement(). + // clients of getBindingForElement(). if (i < 0) return UndefinedVal(); - int64_t byteLength = Str->getByteLength(); - // Technically, only i == byteLength is guaranteed to be null. + int64_t length = Str->getLength(); + // Technically, only i == length is guaranteed to be null. // However, such overflows should be caught before reaching this point; // the only time such an access would be made is if a string literal was // used to initialize a larger array. - char c = (i >= byteLength) ? '\0' : Str->getString()[i]; + char c = (i >= length) ? '\0' : Str->getCodeUnit(i); return svalBuilder.makeIntVal(c, T); } } @@ -1093,10 +1214,11 @@ SVal RegionStoreManager::RetrieveElement(Store store, } } } - return RetrieveFieldOrElementCommon(store, R, R->getElementType(), superR); + return getBindingForFieldOrElementCommon(store, R, R->getElementType(), + superR); } -SVal RegionStoreManager::RetrieveField(Store store, +SVal RegionStoreManager::getBindingForField(Store store, const FieldRegion* R) { // Check if the region has a binding. @@ -1105,14 +1227,14 @@ SVal RegionStoreManager::RetrieveField(Store store, return *V; QualType Ty = R->getValueType(); - return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); + return getBindingForFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); } Optional<SVal> -RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, - const MemRegion *superR, - const TypedValueRegion *R, - QualType Ty) { +RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B, + const MemRegion *superR, + const TypedValueRegion *R, + QualType Ty) { if (const Optional<SVal> &D = getDefaultBinding(B, superR)) { const SVal &val = D.getValue(); @@ -1135,30 +1257,39 @@ RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, return Optional<SVal>(); } -SVal RegionStoreManager::RetrieveLazyBinding(const MemRegion *lazyBindingRegion, +SVal RegionStoreManager::getLazyBinding(const MemRegion *lazyBindingRegion, Store lazyBindingStore) { if (const ElementRegion *ER = dyn_cast<ElementRegion>(lazyBindingRegion)) - return RetrieveElement(lazyBindingStore, ER); + return getBindingForElement(lazyBindingStore, ER); - return RetrieveField(lazyBindingStore, - cast<FieldRegion>(lazyBindingRegion)); + return getBindingForField(lazyBindingStore, + cast<FieldRegion>(lazyBindingRegion)); } -SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, +SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R, QualType Ty, const MemRegion *superR) { - // At this point we have already checked in either RetrieveElement or - // RetrieveField if 'R' has a direct binding. - + // At this point we have already checked in either getBindingForElement or + // getBindingForField if 'R' has a direct binding. RegionBindings B = GetRegionBindings(store); + + // Record whether or not we see a symbolic index. That can completely + // be out of scope of our lookup. + bool hasSymbolicIndex = false; while (superR) { if (const Optional<SVal> &D = - RetrieveDerivedDefaultValue(B, superR, R, Ty)) + getBindingForDerivedDefaultValue(B, superR, R, Ty)) return *D; + if (const ElementRegion *ER = dyn_cast<ElementRegion>(superR)) { + NonLoc index = ER->getIndex(); + if (!index.isConstant()) + hasSymbolicIndex = true; + } + // If our super region is a field or element itself, walk up the region // hierarchy to see if there is a default value installed in an ancestor. if (const SubRegion *SR = dyn_cast<SubRegion>(superR)) { @@ -1174,10 +1305,10 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R); if (lazyBindingRegion) - return RetrieveLazyBinding(lazyBindingRegion, lazyBindingStore); + return getLazyBinding(lazyBindingRegion, lazyBindingStore); if (R->hasStackNonParametersStorage()) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + if (isa<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. if (const TypedValueRegion *typedSuperR = @@ -1185,13 +1316,15 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, if (typedSuperR->getValueType()->isVectorType()) return UnknownVal(); } - - // FIXME: We also need to take ElementRegions with symbolic indexes into - // account. - if (!ER->getIndex().isConstant()) - return UnknownVal(); } + // FIXME: We also need to take ElementRegions with symbolic indexes into + // account. This case handles both directly accessing an ElementRegion + // with a symbolic offset, but also fields within an element with + // a symbolic offset. + if (hasSymbolicIndex) + return UnknownVal(); + return UndefinedVal(); } @@ -1199,7 +1332,8 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){ +SVal RegionStoreManager::getBindingForObjCIvar(Store store, + const ObjCIvarRegion* R) { // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); @@ -1218,10 +1352,10 @@ SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){ return UnknownVal(); } - return RetrieveLazySymbol(R); + return getBindingForLazySymbol(R); } -SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { +SVal RegionStoreManager::getBindingForVar(Store store, const VarRegion *R) { // Check if the region has a binding. RegionBindings B = GetRegionBindings(store); @@ -1253,7 +1387,8 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { } } - if (const Optional<SVal> &V = RetrieveDerivedDefaultValue(B, MS, R, CT)) + if (const Optional<SVal> &V + = getBindingForDerivedDefaultValue(B, MS, R, CT)) return V.getValue(); return svalBuilder.getRegionValueSymbolVal(R); @@ -1270,19 +1405,18 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { return UndefinedVal(); } -SVal RegionStoreManager::RetrieveLazySymbol(const TypedValueRegion *R) { +SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { // All other values are symbolic. return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::RetrieveStruct(Store store, +SVal RegionStoreManager::getBindingForStruct(Store store, const TypedValueRegion* R) { - QualType T = R->getValueType(); - assert(T->isStructureOrClassType()); + assert(R->getValueType()->isStructureOrClassType()); return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); } -SVal RegionStoreManager::RetrieveArray(Store store, +SVal RegionStoreManager::getBindingForArray(Store store, const TypedValueRegion * R) { assert(Ctx.getAsConstantArrayType(R->getValueType())); return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); @@ -1506,11 +1640,15 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, RecordDecl::field_iterator FI, FE; StoreRef newStore(store, *this); - for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) { + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) break; + // Skip any unnamed bitfields to stay in sync with the initializers. + if ((*FI)->isUnnamedBitfield()) + continue; + QualType FTy = (*FI)->getType(); const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); @@ -1520,6 +1658,7 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, newStore = BindStruct(newStore.getStore(), FR, *VI); else newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(FR), *VI); + ++VI; } // There may be fewer values in the initialize list than the fields of struct. @@ -1556,7 +1695,7 @@ StoreRef RegionStoreManager::KillStruct(Store store, const TypedRegion* R, // Remove the old bindings, using 'subReg' as the root of all regions // we will invalidate. RegionBindings B = GetRegionBindings(store); - llvm::OwningPtr<RegionStoreSubRegionMap> + OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(store)); RemoveSubRegionBindings(B, subReg, *SubRegions); @@ -1574,7 +1713,7 @@ StoreRef RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, // Nuke the old bindings stemming from R. RegionBindings B = GetRegionBindings(store); - llvm::OwningPtr<RegionStoreSubRegionMap> + OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(store)); // B and DVM are updated after the call to RemoveSubRegionBindings. @@ -1641,7 +1780,8 @@ class removeDeadBindingsWorker : const StackFrameContext *CurrentLCtx; public: - removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, + removeDeadBindingsWorker(RegionStoreManager &rm, + ProgramStateManager &stateMgr, RegionBindings b, SymbolReaper &symReaper, const StackFrameContext *LCtx) : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b, @@ -1717,9 +1857,9 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) { if (const MemRegion *R = V.getAsRegion()) AddToWorkList(R); - // Update the set of live symbols. - for (SVal::symbol_iterator SI=V.symbol_begin(), SE=V.symbol_end(); - SI!=SE;++SI) + // Update the set of live symbols. + for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); + SI!=SE; ++SI) SymReaper.markLive(*SI); } @@ -1807,7 +1947,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, SymReaper.maybeDead(SymR->getSymbol()); SVal X = I.getData(); - SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); for (; SI != SE; ++SI) SymReaper.maybeDead(*SI); } @@ -1816,37 +1956,41 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, } -StoreRef RegionStoreManager::enterStackFrame(const ProgramState *state, - const StackFrameContext *frame) { - FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl()); +StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) +{ + FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl()); FunctionDecl::param_const_iterator PI = FD->param_begin(), PE = FD->param_end(); StoreRef store = StoreRef(state->getStore(), *this); - if (CallExpr const *CE = dyn_cast<CallExpr>(frame->getCallSite())) { + if (CallExpr const *CE = dyn_cast<CallExpr>(calleeCtx->getCallSite())) { CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); // Copy the arg expression value to the arg variables. We check that // PI != PE because the actual number of arguments may be different than // the function declaration. for (; AI != AE && PI != PE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI); + SVal ArgVal = state->getSVal(*AI, callerCtx); store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal); + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), + ArgVal); } } else if (const CXXConstructExpr *CE = - dyn_cast<CXXConstructExpr>(frame->getCallSite())) { + dyn_cast<CXXConstructExpr>(calleeCtx->getCallSite())) { CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); // Copy the arg expression value to the arg variables. for (; AI != AE; ++AI, ++PI) { - SVal ArgVal = state->getSVal(*AI); + SVal ArgVal = state->getSVal(*AI, callerCtx); store = Bind(store.getStore(), - svalBuilder.makeLoc(MRMgr.getVarRegion(*PI,frame)), ArgVal); + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), + ArgVal); } } else - assert(isa<CXXDestructorDecl>(frame->getDecl())); + assert(isa<CXXDestructorDecl>(calleeCtx->getDecl())); return store; } diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index ebf7ae2..9e97f5e 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprCXX.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" @@ -25,6 +26,8 @@ using namespace ento; // Basic SVal creation. //===----------------------------------------------------------------------===// +void SValBuilder::anchor() { } + DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { if (Loc::isLocType(type)) return makeNull(); @@ -37,23 +40,38 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { return UnknownVal(); } - NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& rhs, QualType type) { // The Environment ensures we always get a persistent APSInt in // BasicValueFactory, so we don't need to get the APSInt from // BasicValueFactory again. + assert(lhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType type) { + assert(rhs); assert(!Loc::isLocType(type)); - return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, rhs, type)); + return nonloc::SymbolVal(SymMgr.getIntSymExpr(lhs, op, rhs, type)); } NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type) { - assert(SymMgr.getType(lhs) == SymMgr.getType(rhs)); + assert(lhs && rhs); + assert(haveSameType(lhs->getType(Context), rhs->getType(Context)) == true); assert(!Loc::isLocType(type)); - return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); + return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); } +NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, + QualType fromTy, QualType toTy) { + assert(operand); + assert(!Loc::isLocType(toTy)); + return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy)); +} SVal SValBuilder::convertToArrayIndex(SVal val) { if (val.isUnknownOrUndef()) @@ -69,6 +87,10 @@ SVal SValBuilder::convertToArrayIndex(SVal val) { return evalCastFromNonLoc(cast<NonLoc>(val), ArrayIndexTy); } +nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ + return makeTruthVal(boolean->getValue()); +} + DefinedOrUnknownSVal SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { QualType T = region->getValueType(); @@ -84,35 +106,46 @@ SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { return nonloc::SymbolVal(sym); } -DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *symbolTag, - const Expr *expr, - unsigned count) { +DefinedOrUnknownSVal +SValBuilder::getConjuredSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + unsigned count) { QualType T = expr->getType(); + return getConjuredSymbolVal(symbolTag, expr, LCtx, T, count); +} - if (!SymbolManager::canSymbolicate(T)) +DefinedOrUnknownSVal +SValBuilder::getConjuredSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + QualType type, + unsigned count) { + if (!SymbolManager::canSymbolicate(type)) return UnknownVal(); - SymbolRef sym = SymMgr.getConjuredSymbol(expr, count, symbolTag); + SymbolRef sym = SymMgr.getConjuredSymbol(expr, LCtx, type, count, symbolTag); - if (Loc::isLocType(T)) + if (Loc::isLocType(type)) return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); return nonloc::SymbolVal(sym); } -DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *symbolTag, - const Expr *expr, - QualType type, - unsigned count) { - + +DefinedOrUnknownSVal +SValBuilder::getConjuredSymbolVal(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount) { if (!SymbolManager::canSymbolicate(type)) return UnknownVal(); - SymbolRef sym = SymMgr.getConjuredSymbol(expr, type, count, symbolTag); - + SymbolRef sym = SymMgr.getConjuredSymbol(stmt, LCtx, type, visitCount); + if (Loc::isLocType(type)) return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - + return nonloc::SymbolVal(sym); } @@ -155,14 +188,41 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, CanQualType locTy, const LocationContext *locContext) { const BlockTextRegion *BC = - MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisContext()); + MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisDeclContext()); const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext); return loc::MemRegionVal(BD); } //===----------------------------------------------------------------------===// -SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op, +SVal SValBuilder::makeGenericVal(ProgramStateRef State, + BinaryOperator::Opcode Op, + NonLoc LHS, NonLoc RHS, + QualType ResultTy) { + // If operands are tainted, create a symbol to ensure that we propagate taint. + if (State->isTainted(RHS) || State->isTainted(LHS)) { + const SymExpr *symLHS; + const SymExpr *symRHS; + + if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) { + symLHS = LHS.getAsSymExpr(); + return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); + } + + if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) { + symRHS = RHS.getAsSymExpr(); + return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); + } + + symLHS = LHS.getAsSymExpr(); + symRHS = RHS.getAsSymExpr(); + return makeNonLoc(symLHS, Op, symRHS, ResultTy); + } + return UnknownVal(); +} + + +SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type) { if (lhs.isUndef() || rhs.isUndef()) @@ -190,37 +250,50 @@ SVal SValBuilder::evalBinOp(const ProgramState *state, BinaryOperator::Opcode op return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type); } -DefinedOrUnknownSVal SValBuilder::evalEQ(const ProgramState *state, +DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs) { return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs, Context.IntTy)); } +/// Recursively check if the pointer types are equal modulo const, volatile, +/// and restrict qualifiers. Assumes the input types are canonical. +/// TODO: This is based off of code in SemaCast; can we reuse it. +static bool haveSimilarTypes(ASTContext &Context, QualType T1, + QualType T2) { + while (Context.UnwrapSimilarPointerTypes(T1, T2)) { + Qualifiers Quals1, Quals2; + T1 = Context.getUnqualifiedArrayType(T1, Quals1); + T2 = Context.getUnqualifiedArrayType(T2, Quals2); + + // Make sure that non cvr-qualifiers the other qualifiers (e.g., address + // spaces) are identical. + Quals1.removeCVRQualifiers(); + Quals2.removeCVRQualifiers(); + if (Quals1 != Quals2) + return false; + } + + if (T1 != T2) + return false; + + return true; +} + // FIXME: should rewrite according to the cast kind. SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { + castTy = Context.getCanonicalType(castTy); + originalTy = Context.getCanonicalType(originalTy); if (val.isUnknownOrUndef() || castTy == originalTy) return val; // For const casts, just propagate the value. if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) - if (Context.hasSameUnqualifiedType(castTy, originalTy)) + if (haveSimilarTypes(Context, Context.getPointerType(castTy), + Context.getPointerType(originalTy))) return val; - - // Check for casts to real or complex numbers. We don't handle these at all - // right now. - if (castTy->isFloatingType() || castTy->isAnyComplexType()) - return UnknownVal(); - // Check for casts from integers to integers. - if (castTy->isIntegerType() && originalTy->isIntegerType()) { - if (isa<Loc>(val)) - // This can be a cast to ObjC property of type int. - return evalCastFromLoc(cast<Loc>(val), castTy); - else - return evalCastFromNonLoc(cast<NonLoc>(val), castTy); - } - // Check for casts from pointers to integers. if (castTy->isIntegerType() && Loc::isLocType(originalTy)) return evalCastFromLoc(cast<Loc>(val), castTy); @@ -235,7 +308,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { } return LV->getLoc(); } - goto DispatchCast; + return dispatchCast(val, castTy); } // Just pass through function and block pointers. @@ -309,8 +382,5 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { return R ? SVal(loc::MemRegionVal(R)) : UnknownVal(); } -DispatchCast: - // All other cases. - return isa<Loc>(val) ? evalCastFromLoc(cast<Loc>(val), castTy) - : evalCastFromNonLoc(cast<NonLoc>(val), castTy); + return dispatchCast(val, castTy); } diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index b5980b9..b94aff4 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -54,13 +54,16 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { return CTR->getDecl(); } - return NULL; + return 0; } -/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and -/// wraps a symbol, return that SymbolRef. Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +/// \brief If this SVal is a location (subclasses Loc) and wraps a symbol, +/// return that SymbolRef. Otherwise return 0. +/// +/// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element +/// region. If that is the case, gets the underlining region. SymbolRef SVal::getAsLocSymbol() const { + // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) return X->getLoc().getAsLocSymbol(); @@ -69,7 +72,7 @@ SymbolRef SVal::getAsLocSymbol() const { if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) return SymR->getSymbol(); } - return NULL; + return 0; } /// Get the symbol in the SVal or its base region. @@ -91,29 +94,34 @@ SymbolRef SVal::getLocSymbolInBase() const { return 0; } -/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. +// TODO: The next 3 functions have to be simplified. + +/// \brief If this SVal wraps a symbol return that SymbolRef. /// Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? SymbolRef SVal::getAsSymbol() const { + // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) return X->getSymbol(); - if (const nonloc::SymExprVal *X = dyn_cast<nonloc::SymExprVal>(this)) - if (SymbolRef Y = dyn_cast<SymbolData>(X->getSymbolicExpression())) - return Y; - return getAsLocSymbol(); } /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then /// return that expression. Otherwise return NULL. const SymExpr *SVal::getAsSymbolicExpression() const { - if (const nonloc::SymExprVal *X = dyn_cast<nonloc::SymExprVal>(this)) - return X->getSymbolicExpression(); + if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) + return X->getSymbol(); return getAsSymbol(); } +const SymExpr* SVal::getAsSymExpr() const { + const SymExpr* Sym = getAsSymbol(); + if (!Sym) + Sym = getAsSymbolicExpression(); + return Sym; +} + const MemRegion *SVal::getAsRegion() const { if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) return X->getRegion(); @@ -130,50 +138,6 @@ const MemRegion *loc::MemRegionVal::stripCasts() const { return R ? R->StripCasts() : NULL; } -bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const { - return itr == X.itr; -} - -bool SVal::symbol_iterator::operator!=(const symbol_iterator &X) const { - return itr != X.itr; -} - -SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) { - itr.push_back(SE); - while (!isa<SymbolData>(itr.back())) expand(); -} - -SVal::symbol_iterator &SVal::symbol_iterator::operator++() { - assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); - assert(isa<SymbolData>(itr.back())); - itr.pop_back(); - if (!itr.empty()) - while (!isa<SymbolData>(itr.back())) expand(); - return *this; -} - -SymbolRef SVal::symbol_iterator::operator*() { - assert(!itr.empty() && "attempting to dereference an 'end' iterator"); - return cast<SymbolData>(itr.back()); -} - -void SVal::symbol_iterator::expand() { - const SymExpr *SE = itr.back(); - itr.pop_back(); - - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { - itr.push_back(SIE->getLHS()); - return; - } - else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { - itr.push_back(SSE->getLHS()); - itr.push_back(SSE->getRHS()); - return; - } - - llvm_unreachable("unhandled expansion case"); -} - const void *nonloc::LazyCompoundVal::getStore() const { return static_cast<const LazyCompoundValData*>(Data)->getStore(); } @@ -281,8 +245,6 @@ void SVal::dumpToStream(raw_ostream &os) const { case UndefinedKind: os << "Undefined"; break; - default: - assert (false && "Invalid SVal."); } } @@ -298,13 +260,8 @@ void NonLoc::dumpToStream(raw_ostream &os) const { << C.getValue().getBitWidth() << 'b'; break; } - case nonloc::SymbolValKind: - os << '$' << cast<nonloc::SymbolVal>(this)->getSymbol(); - break; - case nonloc::SymExprValKind: { - const nonloc::SymExprVal& C = *cast<nonloc::SymExprVal>(this); - const SymExpr *SE = C.getSymbolicExpression(); - os << SE; + case nonloc::SymbolValKind: { + os << cast<nonloc::SymbolVal>(this)->getSymbol(); break; } case nonloc::LocAsIntegerKind: { diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 79d8b8b..a76a2da 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -23,11 +23,9 @@ namespace ento { SimpleConstraintManager::~SimpleConstraintManager() {} bool SimpleConstraintManager::canReasonAbout(SVal X) const { - if (nonloc::SymExprVal *SymVal = dyn_cast<nonloc::SymExprVal>(&X)) { - const SymExpr *SE = SymVal->getSymbolicExpression(); - - if (isa<SymbolData>(SE)) - return true; + nonloc::SymbolVal *SymVal = dyn_cast<nonloc::SymbolVal>(&X); + if (SymVal && SymVal->isExpression()) { + const SymExpr *SE = SymVal->getSymbol(); if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { switch (SIE->getOpcode()) { @@ -56,7 +54,7 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { return true; } -const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption) { if (isa<NonLoc>(Cond)) @@ -65,13 +63,13 @@ const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, return assume(state, cast<Loc>(Cond), Assumption); } -const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, Loc cond, +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond, bool assumption) { state = assumeAux(state, cond, assumption); return SU.processAssume(state, cond, assumption); } -const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state, +ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, Loc Cond, bool Assumption) { BasicValueFactory &BasicVals = state->getBasicVals(); @@ -113,7 +111,7 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state } // end switch } -const ProgramState *SimpleConstraintManager::assume(const ProgramState *state, +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, NonLoc cond, bool assumption) { state = assumeAux(state, cond, assumption); @@ -135,16 +133,29 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { } } -const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state, + +ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol( + ProgramStateRef State, + SymbolRef Sym, + bool Assumption) { + QualType T = State->getSymbolManager().getType(Sym); + const llvm::APSInt &zero = State->getBasicVals().getValue(0, T); + if (Assumption) + return assumeSymNE(State, Sym, zero, zero); + else + return assumeSymEQ(State, Sym, zero, zero); +} + +ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, NonLoc Cond, bool Assumption) { - // We cannot reason about SymSymExprs, - // and can only reason about some SymIntExprs. + // We cannot reason about SymSymExprs, and can only reason about some + // SymIntExprs. if (!canReasonAbout(Cond)) { - // Just return the current state indicating that the path is feasible. - // This may be an over-approximation of what is possible. - return state; + // Just add the constraint to the expression without trying to simplify. + SymbolRef sym = Cond.getAsSymExpr(); + return assumeAuxForSymbol(state, sym, Assumption); } BasicValueFactory &BasicVals = state->getBasicVals(); @@ -157,37 +168,33 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state case nonloc::SymbolValKind: { nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond); SymbolRef sym = SV.getSymbol(); - QualType T = SymMgr.getType(sym); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - if (Assumption) - return assumeSymNE(state, sym, zero, zero); - else - return assumeSymEQ(state, sym, zero, zero); - } + assert(sym); + + // Handle SymbolData. + if (!SV.isExpression()) { + return assumeAuxForSymbol(state, sym, Assumption); + + // Handle symbolic expression. + } else { + // We can only simplify expressions whose RHS is an integer. + const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym); + if (!SE) + return assumeAuxForSymbol(state, sym, Assumption); + + BinaryOperator::Opcode op = SE->getOpcode(); + // Implicitly compare non-comparison expressions to 0. + if (!BinaryOperator::isComparisonOp(op)) { + QualType T = SymMgr.getType(SE); + const llvm::APSInt &zero = BasicVals.getValue(0, T); + op = (Assumption ? BO_NE : BO_EQ); + return assumeSymRel(state, SE, op, zero); + } + // From here on out, op is the real comparison we'll be testing. + if (!Assumption) + op = NegateComparison(op); - case nonloc::SymExprValKind: { - nonloc::SymExprVal V = cast<nonloc::SymExprVal>(Cond); - - // For now, we only handle expressions whose RHS is an integer. - // All other expressions are assumed to be feasible. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(V.getSymbolicExpression()); - if (!SE) - return state; - - BinaryOperator::Opcode op = SE->getOpcode(); - // Implicitly compare non-comparison expressions to 0. - if (!BinaryOperator::isComparisonOp(op)) { - QualType T = SymMgr.getType(SE); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - op = (Assumption ? BO_NE : BO_EQ); - return assumeSymRel(state, SE, op, zero); + return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); } - - // From here on out, op is the real comparison we'll be testing. - if (!Assumption) - op = NegateComparison(op); - - return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); } case nonloc::ConcreteIntKind: { @@ -202,55 +209,52 @@ const ProgramState *SimpleConstraintManager::assumeAux(const ProgramState *state } // end switch } -const ProgramState *SimpleConstraintManager::assumeSymRel(const ProgramState *state, +static llvm::APSInt computeAdjustment(const SymExpr *LHS, + SymbolRef &Sym) { + llvm::APSInt DefaultAdjustment; + DefaultAdjustment = 0; + + // First check if the LHS is a simple symbol reference. + if (isa<SymbolData>(LHS)) + return DefaultAdjustment; + + // Next, see if it's a "($sym+constant1)" expression. + const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS); + + // We cannot simplify "($sym1+$sym2)". + if (!SE) + return DefaultAdjustment; + + // Get the constant out of the expression "($sym+constant1)" or + // "<expr>+constant1". + Sym = SE->getLHS(); + switch (SE->getOpcode()) { + case BO_Add: + return SE->getRHS(); + case BO_Sub: + return -SE->getRHS(); + default: + // We cannot simplify non-additive operators. + return DefaultAdjustment; + } +} + +ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int) { assert(BinaryOperator::isComparisonOp(op) && "Non-comparison ops should be rewritten as comparisons to zero."); - // We only handle simple comparisons of the form "$sym == constant" - // or "($sym+constant1) == constant2". - // The adjustment is "constant1" in the above expression. It's used to - // "slide" the solution range around for modular arithmetic. For example, - // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which - // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to - // the subclasses of SimpleConstraintManager to handle the adjustment. - llvm::APSInt Adjustment; - - // First check if the LHS is a simple symbol reference. - SymbolRef Sym = dyn_cast<SymbolData>(LHS); - if (Sym) { - Adjustment = 0; - } else { - // Next, see if it's a "($sym+constant1)" expression. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(LHS); - - // We don't handle "($sym1+$sym2)". - // Give up and assume the constraint is feasible. - if (!SE) - return state; - - // We don't handle "(<expr>+constant1)". - // Give up and assume the constraint is feasible. - Sym = dyn_cast<SymbolData>(SE->getLHS()); - if (!Sym) - return state; - - // Get the constant out of the expression "($sym+constant1)". - switch (SE->getOpcode()) { - case BO_Add: - Adjustment = SE->getRHS(); - break; - case BO_Sub: - Adjustment = -SE->getRHS(); - break; - default: - // We don't handle non-additive operators. - // Give up and assume the constraint is feasible. - return state; - } - } + // We only handle simple comparisons of the form "$sym == constant" + // or "($sym+constant1) == constant2". + // The adjustment is "constant1" in the above expression. It's used to + // "slide" the solution range around for modular arithmetic. For example, + // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which + // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to + // the subclasses of SimpleConstraintManager to handle the adjustment. + SymbolRef Sym = LHS; + llvm::APSInt Adjustment = computeAdjustment(LHS, Sym); // FIXME: This next section is a hack. It silently converts the integers to // be of the same type as the symbol, which is not always correct. Really the diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index d4295d4..e082d9d 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -31,16 +31,14 @@ public: // Common implementation for the interface provided by ConstraintManager. //===------------------------------------------------------------------===// - bool canReasonAbout(SVal X) const; - - const ProgramState *assume(const ProgramState *state, DefinedSVal Cond, + ProgramStateRef assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption); - const ProgramState *assume(const ProgramState *state, Loc Cond, bool Assumption); + ProgramStateRef assume(ProgramStateRef state, Loc Cond, bool Assumption); - const ProgramState *assume(const ProgramState *state, NonLoc Cond, bool Assumption); + ProgramStateRef assume(ProgramStateRef state, NonLoc Cond, bool Assumption); - const ProgramState *assumeSymRel(const ProgramState *state, + ProgramStateRef assumeSymRel(ProgramStateRef state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int); @@ -53,27 +51,27 @@ protected: // Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison // operation for the method being invoked. - virtual const ProgramState *assumeSymNE(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymEQ(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymLT(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymGT(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymLE(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; - virtual const ProgramState *assumeSymGE(const ProgramState *state, SymbolRef sym, + virtual ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; @@ -81,9 +79,19 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// - const ProgramState *assumeAux(const ProgramState *state, Loc Cond,bool Assumption); + bool canReasonAbout(SVal X) const; + + ProgramStateRef assumeAux(ProgramStateRef state, + Loc Cond, + bool Assumption); + + ProgramStateRef assumeAux(ProgramStateRef state, + NonLoc Cond, + bool Assumption); - const ProgramState *assumeAux(const ProgramState *state, NonLoc Cond, bool Assumption); + ProgramStateRef assumeAuxForSymbol(ProgramStateRef State, + SymbolRef Sym, + bool Assumption); }; } // end GR namespace diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index bd63ecf..d0558f1 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -20,6 +20,7 @@ using namespace ento; namespace { class SimpleSValBuilder : public SValBuilder { protected: + virtual SVal dispatchCast(SVal val, QualType castTy); virtual SVal evalCastFromNonLoc(NonLoc val, QualType castTy); virtual SVal evalCastFromLoc(Loc val, QualType castTy); @@ -31,16 +32,16 @@ public: virtual SVal evalMinus(NonLoc val); virtual SVal evalComplement(NonLoc val); - virtual SVal evalBinOpNN(const ProgramState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy); - virtual SVal evalBinOpLL(const ProgramState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy); - virtual SVal evalBinOpLN(const ProgramState *state, BinaryOperator::Opcode op, + virtual SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy); /// getKnownValue - evaluates a given SVal. If the SVal has only one possible /// (integer) value, that value is returned. Otherwise, returns NULL. - virtual const llvm::APSInt *getKnownValue(const ProgramState *state, SVal V); + virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V); SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, QualType resultTy); @@ -57,6 +58,12 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, // Transfer function for Casts. //===----------------------------------------------------------------------===// +SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) { + assert(isa<Loc>(&Val) || isa<NonLoc>(&Val)); + return isa<Loc>(Val) ? evalCastFromLoc(cast<Loc>(Val), CastTy) + : evalCastFromNonLoc(cast<NonLoc>(Val), CastTy); +} + SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { bool isLocType = Loc::isLocType(castTy); @@ -74,25 +81,27 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { if (const SymExpr *se = val.getAsSymbolicExpression()) { QualType T = Context.getCanonicalType(se->getType(Context)); - if (T == Context.getCanonicalType(castTy)) - return val; - + // If types are the same or both are integers, ignore the cast. // FIXME: Remove this hack when we support symbolic truncation/extension. // HACK: If both castTy and T are integers, ignore the cast. This is // not a permanent solution. Eventually we want to precisely handle // extension/truncation of symbolic integers. This prevents us from losing // precision when we assign 'x = y' and 'y' is symbolic and x and y are // different integer types. - if (T->isIntegerType() && castTy->isIntegerType()) + if (haveSameType(T, castTy)) return val; + if (!isLocType) + return makeNonLoc(se, T, castTy); return UnknownVal(); } + // If value is a non integer constant, produce unknown. if (!isa<nonloc::ConcreteInt>(val)) return UnknownVal(); - // Only handle casts from integers to integers. + // Only handle casts from integers to integers - if val is an integer constant + // being cast to a non integer type, produce unknown. if (!isLocType && !castTy->isIntegerType()) return UnknownVal(); @@ -259,18 +268,15 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, // Idempotent ops (like a*1) can still change the type of an expression. // Wrap the LHS up in a NonLoc again and let evalCastFromNonLoc do the // dirty work. - if (isIdempotent) { - if (SymbolRef LHSSym = dyn_cast<SymbolData>(LHS)) - return evalCastFromNonLoc(nonloc::SymbolVal(LHSSym), resultTy); - return evalCastFromNonLoc(nonloc::SymExprVal(LHS), resultTy); - } + if (isIdempotent) + return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); // If we reach this point, the expression cannot be simplified. - // Make a SymExprVal for the entire thing. + // Make a SymbolVal for the entire expression. return makeNonLoc(LHS, op, RHS, resultTy); } -SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, +SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy) { @@ -298,7 +304,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, while (1) { switch (lhs.getSubKind()) { default: - return UnknownVal(); + return makeGenericVal(state, op, lhs, rhs, resultTy); case nonloc::LocAsIntegerKind: { Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); switch (rhs.getSubKind()) { @@ -321,94 +327,10 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, return makeTruthVal(true, resultTy); default: // This case also handles pointer arithmetic. - return UnknownVal(); + return makeGenericVal(state, op, lhs, rhs, resultTy); } } } - case nonloc::SymExprValKind: { - nonloc::SymExprVal *selhs = cast<nonloc::SymExprVal>(&lhs); - - // Only handle LHS of the form "$sym op constant", at least for now. - const SymIntExpr *symIntExpr = - dyn_cast<SymIntExpr>(selhs->getSymbolicExpression()); - - if (!symIntExpr) - return UnknownVal(); - - // Is this a logical not? (!x is represented as x == 0.) - if (op == BO_EQ && rhs.isZeroConstant()) { - // We know how to negate certain expressions. Simplify them here. - - BinaryOperator::Opcode opc = symIntExpr->getOpcode(); - switch (opc) { - default: - // We don't know how to negate this operation. - // Just handle it as if it were a normal comparison to 0. - break; - case BO_LAnd: - case BO_LOr: - llvm_unreachable("Logical operators handled by branching logic."); - case BO_Assign: - case BO_MulAssign: - case BO_DivAssign: - case BO_RemAssign: - case BO_AddAssign: - case BO_SubAssign: - case BO_ShlAssign: - case BO_ShrAssign: - case BO_AndAssign: - case BO_XorAssign: - case BO_OrAssign: - case BO_Comma: - llvm_unreachable("'=' and ',' operators handled by ExprEngine."); - case BO_PtrMemD: - case BO_PtrMemI: - llvm_unreachable("Pointer arithmetic not handled here."); - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - // Negate the comparison and make a value. - opc = NegateComparison(opc); - assert(symIntExpr->getType(Context) == resultTy); - return makeNonLoc(symIntExpr->getLHS(), opc, - symIntExpr->getRHS(), resultTy); - } - } - - // For now, only handle expressions whose RHS is a constant. - const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs); - if (!rhsInt) - return UnknownVal(); - - // If both the LHS and the current expression are additive, - // fold their constants. - if (BinaryOperator::isAdditiveOp(op)) { - BinaryOperator::Opcode lop = symIntExpr->getOpcode(); - if (BinaryOperator::isAdditiveOp(lop)) { - // resultTy may not be the best type to convert to, but it's - // probably the best choice in expressions with mixed type - // (such as x+1U+2LL). The rules for implicit conversions should - // choose a reasonable type to preserve the expression, and will - // at least match how the value is going to be used. - const llvm::APSInt &first = - BasicVals.Convert(resultTy, symIntExpr->getRHS()); - const llvm::APSInt &second = - BasicVals.Convert(resultTy, rhsInt->getValue()); - const llvm::APSInt *newRHS; - if (lop == op) - newRHS = BasicVals.evalAPSInt(BO_Add, first, second); - else - newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); - return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); - } - } - - // Otherwise, make a SymExprVal out of the expression. - return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); - } case nonloc::ConcreteIntKind: { const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); @@ -467,76 +389,165 @@ SVal SimpleSValBuilder::evalBinOpNN(const ProgramState *state, if (lhsValue == 0) // At this point lhs and rhs have been swapped. return rhs; - return UnknownVal(); + return makeGenericVal(state, op, rhs, lhs, resultTy); default: - return UnknownVal(); + return makeGenericVal(state, op, rhs, lhs, resultTy); } } } case nonloc::SymbolValKind: { - nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); - SymbolRef Sym = slhs->getSymbol(); - QualType lhsType = Sym->getType(Context); - - // The conversion type is usually the result type, but not in the case - // of relational expressions. - QualType conversionType = resultTy; - if (BinaryOperator::isRelationalOp(op)) - conversionType = lhsType; - - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (lhsType->isIntegerType()) - if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { - // The symbol evaluates to a constant. If necessary, promote the - // folded constant (LHS) to the result type. - const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, - *Constant); - lhs = nonloc::ConcreteInt(lhs_I); - - // Also promote the RHS (if necessary). - - // For shifts, it is not necessary to promote the RHS. - if (BinaryOperator::isShiftOp(op)) - continue; - - // Other operators: do an implicit conversion. This shouldn't be - // necessary once we support truncation/extension of symbolic values. - if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ - rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, - rhs_I->getValue())); + nonloc::SymbolVal *selhs = cast<nonloc::SymbolVal>(&lhs); + + // LHS is a symbolic expression. + if (selhs->isExpression()) { + + // Only handle LHS of the form "$sym op constant", at least for now. + const SymIntExpr *symIntExpr = + dyn_cast<SymIntExpr>(selhs->getSymbol()); + + if (!symIntExpr) + return makeGenericVal(state, op, lhs, rhs, resultTy); + + // Is this a logical not? (!x is represented as x == 0.) + if (op == BO_EQ && rhs.isZeroConstant()) { + // We know how to negate certain expressions. Simplify them here. + + BinaryOperator::Opcode opc = symIntExpr->getOpcode(); + switch (opc) { + default: + // We don't know how to negate this operation. + // Just handle it as if it were a normal comparison to 0. + break; + case BO_LAnd: + case BO_LOr: + llvm_unreachable("Logical operators handled by branching logic."); + case BO_Assign: + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_AddAssign: + case BO_SubAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + case BO_Comma: + llvm_unreachable("'=' and ',' operators handled by ExprEngine."); + case BO_PtrMemD: + case BO_PtrMemI: + llvm_unreachable("Pointer arithmetic not handled here."); + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + // Negate the comparison and make a value. + opc = NegateComparison(opc); + assert(symIntExpr->getType(Context) == resultTy); + return makeNonLoc(symIntExpr->getLHS(), opc, + symIntExpr->getRHS(), resultTy); } - - continue; } - // Is the RHS a symbol we can simplify? - if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { - SymbolRef RSym = srhs->getSymbol(); - if (RSym->getType(Context)->isIntegerType()) { - if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { - // The symbol evaluates to a constant. - const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, - *Constant); - rhs = nonloc::ConcreteInt(rhs_I); + // For now, only handle expressions whose RHS is a constant. + const nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs); + if (!rhsInt) + return makeGenericVal(state, op, lhs, rhs, resultTy); + + // If both the LHS and the current expression are additive, + // fold their constants. + if (BinaryOperator::isAdditiveOp(op)) { + BinaryOperator::Opcode lop = symIntExpr->getOpcode(); + if (BinaryOperator::isAdditiveOp(lop)) { + // resultTy may not be the best type to convert to, but it's + // probably the best choice in expressions with mixed type + // (such as x+1U+2LL). The rules for implicit conversions should + // choose a reasonable type to preserve the expression, and will + // at least match how the value is going to be used. + const llvm::APSInt &first = + BasicVals.Convert(resultTy, symIntExpr->getRHS()); + const llvm::APSInt &second = + BasicVals.Convert(resultTy, rhsInt->getValue()); + const llvm::APSInt *newRHS; + if (lop == op) + newRHS = BasicVals.evalAPSInt(BO_Add, first, second); + else + newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); } } - } - if (isa<nonloc::ConcreteInt>(rhs)) { - return MakeSymIntVal(slhs->getSymbol(), op, - cast<nonloc::ConcreteInt>(rhs).getValue(), - resultTy); - } + // Otherwise, make a SymbolVal out of the expression. + return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); - return UnknownVal(); + // LHS is a simple symbol (not a symbolic expression). + } else { + nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); + SymbolRef Sym = slhs->getSymbol(); + QualType lhsType = Sym->getType(Context); + + // The conversion type is usually the result type, but not in the case + // of relational expressions. + QualType conversionType = resultTy; + if (BinaryOperator::isRelationalOp(op)) + conversionType = lhsType; + + // Does the symbol simplify to a constant? If so, "fold" the constant + // by setting 'lhs' to a ConcreteInt and try again. + if (lhsType->isIntegerType()) + if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { + // The symbol evaluates to a constant. If necessary, promote the + // folded constant (LHS) to the result type. + const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, + *Constant); + lhs = nonloc::ConcreteInt(lhs_I); + + // Also promote the RHS (if necessary). + + // For shifts, it is not necessary to promote the RHS. + if (BinaryOperator::isShiftOp(op)) + continue; + + // Other operators: do an implicit conversion. This shouldn't be + // necessary once we support truncation/extension of symbolic values. + if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ + rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, + rhs_I->getValue())); + } + + continue; + } + + // Is the RHS a symbol we can simplify? + if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { + SymbolRef RSym = srhs->getSymbol(); + if (RSym->getType(Context)->isIntegerType()) { + if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { + // The symbol evaluates to a constant. + const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, + *Constant); + rhs = nonloc::ConcreteInt(rhs_I); + } + } + } + + if (isa<nonloc::ConcreteInt>(rhs)) { + return MakeSymIntVal(slhs->getSymbol(), op, + cast<nonloc::ConcreteInt>(rhs).getValue(), + resultTy); + } + + return makeGenericVal(state, op, lhs, rhs, resultTy); + } } } } } // FIXME: all this logic will change if/when we have MemRegion::getLocation(). -SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, +SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy) { @@ -703,6 +714,24 @@ SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, // The two regions are from the same base region. See if they're both a // type of region we know how to compare. + const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); + const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); + + // Heuristic: assume that no symbolic region (whose memory space is + // unknown) is on the stack. + // FIXME: we should be able to be more precise once we can do better + // aliasing constraints for symbolic regions, but this is a reasonable, + // albeit unsound, assumption that holds most of the time. + if (isa<StackSpaceRegion>(LeftMS) ^ isa<StackSpaceRegion>(RightMS)) { + switch (op) { + default: + break; + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); + } + } // FIXME: If/when there is a getAsRawOffset() for FieldRegions, this // ElementRegion path and the FieldRegion path below should be unified. @@ -831,7 +860,7 @@ SVal SimpleSValBuilder::evalBinOpLL(const ProgramState *state, } } -SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state, +SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) { @@ -925,7 +954,7 @@ SVal SimpleSValBuilder::evalBinOpLN(const ProgramState *state, return UnknownVal(); } -const llvm::APSInt *SimpleSValBuilder::getKnownValue(const ProgramState *state, +const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, SVal V) { if (V.isUnknownOrUndef()) return NULL; diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 48a6f4f..11748ae 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/DeclObjC.h" using namespace clang; using namespace ento; @@ -22,8 +23,9 @@ StoreManager::StoreManager(ProgramStateManager &stateMgr) : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} -StoreRef StoreManager::enterStackFrame(const ProgramState *state, - const StackFrameContext *frame) { +StoreRef StoreManager::enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) { return StoreRef(state->getStore(), *this); } @@ -101,8 +103,10 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) case MemRegion::StackArgumentsSpaceRegionKind: case MemRegion::HeapSpaceRegionKind: case MemRegion::UnknownSpaceRegionKind: - case MemRegion::NonStaticGlobalSpaceRegionKind: - case MemRegion::StaticGlobalSpaceRegionKind: { + case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::GlobalInternalSpaceRegionKind: + case MemRegion::GlobalSystemSpaceRegionKind: + case MemRegion::GlobalImmutableSpaceRegionKind: { llvm_unreachable("Invalid region cast"); } @@ -116,6 +120,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) case MemRegion::CompoundLiteralRegionKind: case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: + case MemRegion::ObjCStringRegionKind: case MemRegion::VarRegionKind: case MemRegion::CXXTempObjectRegionKind: case MemRegion::CXXBaseObjectRegionKind: @@ -212,7 +217,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, QualType castTy, bool performTestOnly) { - if (castTy.isNull()) + if (castTy.isNull() || V.isUnknownOrUndef()) return V; ASTContext &Ctx = svalBuilder.getContext(); @@ -227,12 +232,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, return V; } - if (const Loc *L = dyn_cast<Loc>(&V)) - return svalBuilder.evalCastFromLoc(*L, castTy); - else if (const NonLoc *NL = dyn_cast<NonLoc>(&V)) - return svalBuilder.evalCastFromNonLoc(*NL, castTy); - - return V; + return svalBuilder.dispatchCast(V, castTy); } SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { @@ -270,6 +270,10 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR)); } +SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) { + return getLValueFieldOrIvar(decl, base); +} + SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal Base) { @@ -336,3 +340,23 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, StoreManager::BindingsHandler::~BindingsHandler() {} +bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr, + Store store, + const MemRegion* R, + SVal val) { + SymbolRef SymV = val.getAsLocSymbol(); + if (!SymV || SymV != Sym) + return true; + + if (Binding) { + First = false; + return false; + } + else + Binding = R; + + return true; +} + +void SubRegionMap::anchor() { } +void SubRegionMap::Visitor::anchor() { } diff --git a/lib/StaticAnalyzer/Core/SubEngine.cpp b/lib/StaticAnalyzer/Core/SubEngine.cpp new file mode 100644 index 0000000..350f4b8 --- /dev/null +++ b/lib/StaticAnalyzer/Core/SubEngine.cpp @@ -0,0 +1,14 @@ +//== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" + +using namespace clang::ento; + +void SubEngine::anchor() { } diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index b843ab1..adefb58 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -21,6 +21,8 @@ using namespace clang; using namespace ento; +void SymExpr::anchor() { } + void SymExpr::dump() const { dumpToStream(llvm::errs()); } @@ -57,6 +59,15 @@ void SymIntExpr::dumpToStream(raw_ostream &os) const { if (getRHS().isUnsigned()) os << 'U'; } +void IntSymExpr::dumpToStream(raw_ostream &os) const { + os << ' ' << getLHS().getZExtValue(); + if (getLHS().isUnsigned()) os << 'U'; + print(os, getOpcode()); + os << '('; + getRHS()->dumpToStream(os); + os << ") "; +} + void SymSymExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); @@ -66,6 +77,12 @@ void SymSymExpr::dumpToStream(raw_ostream &os) const { os << ')'; } +void SymbolCast::dumpToStream(raw_ostream &os) const { + os << '(' << ToTy.getAsString() << ") ("; + Operand->dumpToStream(os); + os << ')'; +} + void SymbolConjured::dumpToStream(raw_ostream &os) const { os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; } @@ -84,10 +101,69 @@ void SymbolMetadata::dumpToStream(raw_ostream &os) const { << getRegion() << ',' << T.getAsString() << '}'; } +void SymbolData::anchor() { } + void SymbolRegionValue::dumpToStream(raw_ostream &os) const { os << "reg_$" << getSymbolID() << "<" << R << ">"; } +bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const { + return itr == X.itr; +} + +bool SymExpr::symbol_iterator::operator!=(const symbol_iterator &X) const { + return itr != X.itr; +} + +SymExpr::symbol_iterator::symbol_iterator(const SymExpr *SE) { + itr.push_back(SE); + while (!isa<SymbolData>(itr.back())) expand(); +} + +SymExpr::symbol_iterator &SymExpr::symbol_iterator::operator++() { + assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); + assert(isa<SymbolData>(itr.back())); + itr.pop_back(); + if (!itr.empty()) + while (!isa<SymbolData>(itr.back())) expand(); + return *this; +} + +SymbolRef SymExpr::symbol_iterator::operator*() { + assert(!itr.empty() && "attempting to dereference an 'end' iterator"); + return cast<SymbolData>(itr.back()); +} + +void SymExpr::symbol_iterator::expand() { + const SymExpr *SE = itr.back(); + itr.pop_back(); + + switch (SE->getKind()) { + case SymExpr::RegionValueKind: + case SymExpr::ConjuredKind: + case SymExpr::DerivedKind: + case SymExpr::ExtentKind: + case SymExpr::MetadataKind: + return; + case SymExpr::CastSymbolKind: + itr.push_back(cast<SymbolCast>(SE)->getOperand()); + return; + case SymExpr::SymIntKind: + itr.push_back(cast<SymIntExpr>(SE)->getLHS()); + return; + case SymExpr::IntSymKind: + itr.push_back(cast<IntSymExpr>(SE)->getRHS()); + return; + case SymExpr::SymSymKind: { + const SymSymExpr *x = cast<SymSymExpr>(SE); + itr.push_back(x->getLHS()); + itr.push_back(x->getRHS()); + return; + } + } + llvm_unreachable("unhandled expansion case"); +} + const SymbolRegionValue* SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { llvm::FoldingSetNodeID profile; @@ -105,16 +181,17 @@ SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { } const SymbolConjured* -SymbolManager::getConjuredSymbol(const Stmt *E, QualType T, unsigned Count, +SymbolManager::getConjuredSymbol(const Stmt *E, const LocationContext *LCtx, + QualType T, unsigned Count, const void *SymbolTag) { llvm::FoldingSetNodeID profile; - SymbolConjured::Profile(profile, E, T, Count, SymbolTag); + SymbolConjured::Profile(profile, E, T, Count, LCtx, SymbolTag); void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>(); - new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag); + new (SD) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag); DataSet.InsertNode(SD, InsertPos); ++SymbolCounter; } @@ -174,6 +251,22 @@ SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T, return cast<SymbolMetadata>(SD); } +const SymbolCast* +SymbolManager::getCastSymbol(const SymExpr *Op, + QualType From, QualType To) { + llvm::FoldingSetNodeID ID; + SymbolCast::Profile(ID, Op, From, To); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + if (!data) { + data = (SymbolCast*) BPAlloc.Allocate<SymbolCast>(); + new (data) SymbolCast(Op, From, To); + DataSet.InsertNode(data, InsertPos); + } + + return cast<SymbolCast>(data); +} + const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& v, @@ -192,6 +285,24 @@ const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, return cast<SymIntExpr>(data); } +const IntSymExpr *SymbolManager::getIntSymExpr(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, + QualType t) { + llvm::FoldingSetNodeID ID; + IntSymExpr::Profile(ID, lhs, op, rhs, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (IntSymExpr*) BPAlloc.Allocate<IntSymExpr>(); + new (data) IntSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast<IntSymExpr>(data); +} + const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, @@ -381,7 +492,16 @@ bool SymbolReaper::isLive(SymbolRef sym) { return isa<SymbolRegionValue>(sym); } -bool SymbolReaper::isLive(const Stmt *ExprVal) const { +bool +SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { + if (LCtx != ELCtx) { + // If the reaper's location context is a parent of the expression's + // location context, then the expression value is now "out of scope". + if (LCtx->isParentOf(ELCtx)) + return false; + return true; + } + return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal); } diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index 3543f7f..fe912df 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -31,9 +31,8 @@ public: TextPathDiagnostics(const std::string& output, DiagnosticsEngine &diag) : OutputFile(output), Diag(diag) {} - void HandlePathDiagnosticImpl(const PathDiagnostic* D); - - void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade) { } + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade); virtual StringRef getName() const { return "TextPathDiagnostics"; @@ -53,18 +52,18 @@ ento::createTextPathDiagnosticConsumer(const std::string& out, return new TextPathDiagnostics(out, PP.getDiagnostics()); } -void TextPathDiagnostics::HandlePathDiagnosticImpl(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; - } - - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) { - unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID( - DiagnosticIDs::Note, I->getString()); - Diag.Report(I->getLocation().asLocation(), diagID); +void TextPathDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + SmallVectorImpl<std::string> *FilesMade) { + for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), + et = Diags.end(); it != et; ++it) { + const PathDiagnostic *D = *it; + for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + I != E; ++I) { + unsigned diagID = + Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, + (*I)->getString()); + Diag.Report((*I)->getLocation().asLocation(), diagID); + } } } |