diff options
Diffstat (limited to 'lib/Analysis')
68 files changed, 461 insertions, 22597 deletions
diff --git a/lib/Analysis/AnalysisContext.cpp b/lib/Analysis/AnalysisContext.cpp index ad9f6dd..ccd5088 100644 --- a/lib/Analysis/AnalysisContext.cpp +++ b/lib/Analysis/AnalysisContext.cpp @@ -12,10 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -87,12 +86,6 @@ AnalysisContext *AnalysisContextManager::getContext(const Decl *D) { return AC; } -const BlockDecl *BlockInvocationContext::getBlockDecl() const { - return Data.is<const BlockDataRegion*>() ? - Data.get<const BlockDataRegion*>()->getDecl() - : Data.get<const BlockDecl*>(); -} - //===----------------------------------------------------------------------===// // FoldingSet profiling. //===----------------------------------------------------------------------===// @@ -117,11 +110,7 @@ void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { } void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { - if (const BlockDataRegion *BR = getBlockRegion()) - Profile(ID, getAnalysisContext(), getParent(), BR); - else - Profile(ID, getAnalysisContext(), getParent(), - Data.get<const BlockDecl*>()); + Profile(ID, getAnalysisContext(), getParent(), BD); } //===----------------------------------------------------------------------===// @@ -170,15 +159,6 @@ LocationContextManager::getScope(AnalysisContext *ctx, return getLocationContext<ScopeContext, Stmt>(ctx, parent, s); } -const BlockInvocationContext * -LocationContextManager::getBlockInvocation(AnalysisContext *ctx, - const LocationContext *parent, - const BlockDataRegion *BR) { - return getLocationContext<BlockInvocationContext, BlockDataRegion>(ctx, - parent, - BR); -} - //===----------------------------------------------------------------------===// // LocationContext methods. //===----------------------------------------------------------------------===// @@ -214,6 +194,7 @@ namespace { class FindBlockDeclRefExprsVals : public StmtVisitor<FindBlockDeclRefExprsVals>{ BumpVector<const VarDecl*> &BEVals; BumpVectorContext &BC; + llvm::DenseMap<const VarDecl*, unsigned> Visited; public: FindBlockDeclRefExprsVals(BumpVector<const VarDecl*> &bevals, BumpVectorContext &bc) @@ -224,10 +205,27 @@ public: if (Stmt *child = *I) Visit(child); } + + void VisitDeclRefExpr(const DeclRefExpr *DR) { + // Non-local variables are also directly modified. + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + if (!VD->hasLocalStorage()) { + unsigned &flag = Visited[VD]; + if (!flag) { + flag = 1; + BEVals.push_back(VD, BC); + } + } + } void VisitBlockDeclRefExpr(BlockDeclRefExpr *DR) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) - BEVals.push_back(VD, BC); + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + unsigned &flag = Visited[VD]; + if (!flag) { + flag = 1; + BEVals.push_back(VD, BC); + } + } } }; } // end anonymous namespace diff --git a/lib/Analysis/ArrayBoundChecker.cpp b/lib/Analysis/ArrayBoundChecker.cpp deleted file mode 100644 index 49c8606..0000000 --- a/lib/Analysis/ArrayBoundChecker.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ArrayBoundChecker, which is a path-sensitive check -// which looks for an out-of-bound array element access. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" - -using namespace clang; - -namespace { -class ArrayBoundChecker : - public CheckerVisitor<ArrayBoundChecker> { - BuiltinBug *BT; -public: - ArrayBoundChecker() : BT(0) {} - static void *getTag(); - void VisitLocation(CheckerContext &C, const Stmt *S, SVal l); -}; -} - -void clang::RegisterArrayBoundChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ArrayBoundChecker()); -} - -void *ArrayBoundChecker::getTag() { - static int x = 0; return &x; -} - -void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ - // Check for out of bound array element access. - const MemRegion *R = l.getAsRegion(); - if (!R) - return; - - R = R->StripCasts(); - - const ElementRegion *ER = dyn_cast<ElementRegion>(R); - if (!ER) - return; - - // Get the index of the accessed element. - DefinedOrUnknownSVal &Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); - - const GRState *state = C.getState(); - - // Get the size of the array. - DefinedOrUnknownSVal NumElements - = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), - ER->getValueType(C.getASTContext())); - - const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); - const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); - if (StOutBound && !StInBound) { - ExplodedNode *N = C.GenerateSink(StOutBound); - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Out-of-bound array access", - "Access out-of-bound array element (buffer overflow)"); - - // FIXME: It would be nice to eventually make this diagnostic more clear, - // e.g., by referencing the original declaration or by saying *why* this - // reference is outside the range. - - // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT, BT->getDescription(), N); - - report->addRange(S->getSourceRange()); - C.EmitReport(report); - return; - } - - // Array bound check succeeded. From this point forward the array bound - // should always succeed. - assert(StInBound); - C.addTransition(StInBound); -} diff --git a/lib/Analysis/AttrNonNullChecker.cpp b/lib/Analysis/AttrNonNullChecker.cpp deleted file mode 100644 index aa21700..0000000 --- a/lib/Analysis/AttrNonNullChecker.cpp +++ /dev/null @@ -1,112 +0,0 @@ -//===--- AttrNonNullChecker.h - Undefined arguments checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines AttrNonNullChecker, a builtin check in GRExprEngine that -// performs checks for arguments declared to have nonnull attribute. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class AttrNonNullChecker - : public CheckerVisitor<AttrNonNullChecker> { - BugType *BT; -public: - AttrNonNullChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); -}; -} // end anonymous namespace - -void clang::RegisterAttrNonNullChecker(GRExprEngine &Eng) { - Eng.registerCheck(new AttrNonNullChecker()); -} - -void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, - const CallExpr *CE) { - const GRState *state = C.getState(); - - // Check if the callee has a 'nonnull' attribute. - SVal X = state->getSVal(CE->getCallee()); - - const FunctionDecl* FD = X.getAsFunctionDecl(); - if (!FD) - return; - - const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); - if (!Att) - return; - - // Iterate through the arguments of CE and check them for null. - unsigned idx = 0; - - for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; - ++I, ++idx) { - - if (!Att->isNonNull(idx)) - continue; - - const SVal &V = state->getSVal(*I); - const DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); - - if (!DV) - continue; - - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotNull, *stateNull; - llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); - - if (stateNull && !stateNotNull) { - // Generate an error node. Check for a null node in case - // we cache out. - if (ExplodedNode *errorNode = C.GenerateSink(stateNull)) { - - // Lazily allocate the BugType object if it hasn't already been - // created. Ownership is transferred to the BugReporter object once - // the BugReport is passed to 'EmitWarning'. - if (!BT) - BT = new BugType("Argument with 'nonnull' attribute passed null", - "API"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, - "Null pointer passed as an argument to a " - "'nonnull' parameter", errorNode); - - // Highlight the range of the argument that was null. - const Expr *arg = *I; - R->addRange(arg->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); - - // Emit the bug report. - C.EmitReport(R); - } - - // Always return. Either we cached out or we just emitted an error. - return; - } - - // If a pointer value passed the check we should assume that it is - // indeed not null from this point forward. - assert(stateNotNull); - state = stateNotNull; - } - - // If we reach here all of the arguments passed the nonnull check. - // If 'state' has been updated generated a new node. - C.addTransition(state); -} diff --git a/lib/Analysis/BasicConstraintManager.cpp b/lib/Analysis/BasicConstraintManager.cpp deleted file mode 100644 index 6dfc470..0000000 --- a/lib/Analysis/BasicConstraintManager.cpp +++ /dev/null @@ -1,317 +0,0 @@ -//== BasicConstraintManager.cpp - Manage basic constraints.------*- 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 BasicConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. -// -//===----------------------------------------------------------------------===// - -#include "SimpleConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - - -namespace { class ConstNotEq {}; } -namespace { class ConstEq {}; } - -typedef llvm::ImmutableMap<SymbolRef,GRState::IntSetTy> ConstNotEqTy; -typedef llvm::ImmutableMap<SymbolRef,const llvm::APSInt*> ConstEqTy; - -static int ConstEqIndex = 0; -static int ConstNotEqIndex = 0; - -namespace clang { -template<> -struct GRStateTrait<ConstNotEq> : public GRStatePartialTrait<ConstNotEqTy> { - static inline void* GDMIndex() { return &ConstNotEqIndex; } -}; - -template<> -struct GRStateTrait<ConstEq> : public GRStatePartialTrait<ConstEqTy> { - static inline void* GDMIndex() { return &ConstEqIndex; } -}; -} - -namespace { -// BasicConstraintManager only tracks equality and inequality constraints of -// constants and integer variables. -class BasicConstraintManager - : public SimpleConstraintManager { - GRState::IntSetTy::Factory ISetFactory; -public: - BasicConstraintManager(GRStateManager &statemgr, GRSubEngine &subengine) - : SimpleConstraintManager(subengine), - ISetFactory(statemgr.getAllocator()) {} - - const GRState* AssumeSymNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymEQ(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGT(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V); - - const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const; - bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) - const; - - const GRState* RemoveDeadBindings(const GRState* state, SymbolReaper& SymReaper); - - void print(const GRState* state, llvm::raw_ostream& Out, - const char* nl, const char *sep); -}; - -} // end anonymous namespace - -ConstraintManager* clang::CreateBasicConstraintManager(GRStateManager& statemgr, - GRSubEngine &subengine) { - return new BasicConstraintManager(statemgr, subengine); -} - -const GRState* -BasicConstraintManager::AssumeSymNE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) { - // First, determine if sym == X, where X != V. - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = (*X != V); - return isFeasible ? state : NULL; - } - - // Second, determine if sym != V. - if (isNotEqual(state, sym, V)) - return state; - - // If we reach here, sym is not a constant and we don't know if it is != V. - // Make that assumption. - return AddNE(state, sym, V); -} - -const GRState *BasicConstraintManager::AssumeSymEQ(const GRState *state, - SymbolRef sym, - const llvm::APSInt &V) { - // First, determine if sym == X, where X != V. - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = *X == V; - return isFeasible ? state : NULL; - } - - // Second, determine if sym != V. - if (isNotEqual(state, sym, V)) - return NULL; - - // If we reach here, sym is not a constant and we don't know if it is == V. - // Make that assumption. - return AddEQ(state, sym, V); -} - -// These logic will be handled in another ConstraintManager. -const GRState *BasicConstraintManager::AssumeSymLT(const GRState *state, - SymbolRef sym, - const llvm::APSInt& V) { - // Is 'V' the smallest possible value? - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { - // sym cannot be any value less than 'V'. This path is infeasible. - return NULL; - } - - // FIXME: For now have assuming x < y be the same as assuming sym != V; - return AssumeSymNE(state, sym, V); -} - -const GRState *BasicConstraintManager::AssumeSymGT(const GRState *state, - SymbolRef sym, - const llvm::APSInt& V) { - - // Is 'V' the largest possible value? - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { - // sym cannot be any value greater than 'V'. This path is infeasible. - return NULL; - } - - // FIXME: For now have assuming x > y be the same as assuming sym != V; - return AssumeSymNE(state, sym, V); -} - -const GRState *BasicConstraintManager::AssumeSymGE(const GRState *state, - SymbolRef sym, - const llvm::APSInt &V) { - - // Reject a path if the value of sym is a constant X and !(X >= V). - if (const llvm::APSInt *X = getSymVal(state, sym)) { - bool isFeasible = *X >= V; - return isFeasible ? state : NULL; - } - - // Sym is not a constant, but it is worth looking to see if V is the - // maximum integer value. - if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { - // If we know that sym != V, then this condition is infeasible since - // there is no other value greater than V. - bool isFeasible = !isNotEqual(state, sym, V); - - // If the path is still feasible then as a consequence we know that - // 'sym == V' because we cannot have 'sym > V' (no larger values). - // Add this constraint. - return isFeasible ? AddEQ(state, sym, V) : NULL; - } - - return state; -} - -const GRState* -BasicConstraintManager::AssumeSymLE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { - - // Reject a path if the value of sym is a constant X and !(X <= V). - if (const llvm::APSInt* X = getSymVal(state, sym)) { - bool isFeasible = *X <= V; - return isFeasible ? state : NULL; - } - - // Sym is not a constant, but it is worth looking to see if V is the - // minimum integer value. - if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { - // If we know that sym != V, then this condition is infeasible since - // there is no other value less than V. - bool isFeasible = !isNotEqual(state, sym, V); - - // If the path is still feasible then as a consequence we know that - // 'sym == V' because we cannot have 'sym < V' (no smaller values). - // Add this constraint. - return isFeasible ? AddEQ(state, sym, V) : NULL; - } - - return state; -} - -const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { - // Create a new state with the old binding replaced. - return state->set<ConstEq>(sym, &V); -} - -const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) { - - // First, retrieve the NE-set associated with the given symbol. - ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym); - GRState::IntSetTy S = T ? *T : ISetFactory.GetEmptySet(); - - // Now add V to the NE set. - S = ISetFactory.Add(S, &V); - - // Create a new state with the old binding replaced. - return state->set<ConstNotEq>(sym, S); -} - -const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state, - SymbolRef sym) const { - const ConstEqTy::data_type* T = state->get<ConstEq>(sym); - return T ? *T : NULL; -} - -bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) const { - - // Retrieve the NE-set associated with the given symbol. - const ConstNotEqTy::data_type* T = state->get<ConstNotEq>(sym); - - // See if V is present in the NE-set. - return T ? T->contains(&V) : false; -} - -bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, - const llvm::APSInt& V) const { - // Retrieve the EQ-set associated with the given symbol. - const ConstEqTy::data_type* T = state->get<ConstEq>(sym); - // See if V is present in the EQ-set. - return T ? **T == V : false; -} - -/// Scan all symbols referenced by the constraints. If the symbol is not alive -/// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -BasicConstraintManager::RemoveDeadBindings(const GRState* state, - SymbolReaper& SymReaper) { - - ConstEqTy CE = state->get<ConstEq>(); - ConstEqTy::Factory& CEFactory = state->get_context<ConstEq>(); - - for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) CE = CEFactory.Remove(CE, sym); - } - state = state->set<ConstEq>(CE); - - ConstNotEqTy CNE = state->get<ConstNotEq>(); - ConstNotEqTy::Factory& CNEFactory = state->get_context<ConstNotEq>(); - - for (ConstNotEqTy::iterator I = CNE.begin(), E = CNE.end(); I != E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) CNE = CNEFactory.Remove(CNE, sym); - } - - return state->set<ConstNotEq>(CNE); -} - -void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - // Print equality constraints. - - ConstEqTy CE = state->get<ConstEq>(); - - if (!CE.isEmpty()) { - Out << nl << sep << "'==' constraints:"; - for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) - Out << nl << " $" << I.getKey() << " : " << *I.getData(); - } - - // Print != constraints. - - ConstNotEqTy CNE = state->get<ConstNotEq>(); - - if (!CNE.isEmpty()) { - Out << nl << sep << "'!=' constraints:"; - - for (ConstNotEqTy::iterator I = CNE.begin(), EI = CNE.end(); I!=EI; ++I) { - Out << nl << " $" << I.getKey() << " : "; - bool isFirst = true; - - GRState::IntSetTy::iterator J = I.getData().begin(), - EJ = I.getData().end(); - - for ( ; J != EJ; ++J) { - if (isFirst) isFirst = false; - else Out << ", "; - - Out << (*J)->getSExtValue(); // Hack: should print to raw_ostream. - } - } - } -} diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp deleted file mode 100644 index 67483d9..0000000 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ /dev/null @@ -1,598 +0,0 @@ -//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicObjCFoundationChecks, a class that encapsulates -// a set of simple checks to run on Objective-C code using Apple's Foundation -// classes. -// -//===----------------------------------------------------------------------===// - -#include "BasicObjCFoundationChecks.h" - -#include "clang/Analysis/PathSensitive/ExplodedGraph.h" -#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/LocalCheckers.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/ASTContext.h" - -using namespace clang; - -static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { - const Expr* Receiver = ME->getReceiver(); - - if (!Receiver) - return NULL; - - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs<ObjCObjectPointerType>()) - return PT->getInterfaceType(); - - return NULL; -} - -static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { - if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) - return ReceiverType->getDecl()->getIdentifier()->getNameStart(); - return NULL; -} - -namespace { - -class APIMisuse : public BugType { -public: - APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} -}; - -class BasicObjCFoundationChecks : public GRSimpleAPICheck { - APIMisuse *BT; - BugReporter& BR; - ASTContext &Ctx; - - bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix); - bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME); - - void Warn(ExplodedNode* N, const Expr* E, const std::string& s); - void WarnNilArg(ExplodedNode* N, const Expr* E); - - bool CheckNilArg(ExplodedNode* N, unsigned Arg); - -public: - BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br) - : BT(0), BR(br), Ctx(ctx) {} - - bool Audit(ExplodedNode* N, GRStateManager&); - -private: - void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(ME) << "' method '" - << ME->getSelector().getAsString() << "' cannot be nil."; - - // Lazily create the BugType object for NilArg. This will be owned - // by the BugReporter object 'BR' once we call BR.EmitWarning. - if (!BT) BT = new APIMisuse("nil argument"); - - RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); - R->addRange(ME->getArg(Arg)->getSourceRange()); - BR.EmitReport(R); - } -}; - -} // end anonymous namespace - - -GRSimpleAPICheck* -clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) { - return new BasicObjCFoundationChecks(Ctx, BR); -} - - - -bool BasicObjCFoundationChecks::Audit(ExplodedNode* N, - GRStateManager&) { - - const ObjCMessageExpr* ME = - cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - - const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); - - if (!ReceiverType) - return false; - - if (isNSString(ReceiverType, - ReceiverType->getDecl()->getIdentifier()->getName())) - return AuditNSString(N, ME); - - return false; -} - -static inline bool isNil(SVal X) { - return isa<loc::ConcreteInt>(X); -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) { - const ObjCMessageExpr* ME = - cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - - const Expr * E = ME->getArg(Arg); - - if (isNil(N->getState()->getSVal(E))) { - WarnNilArg(N, ME, Arg); - return true; - } - - return false; -} - -//===----------------------------------------------------------------------===// -// NSString checking. -//===----------------------------------------------------------------------===// - -bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T, - llvm::StringRef ClassName) { - return ClassName == "NSString" || ClassName == "NSMutableString"; -} - -bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, - const ObjCMessageExpr* ME) { - - Selector S = ME->getSelector(); - - if (S.isUnarySelector()) - return false; - - // FIXME: This is going to be really slow doing these checks with - // lexical comparisons. - - std::string name = S.getAsString(); - assert (!name.empty()); - const char* cstr = &name[0]; - unsigned len = name.size(); - - switch (len) { - default: - break; - case 8: - if (!strcmp(cstr, "compare:")) - return CheckNilArg(N, 0); - - break; - - case 15: - // FIXME: Checking for initWithFormat: will not work in most cases - // yet because [NSString alloc] returns id, not NSString*. We will - // need support for tracking expected-type information in the analyzer - // to find these errors. - if (!strcmp(cstr, "initWithFormat:")) - return CheckNilArg(N, 0); - - break; - - case 16: - if (!strcmp(cstr, "compare:options:")) - return CheckNilArg(N, 0); - - break; - - case 22: - if (!strcmp(cstr, "compare:options:range:")) - return CheckNilArg(N, 0); - - break; - - case 23: - - if (!strcmp(cstr, "caseInsensitiveCompare:")) - return CheckNilArg(N, 0); - - break; - - case 29: - if (!strcmp(cstr, "compare:options:range:locale:")) - return CheckNilArg(N, 0); - - break; - - case 37: - if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:")) - return CheckNilArg(N, 0); - - break; - } - - return false; -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -namespace { - -class AuditCFNumberCreate : public GRSimpleAPICheck { - APIMisuse* BT; - - // FIXME: Either this should be refactored into GRSimpleAPICheck, or - // it should always be passed with a call to Audit. The latter - // approach makes this class more stateless. - ASTContext& Ctx; - IdentifierInfo* II; - BugReporter& BR; - -public: - AuditCFNumberCreate(ASTContext& ctx, BugReporter& br) - : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){} - - ~AuditCFNumberCreate() {} - - bool Audit(ExplodedNode* N, GRStateManager&); - -private: - void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N, - uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); -}; -} // end anonymous namespace - -enum CFNumberType { - kCFNumberSInt8Type = 1, - kCFNumberSInt16Type = 2, - kCFNumberSInt32Type = 3, - kCFNumberSInt64Type = 4, - kCFNumberFloat32Type = 5, - kCFNumberFloat64Type = 6, - kCFNumberCharType = 7, - kCFNumberShortType = 8, - kCFNumberIntType = 9, - kCFNumberLongType = 10, - kCFNumberLongLongType = 11, - kCFNumberFloatType = 12, - kCFNumberDoubleType = 13, - kCFNumberCFIndexType = 14, - kCFNumberNSIntegerType = 15, - kCFNumberCGFloatType = 16 -}; - -namespace { - template<typename T> - class Optional { - bool IsKnown; - T Val; - public: - Optional() : IsKnown(false), Val(0) {} - Optional(const T& val) : IsKnown(true), Val(val) {} - - bool isKnown() const { return IsKnown; } - - const T& getValue() const { - assert (isKnown()); - return Val; - } - - operator const T&() const { - return getValue(); - } - }; -} - -static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { - static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; - - if (i < kCFNumberCharType) - return FixedSize[i-1]; - - QualType T; - - switch (i) { - case kCFNumberCharType: T = Ctx.CharTy; break; - case kCFNumberShortType: T = Ctx.ShortTy; break; - case kCFNumberIntType: T = Ctx.IntTy; break; - case kCFNumberLongType: T = Ctx.LongTy; break; - case kCFNumberLongLongType: T = Ctx.LongLongTy; break; - case kCFNumberFloatType: T = Ctx.FloatTy; break; - case kCFNumberDoubleType: T = Ctx.DoubleTy; break; - case kCFNumberCFIndexType: - case kCFNumberNSIntegerType: - case kCFNumberCGFloatType: - // FIXME: We need a way to map from names to Type*. - default: - return Optional<uint64_t>(); - } - - return Ctx.getTypeSize(T); -} - -#if 0 -static const char* GetCFNumberTypeStr(uint64_t i) { - static const char* Names[] = { - "kCFNumberSInt8Type", - "kCFNumberSInt16Type", - "kCFNumberSInt32Type", - "kCFNumberSInt64Type", - "kCFNumberFloat32Type", - "kCFNumberFloat64Type", - "kCFNumberCharType", - "kCFNumberShortType", - "kCFNumberIntType", - "kCFNumberLongType", - "kCFNumberLongLongType", - "kCFNumberFloatType", - "kCFNumberDoubleType", - "kCFNumberCFIndexType", - "kCFNumberNSIntegerType", - "kCFNumberCGFloatType" - }; - - return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; -} -#endif - -bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ - const CallExpr* CE = - cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - const Expr* Callee = CE->getCallee(); - SVal CallV = N->getState()->getSVal(Callee); - const FunctionDecl* FD = CallV.getAsFunctionDecl(); - - if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3) - return false; - - // Get the value of the "theType" argument. - SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1)); - - // FIXME: We really should allow ranges of valid theType values, and - // bifurcate the state appropriately. - nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); - - if (!V) - return false; - - uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); - - // FIXME: In some cases we can emit an error. - if (!TargetSize.isKnown()) - return false; - - // Look at the value of the integer being passed by reference. Essentially - // we want to catch cases where the value passed in is not equal to the - // size of the type being created. - SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2)); - - // FIXME: Eventually we should handle arbitrary locations. We can do this - // by having an enhanced memory model that does low-level typing. - loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); - - if (!LV) - return false; - - const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); - - if (!R) - return false; - - QualType T = Ctx.getCanonicalType(R->getValueType(Ctx)); - - // FIXME: If the pointee isn't an integer type, should we flag a warning? - // People can do weird stuff with pointers. - - if (!T->isIntegerType()) - return false; - - uint64_t SourceSize = Ctx.getTypeSize(T); - - // CHECK: is SourceSize == TargetSize - - if (SourceSize == TargetSize) - return false; - - AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind); - - // FIXME: We can actually create an abstract "CFNumber" object that has - // the bits initialized to the provided values. - return SourceSize < TargetSize; -} - -void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex, - ExplodedNode *N, - uint64_t SourceSize, uint64_t TargetSize, - uint64_t NumberKind) { - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << (SourceSize == 8 ? "An " : "A ") - << SourceSize << " bit integer is used to initialize a CFNumber " - "object that represents " - << (TargetSize == 8 ? "an " : "a ") - << TargetSize << " bit integer. "; - - if (SourceSize < TargetSize) - os << (TargetSize - SourceSize) - << " bits of the CFNumber value will be garbage." ; - else - os << (SourceSize - TargetSize) - << " bits of the input integer will be lost."; - - // Lazily create the BugType object. This will be owned - // by the BugReporter object 'BR' once we call BR.EmitWarning. - if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(Ex->getSourceRange()); - BR.EmitReport(report); -} - -GRSimpleAPICheck* -clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) { - return new AuditCFNumberCreate(Ctx, BR); -} - -//===----------------------------------------------------------------------===// -// CFRetain/CFRelease auditing for null arguments. -//===----------------------------------------------------------------------===// - -namespace { -class AuditCFRetainRelease : public GRSimpleAPICheck { - APIMisuse *BT; - - // FIXME: Either this should be refactored into GRSimpleAPICheck, or - // it should always be passed with a call to Audit. The latter - // approach makes this class more stateless. - ASTContext& Ctx; - IdentifierInfo *Retain, *Release; - BugReporter& BR; - -public: - AuditCFRetainRelease(ASTContext& ctx, BugReporter& br) - : BT(0), Ctx(ctx), - Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")), - BR(br){} - - ~AuditCFRetainRelease() {} - - bool Audit(ExplodedNode* N, GRStateManager&); -}; -} // end anonymous namespace - - -bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) { - const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - - // If the CallExpr doesn't have exactly 1 argument just give up checking. - if (CE->getNumArgs() != 1) - return false; - - // Check if we called CFRetain/CFRelease. - const GRState* state = N->getState(); - SVal X = state->getSVal(CE->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - - if (!FD) - return false; - - const IdentifierInfo *FuncII = FD->getIdentifier(); - if (!(FuncII == Retain || FuncII == Release)) - return false; - - // Finally, check if the argument is NULL. - // FIXME: We should be able to bifurcate the state here, as a successful - // check will result in the value not being NULL afterwards. - // FIXME: Need a way to register vistors for the BugReporter. Would like - // to benefit from the same diagnostics that regular null dereference - // reporting has. - if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) { - if (!BT) - BT = new APIMisuse("null passed to CFRetain/CFRelease"); - - const char *description = (FuncII == Retain) - ? "Null pointer argument in call to CFRetain" - : "Null pointer argument in call to CFRelease"; - - RangedBugReport *report = new RangedBugReport(*BT, description, N); - report->addRange(CE->getArg(0)->getSourceRange()); - BR.EmitReport(report); - return true; - } - - return false; -} - - -GRSimpleAPICheck* -clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { - return new AuditCFRetainRelease(Ctx, BR); -} - -//===----------------------------------------------------------------------===// -// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. -//===----------------------------------------------------------------------===// - -namespace { -class ClassReleaseChecker : - public CheckerVisitor<ClassReleaseChecker> { - Selector releaseS; - Selector retainS; - Selector autoreleaseS; - Selector drainS; - BugType *BT; -public: - ClassReleaseChecker(ASTContext &Ctx) - : releaseS(GetNullarySelector("release", Ctx)), - retainS(GetNullarySelector("retain", Ctx)), - autoreleaseS(GetNullarySelector("autorelease", Ctx)), - drainS(GetNullarySelector("drain", Ctx)), - BT(0) {} - - static void *getTag() { static int x = 0; return &x; } - - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); -}; -} - -void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - - const IdentifierInfo *ClsName = ME->getClassName(); - if (!ClsName) - return; - - Selector S = ME->getSelector(); - if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) - return; - - if (!BT) - BT = new APIMisuse("message incorrectly sent to class instead of class " - "instance"); - - ExplodedNode *N = C.GenerateNode(); - - if (!N) - return; - - llvm::SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - - os << "The '" << S.getAsString() << "' message should be sent to instances " - "of class '" << ClsName->getName() - << "' and not the class directly"; - - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(ME->getSourceRange()); - C.EmitReport(report); -} - -//===----------------------------------------------------------------------===// -// Check registration. -//===----------------------------------------------------------------------===// - -void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { - ASTContext& Ctx = Eng.getContext(); - BugReporter &BR = Eng.getBugReporter(); - - Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR), - Stmt::ObjCMessageExprClass); - Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass); - Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass); - - RegisterNSErrorChecks(BR, Eng, D); - RegisterNSAutoreleasePoolChecks(Eng); - Eng.registerCheck(new ClassReleaseChecker(Ctx)); -} diff --git a/lib/Analysis/BasicObjCFoundationChecks.h b/lib/Analysis/BasicObjCFoundationChecks.h deleted file mode 100644 index 679c6dc..0000000 --- a/lib/Analysis/BasicObjCFoundationChecks.h +++ /dev/null @@ -1,41 +0,0 @@ -//== BasicObjCFoundationChecks.h - Simple Apple-Foundation checks -*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicObjCFoundationChecks, a class that encapsulates -// a set of simple checks to run on Objective-C code using Apple's Foundation -// classes. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS -#define LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS - -namespace clang { - -class ASTContext; -class BugReporter; -class Decl; -class GRExprEngine; -class GRSimpleAPICheck; - -GRSimpleAPICheck *CreateBasicObjCFoundationChecks(ASTContext& Ctx, - BugReporter& BR); - -GRSimpleAPICheck *CreateAuditCFNumberCreate(ASTContext& Ctx, - BugReporter& BR); - -GRSimpleAPICheck *CreateAuditCFRetainRelease(ASTContext& Ctx, - BugReporter& BR); - -void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D); -void RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng); - -} // end clang namespace - -#endif diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp deleted file mode 100644 index 224281b..0000000 --- a/lib/Analysis/BasicStore.cpp +++ /dev/null @@ -1,625 +0,0 @@ -//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the BasicStore and BasicStoreManager classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; - -typedef llvm::ImmutableMap<const MemRegion*,SVal> BindingsTy; - -namespace { - -class BasicStoreSubRegionMap : public SubRegionMap { -public: - BasicStoreSubRegionMap() {} - - bool iterSubRegions(const MemRegion* R, Visitor& V) const { - return true; // Do nothing. No subregions. - } -}; - -class BasicStoreManager : public StoreManager { - BindingsTy::Factory VBFactory; -public: - BasicStoreManager(GRStateManager& mgr) - : StoreManager(mgr), VBFactory(mgr.getAllocator()) {} - - ~BasicStoreManager() {} - - SubRegionMap *getSubRegionMap(const GRState *state) { - return new BasicStoreSubRegionMap(); - } - - SValuator::CastResult Retrieve(const GRState *state, Loc loc, - QualType T = QualType()); - - const GRState *InvalidateRegion(const GRState *state, const MemRegion *R, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS); - - const GRState *Bind(const GRState *state, Loc L, SVal V) { - return state->makeWithStore(BindInternal(state->getStore(), L, V)); - } - - Store scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, Store St); - - Store BindInternal(Store St, Loc loc, SVal V); - Store Remove(Store St, Loc loc); - Store getInitialStore(const LocationContext *InitLoc); - - // FIXME: Investigate what is using this. This method should be removed. - virtual Loc getLoc(const VarDecl* VD, const LocationContext *LC) { - return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC)); - } - - const GRState *BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr*, - const LocationContext*, - SVal val) { - return state; - } - - SVal getLValueVar(const VarDecl *VD, const LocationContext *LC); - SVal getLValueString(const StringLiteral *S); - SVal getLValueIvar(const ObjCIvarDecl* D, SVal Base); - SVal getLValueField(const FieldDecl *D, SVal Base); - SVal getLValueElement(QualType elementType, SVal Offset, SVal Base); - - /// ArrayToPointer - Used by GRExprEngine::VistCast to handle implicit - /// conversions between arrays and pointers. - SVal ArrayToPointer(Loc Array) { return Array; } - - /// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values. - /// It updatees the GRState object in place with the values removed. - void RemoveDeadBindings(GRState &state, Stmt* Loc, SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); - - void iterBindings(Store store, BindingsHandler& f); - - const GRState *BindDecl(const GRState *state, const VarRegion *VR, - SVal InitVal) { - return state->makeWithStore(BindDeclInternal(state->getStore(), VR, - &InitVal)); - } - - const GRState *BindDeclWithNoInit(const GRState *state, const VarRegion *VR) { - return state->makeWithStore(BindDeclInternal(state->getStore(), VR, 0)); - } - - Store BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); - - static inline BindingsTy GetBindings(Store store) { - return BindingsTy(static_cast<const BindingsTy::TreeTy*>(store)); - } - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - -private: - ASTContext& getContext() { return StateMgr.getContext(); } -}; - -} // end anonymous namespace - - -StoreManager* clang::CreateBasicStoreManager(GRStateManager& StMgr) { - return new BasicStoreManager(StMgr); -} - -SVal BasicStoreManager::getLValueVar(const VarDecl* VD, - const LocationContext *LC) { - return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC)); -} - -SVal BasicStoreManager::getLValueString(const StringLiteral* S) { - return ValMgr.makeLoc(MRMgr.getStringRegion(S)); -} - -SVal BasicStoreManager::getLValueIvar(const ObjCIvarDecl* D, SVal Base) { - - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast<Loc>(Base); - - if (isa<loc::MemRegionVal>(BaseL)) { - const MemRegion *BaseR = cast<loc::MemRegionVal>(BaseL).getRegion(); - return ValMgr.makeLoc(MRMgr.getObjCIvarRegion(D, BaseR)); - } - - return UnknownVal(); -} - -SVal BasicStoreManager::getLValueField(const FieldDecl* D, SVal Base) { - - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast<Loc>(Base); - const MemRegion* BaseR = 0; - - switch(BaseL.getSubKind()) { - case loc::GotoLabelKind: - return UndefinedVal(); - - case loc::MemRegionKind: - BaseR = cast<loc::MemRegionVal>(BaseL).getRegion(); - break; - - case loc::ConcreteIntKind: - // While these seem funny, this can happen through casts. - // FIXME: What we should return is the field offset. For example, - // add the field offset to the integer value. That way funny things - // like this work properly: &(((struct foo *) 0xa)->f) - return Base; - - default: - assert ("Unhandled Base."); - return Base; - } - - return ValMgr.makeLoc(MRMgr.getFieldRegion(D, BaseR)); -} - -SVal BasicStoreManager::getLValueElement(QualType elementType, - SVal Offset, SVal Base) { - - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast<Loc>(Base); - const MemRegion* BaseR = 0; - - switch(BaseL.getSubKind()) { - case loc::GotoLabelKind: - // Technically we can get here if people do funny things with casts. - return UndefinedVal(); - - case loc::MemRegionKind: { - const MemRegion *R = cast<loc::MemRegionVal>(BaseL).getRegion(); - - if (isa<ElementRegion>(R)) { - // int x; - // char* y = (char*) &x; - // 'y' => ElementRegion(0, VarRegion('x')) - // y[0] = 'a'; - return Base; - } - - if (isa<TypedRegion>(R) || isa<SymbolicRegion>(R)) { - BaseR = R; - break; - } - - break; - } - - case loc::ConcreteIntKind: - // While these seem funny, this can happen through casts. - // FIXME: What we should return is the field offset. For example, - // add the field offset to the integer value. That way funny things - // like this work properly: &(((struct foo *) 0xa)->f) - return Base; - - default: - assert ("Unhandled Base."); - return Base; - } - - if (BaseR) { - return ValMgr.makeLoc(MRMgr.getElementRegion(elementType, UnknownVal(), - BaseR, getContext())); - } - else - return UnknownVal(); -} - -static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { - bool foundPointer = false; - while (1) { - const PointerType *PT = T->getAs<PointerType>(); - if (!PT) { - if (!foundPointer) - return false; - - // intptr_t* or intptr_t**, etc? - if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy)) - return true; - - QualType X = C.getCanonicalType(T).getUnqualifiedType(); - return X == C.VoidTy; - } - - foundPointer = true; - T = PT->getPointeeType(); - } -} - -SValuator::CastResult BasicStoreManager::Retrieve(const GRState *state, - Loc loc, QualType T) { - - if (isa<UnknownVal>(loc)) - return SValuator::CastResult(state, UnknownVal()); - - assert(!isa<UndefinedVal>(loc)); - - switch (loc.getSubKind()) { - - case loc::MemRegionKind: { - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) - return SValuator::CastResult(state, UnknownVal()); - - BindingsTy B = GetBindings(state->getStore()); - BindingsTy::data_type *Val = B.lookup(R); - - if (!Val) - break; - - return SValuator::CastResult(state, - CastRetrievedVal(*Val, cast<TypedRegion>(R), T)); - } - - case loc::ConcreteIntKind: - // Some clients may call GetSVal with such an option simply because - // they are doing a quick scan through their Locs (potentially to - // invalidate their bindings). Just return Undefined. - return SValuator::CastResult(state, UndefinedVal()); - - default: - assert (false && "Invalid Loc."); - break; - } - - return SValuator::CastResult(state, UnknownVal()); -} - -Store BasicStoreManager::BindInternal(Store store, Loc loc, SVal V) { - if (isa<loc::ConcreteInt>(loc)) - return store; - - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - ASTContext &C = StateMgr.getContext(); - - // Special case: handle store of pointer values (Loc) to pointers via - // a cast to intXX_t*, void*, etc. This is needed to handle - // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. - if (isa<Loc>(V) || isa<nonloc::LocAsInteger>(V)) - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // FIXME: Should check for index 0. - QualType T = ER->getLocationType(C); - - if (isHigherOrderRawPtr(T, C)) - R = ER->getSuperRegion(); - } - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) - return store; - - const TypedRegion *TyR = cast<TypedRegion>(R); - - // Do not bind to arrays. We need to explicitly check for this so that - // we do not encounter any weirdness of trying to load/store from arrays. - if (TyR->isBoundable() && TyR->getValueType(C)->isArrayType()) - return store; - - if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&V)) { - // Only convert 'V' to a location iff the underlying region type - // is a location as well. - // FIXME: We are allowing a store of an arbitrary location to - // a pointer. We may wish to flag a type error here if the types - // are incompatible. This may also cause lots of breakage - // elsewhere. Food for thought. - if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType(C))) - V = X->getLoc(); - } - - BindingsTy B = GetBindings(store); - return V.isUnknown() - ? VBFactory.Remove(B, R).getRoot() - : VBFactory.Add(B, R, V).getRoot(); -} - -Store BasicStoreManager::Remove(Store store, Loc loc) { - switch (loc.getSubKind()) { - case loc::MemRegionKind: { - const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) - return store; - - return VBFactory.Remove(GetBindings(store), R).getRoot(); - } - default: - assert ("Remove for given Loc type not yet implemented."); - return store; - } -} - -void -BasicStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) -{ - Store store = state.getStore(); - BindingsTy B = GetBindings(store); - typedef SVal::symbol_iterator symbol_iterator; - - // Iterate over the variable bindings. - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - if (const VarRegion *VR = dyn_cast<VarRegion>(I.getKey())) { - if (SymReaper.isLive(Loc, VR)) - RegionRoots.push_back(VR); - else - continue; - } - else if (isa<ObjCIvarRegion>(I.getKey())) { - RegionRoots.push_back(I.getKey()); - } - else - continue; - - // Mark the bindings in the data as live. - SVal X = I.getData(); - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.markLive(*SI); - } - - // Scan for live variables and live symbols. - llvm::SmallPtrSet<const MemRegion*, 10> Marked; - - while (!RegionRoots.empty()) { - const MemRegion* MR = RegionRoots.back(); - RegionRoots.pop_back(); - - while (MR) { - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(MR)) { - SymReaper.markLive(SymR->getSymbol()); - break; - } - else if (isa<VarRegion>(MR) || isa<ObjCIvarRegion>(MR)) { - if (Marked.count(MR)) - break; - - Marked.insert(MR); - SVal X = Retrieve(&state, loc::MemRegionVal(MR)).getSVal(); - - // FIXME: We need to handle symbols nested in region definitions. - for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI) - SymReaper.markLive(*SI); - - if (!isa<loc::MemRegionVal>(X)) - break; - - const loc::MemRegionVal& LVD = cast<loc::MemRegionVal>(X); - RegionRoots.push_back(LVD.getRegion()); - break; - } - else if (const SubRegion* R = dyn_cast<SubRegion>(MR)) - MR = R->getSuperRegion(); - else - break; - } - } - - // Remove dead variable bindings. - for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { - const MemRegion* R = I.getKey(); - - if (!Marked.count(R)) { - store = Remove(store, ValMgr.makeLoc(R)); - SVal X = I.getData(); - - for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) - SymReaper.maybeDead(*SI); - } - } - - // Write the store back. - state.setStore(store); -} - -Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, - const MemRegion *SelfRegion, Store St) { - for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end(); - CI != CE; ++CI) { - - if (!*CI) - continue; - - // Check if the statement is an ivar reference. We only - // care about self.ivar. - if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(*CI)) { - const Expr *Base = IV->getBase()->IgnoreParenCasts(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Base)) { - if (DR->getDecl() == SelfDecl) { - const MemRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(), - SelfRegion); - SVal X = ValMgr.getRegionValueSymbolVal(IVR); - St = BindInternal(St, ValMgr.makeLoc(IVR), X); - } - } - } - else - St = scanForIvars(*CI, SelfDecl, SelfRegion, St); - } - - return St; -} - -Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { - // The LiveVariables information already has a compilation of all VarDecls - // used in the function. Iterate through this set, and "symbolicate" - // any VarDecl whose value originally comes from outside the function. - typedef LiveVariables::AnalysisDataTy LVDataTy; - LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData(); - Store St = VBFactory.GetEmptyMap().getRoot(); - - for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { - NamedDecl* ND = const_cast<NamedDecl*>(I->first); - - // Handle implicit parameters. - if (ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) { - const Decl& CD = *InitLoc->getDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) { - if (MD->getSelfDecl() == PD) { - // FIXME: Add type constraints (when they become available) to - // SelfRegion? (i.e., it implements MD->getClassInterface()). - const MemRegion *VR = MRMgr.getVarRegion(PD, InitLoc); - const MemRegion *SelfRegion = - ValMgr.getRegionValueSymbolVal(VR).getAsRegion(); - assert(SelfRegion); - St = BindInternal(St, ValMgr.makeLoc(VR), - loc::MemRegionVal(SelfRegion)); - // Scan the method for ivar references. While this requires an - // entire AST scan, the cost should not be high in practice. - St = scanForIvars(MD->getBody(), PD, SelfRegion, St); - } - } - } - else if (VarDecl* VD = dyn_cast<VarDecl>(ND)) { - // Only handle simple types that we can symbolicate. - if (!SymbolManager::canSymbolicate(VD->getType())) - continue; - - // Initialize globals and parameters to symbolic values. - // Initialize local variables to undefined. - const MemRegion *R = ValMgr.getRegionManager().getVarRegion(VD, InitLoc); - SVal X = UndefinedVal(); - if (R->hasGlobalsOrParametersStorage()) - X = ValMgr.getRegionValueSymbolVal(R); - - St = BindInternal(St, ValMgr.makeLoc(R), X); - } - } - return St; -} - -Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, - SVal* InitVal) { - - BasicValueFactory& BasicVals = StateMgr.getBasicVals(); - const VarDecl *VD = VR->getDecl(); - - // BasicStore does not model arrays and structs. - if (VD->getType()->isArrayType() || VD->getType()->isStructureType()) - return store; - - if (VD->hasGlobalStorage()) { - // Handle variables with global storage: extern, static, PrivateExtern. - - // FIXME:: static variables may have an initializer, but the second time a - // function is called those values may not be current. Currently, a function - // will not be called more than once. - - // Static global variables should not be visited here. - assert(!(VD->getStorageClass() == VarDecl::Static && - VD->isFileVarDecl())); - - // Process static variables. - if (VD->getStorageClass() == VarDecl::Static) { - // C99: 6.7.8 Initialization - // If an object that has static storage duration is not initialized - // explicitly, then: - // —if it has pointer type, it is initialized to a null pointer; - // —if it has arithmetic type, it is initialized to (positive or - // unsigned) zero; - if (!InitVal) { - QualType T = VD->getType(); - if (Loc::IsLocType(T)) - store = BindInternal(store, loc::MemRegionVal(VR), - loc::ConcreteInt(BasicVals.getValue(0, T))); - else if (T->isIntegerType()) - store = BindInternal(store, loc::MemRegionVal(VR), - nonloc::ConcreteInt(BasicVals.getValue(0, T))); - else { - // assert(0 && "ignore other types of variables"); - } - } else { - store = BindInternal(store, loc::MemRegionVal(VR), *InitVal); - } - } - } else { - // Process local scalar variables. - QualType T = VD->getType(); - if (ValMgr.getSymbolManager().canSymbolicate(T)) { - SVal V = InitVal ? *InitVal : UndefinedVal(); - store = BindInternal(store, loc::MemRegionVal(VR), V); - } - } - - return store; -} - -void BasicStoreManager::print(Store store, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - - BindingsTy B = GetBindings(store); - Out << "Variables:" << nl; - - bool isFirst = true; - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) { - if (isFirst) - isFirst = false; - else - Out << nl; - - Out << ' ' << I.getKey() << " : " << I.getData(); - } -} - - -void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) { - BindingsTy B = GetBindings(store); - - for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) - f.HandleBinding(*this, store, I.getKey(), I.getData()); - -} - -StoreManager::BindingsHandler::~BindingsHandler() {} - -//===----------------------------------------------------------------------===// -// Binding invalidation. -//===----------------------------------------------------------------------===// - -const GRState *BasicStoreManager::InvalidateRegion(const GRState *state, - const MemRegion *R, - const Expr *E, - unsigned Count, - InvalidatedSymbols *IS) { - R = R->StripCasts(); - - if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) - return state; - - if (IS) { - BindingsTy B = GetBindings(state->getStore()); - if (BindingsTy::data_type *Val = B.lookup(R)) { - if (SymbolRef Sym = Val->getAsSymbol()) - IS->insert(Sym); - } - } - - QualType T = cast<TypedRegion>(R)->getValueType(R->getContext()); - SVal V = ValMgr.getConjuredSymbolVal(R, E, T, Count); - return Bind(state, loc::MemRegionVal(R), V); -} - diff --git a/lib/Analysis/BasicValueFactory.cpp b/lib/Analysis/BasicValueFactory.cpp deleted file mode 100644 index b33c277..0000000 --- a/lib/Analysis/BasicValueFactory.cpp +++ /dev/null @@ -1,290 +0,0 @@ -//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- 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 BasicValueFactory, a class that manages the lifetime -// of APSInt objects and symbolic constraints used by GRExprEngine -// and related classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BasicValueFactory.h" - -using namespace clang; - -void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, - llvm::ImmutableList<SVal> L) { - T.Profile(ID); - ID.AddPointer(L.getInternalPointer()); -} - -void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, - const GRState *state, - const TypedRegion *region) { - ID.AddPointer(state); - ID.AddPointer(region); -} - -typedef std::pair<SVal, uintptr_t> SValData; -typedef std::pair<SVal, SVal> SValPair; - -namespace llvm { -template<> struct FoldingSetTrait<SValData> { - static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) { - X.first.Profile(ID); - ID.AddPointer( (void*) X.second); - } -}; - -template<> struct FoldingSetTrait<SValPair> { - static inline void Profile(const SValPair& X, llvm::FoldingSetNodeID& ID) { - X.first.Profile(ID); - X.second.Profile(ID); - } -}; -} - -typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData> > - PersistentSValsTy; - -typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair> > - PersistentSValPairsTy; - -BasicValueFactory::~BasicValueFactory() { - // Note that the dstor for the contents of APSIntSet will never be called, - // so we iterate over the set and invoke the dstor for each APSInt. This - // frees an aux. memory allocated to represent very large constants. - for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I) - I->getValue().~APSInt(); - - delete (PersistentSValsTy*) PersistentSVals; - delete (PersistentSValPairsTy*) PersistentSValPairs; -} - -const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { - llvm::FoldingSetNodeID ID; - void* InsertPos; - typedef llvm::FoldingSetNodeWrapper<llvm::APSInt> FoldNodeTy; - - X.Profile(ID); - FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!P) { - P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>(); - new (P) FoldNodeTy(X); - APSIntSet.InsertNode(P, InsertPos); - } - - return *P; -} - -const llvm::APSInt& BasicValueFactory::getValue(const llvm::APInt& X, - bool isUnsigned) { - llvm::APSInt V(X, isUnsigned); - return getValue(V); -} - -const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth, - bool isUnsigned) { - llvm::APSInt V(BitWidth, isUnsigned); - V = X; - return getValue(V); -} - -const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) { - - unsigned bits = Ctx.getTypeSize(T); - llvm::APSInt V(bits, T->isUnsignedIntegerType() || Loc::IsLocType(T)); - V = X; - return getValue(V); -} - -const CompoundValData* -BasicValueFactory::getCompoundValData(QualType T, - llvm::ImmutableList<SVal> Vals) { - - llvm::FoldingSetNodeID ID; - CompoundValData::Profile(ID, T, Vals); - void* InsertPos; - - CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!D) { - D = (CompoundValData*) BPAlloc.Allocate<CompoundValData>(); - new (D) CompoundValData(T, Vals); - CompoundValDataSet.InsertNode(D, InsertPos); - } - - return D; -} - -const LazyCompoundValData* -BasicValueFactory::getLazyCompoundValData(const GRState *state, - const TypedRegion *region) { - llvm::FoldingSetNodeID ID; - LazyCompoundValData::Profile(ID, state, region); - void* InsertPos; - - LazyCompoundValData *D = - LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!D) { - D = (LazyCompoundValData*) BPAlloc.Allocate<LazyCompoundValData>(); - new (D) LazyCompoundValData(state, region); - LazyCompoundValDataSet.InsertNode(D, InsertPos); - } - - return D; -} - -const llvm::APSInt* -BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op, - const llvm::APSInt& V1, const llvm::APSInt& V2) { - - switch (Op) { - default: - assert (false && "Invalid Opcode."); - - case BinaryOperator::Mul: - return &getValue( V1 * V2 ); - - case BinaryOperator::Div: - return &getValue( V1 / V2 ); - - case BinaryOperator::Rem: - return &getValue( V1 % V2 ); - - case BinaryOperator::Add: - return &getValue( V1 + V2 ); - - case BinaryOperator::Sub: - return &getValue( V1 - V2 ); - - case BinaryOperator::Shl: { - - // FIXME: This logic should probably go higher up, where we can - // test these conditions symbolically. - - // FIXME: Expand these checks to include all undefined behavior. - - if (V2.isSigned() && V2.isNegative()) - return NULL; - - uint64_t Amt = V2.getZExtValue(); - - if (Amt > V1.getBitWidth()) - return NULL; - - return &getValue( V1.operator<<( (unsigned) Amt )); - } - - case BinaryOperator::Shr: { - - // FIXME: This logic should probably go higher up, where we can - // test these conditions symbolically. - - // FIXME: Expand these checks to include all undefined behavior. - - if (V2.isSigned() && V2.isNegative()) - return NULL; - - uint64_t Amt = V2.getZExtValue(); - - if (Amt > V1.getBitWidth()) - return NULL; - - return &getValue( V1.operator>>( (unsigned) Amt )); - } - - case BinaryOperator::LT: - return &getTruthValue( V1 < V2 ); - - case BinaryOperator::GT: - return &getTruthValue( V1 > V2 ); - - case BinaryOperator::LE: - return &getTruthValue( V1 <= V2 ); - - case BinaryOperator::GE: - return &getTruthValue( V1 >= V2 ); - - case BinaryOperator::EQ: - return &getTruthValue( V1 == V2 ); - - case BinaryOperator::NE: - return &getTruthValue( V1 != V2 ); - - // Note: LAnd, LOr, Comma are handled specially by higher-level logic. - - case BinaryOperator::And: - return &getValue( V1 & V2 ); - - case BinaryOperator::Or: - return &getValue( V1 | V2 ); - - case BinaryOperator::Xor: - return &getValue( V1 ^ V2 ); - } -} - - -const std::pair<SVal, uintptr_t>& -BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { - - // Lazily create the folding set. - if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); - - llvm::FoldingSetNodeID ID; - void* InsertPos; - V.Profile(ID); - ID.AddPointer((void*) Data); - - PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals); - - typedef llvm::FoldingSetNodeWrapper<SValData> FoldNodeTy; - FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); - - if (!P) { - P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>(); - new (P) FoldNodeTy(std::make_pair(V, Data)); - Map.InsertNode(P, InsertPos); - } - - return P->getValue(); -} - -const std::pair<SVal, SVal>& -BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { - - // Lazily create the folding set. - if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); - - llvm::FoldingSetNodeID ID; - void* InsertPos; - V1.Profile(ID); - V2.Profile(ID); - - PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs); - - typedef llvm::FoldingSetNodeWrapper<SValPair> FoldNodeTy; - FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); - - if (!P) { - P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>(); - new (P) FoldNodeTy(std::make_pair(V1, V2)); - Map.InsertNode(P, InsertPos); - } - - return P->getValue(); -} - -const SVal* BasicValueFactory::getPersistentSVal(SVal X) { - return &getPersistentSValWithData(X, 0).first; -} - - diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp deleted file mode 100644 index 2a9531d..0000000 --- a/lib/Analysis/BugReporter.cpp +++ /dev/null @@ -1,1879 +0,0 @@ -// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- 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 BugReporter, a utility class for generating -// PathDiagnostics. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/AST/ASTContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/StmtObjC.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Analysis/ProgramPoint.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/OwningPtr.h" -#include <queue> - -using namespace clang; - -BugReporterVisitor::~BugReporterVisitor() {} -BugReporterContext::~BugReporterContext() { - for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) - if ((*I)->isOwnedByReporterContext()) delete *I; -} - -//===----------------------------------------------------------------------===// -// Helper routines for walking the ExplodedGraph and fetching statements. -//===----------------------------------------------------------------------===// - -static inline const Stmt* GetStmt(ProgramPoint P) { - if (const StmtPoint* SP = dyn_cast<StmtPoint>(&P)) - return SP->getStmt(); - else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) - return BE->getSrc()->getTerminator(); - - return 0; -} - -static inline const ExplodedNode* -GetPredecessorNode(const ExplodedNode* N) { - return N->pred_empty() ? NULL : *(N->pred_begin()); -} - -static inline const ExplodedNode* -GetSuccessorNode(const ExplodedNode* N) { - return N->succ_empty() ? NULL : *(N->succ_begin()); -} - -static const Stmt* GetPreviousStmt(const ExplodedNode* N) { - for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return 0; -} - -static const Stmt* GetNextStmt(const ExplodedNode* N) { - for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) - if (const Stmt *S = GetStmt(N->getLocation())) { - // Check if the statement is '?' or '&&'/'||'. These are "merges", - // not actual statement points. - switch (S->getStmtClass()) { - case Stmt::ChooseExprClass: - case Stmt::ConditionalOperatorClass: continue; - case Stmt::BinaryOperatorClass: { - BinaryOperator::Opcode Op = cast<BinaryOperator>(S)->getOpcode(); - if (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr) - continue; - break; - } - default: - break; - } - - // Some expressions don't have locations. - if (S->getLocStart().isInvalid()) - continue; - - return S; - } - - return 0; -} - -static inline const Stmt* -GetCurrentOrPreviousStmt(const ExplodedNode* N) { - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return GetPreviousStmt(N); -} - -static inline const Stmt* -GetCurrentOrNextStmt(const ExplodedNode* N) { - if (const Stmt *S = GetStmt(N->getLocation())) - return S; - - return GetNextStmt(N); -} - -//===----------------------------------------------------------------------===// -// PathDiagnosticBuilder and its associated routines and helper objects. -//===----------------------------------------------------------------------===// - -typedef llvm::DenseMap<const ExplodedNode*, -const ExplodedNode*> NodeBackMap; - -namespace { -class NodeMapClosure : public BugReport::NodeResolver { - NodeBackMap& M; -public: - NodeMapClosure(NodeBackMap *m) : M(*m) {} - ~NodeMapClosure() {} - - const ExplodedNode* getOriginalNode(const ExplodedNode* N) { - NodeBackMap::iterator I = M.find(N); - return I == M.end() ? 0 : I->second; - } -}; - -class PathDiagnosticBuilder : public BugReporterContext { - BugReport *R; - PathDiagnosticClient *PDC; - llvm::OwningPtr<ParentMap> PM; - NodeMapClosure NMC; -public: - PathDiagnosticBuilder(GRBugReporter &br, - BugReport *r, NodeBackMap *Backmap, - PathDiagnosticClient *pdc) - : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap) { - addVisitor(R); - } - - PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N); - - PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N); - - Decl const &getCodeDecl() { return R->getEndNode()->getCodeDecl(); } - - ParentMap& getParentMap() { return R->getEndNode()->getParentMap(); } - - const Stmt *getParent(const Stmt *S) { - return getParentMap().getParent(S); - } - - virtual NodeMapClosure& getNodeResolver() { return NMC; } - BugReport& getReport() { return *R; } - - PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - - PathDiagnosticLocation - getEnclosingStmtLocation(const PathDiagnosticLocation &L) { - if (const Stmt *S = L.asStmt()) - return getEnclosingStmtLocation(S); - - return L; - } - - PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; - } - - bool supportsLogicalOpControlFlow() const { - return PDC ? PDC->supportsLogicalOpControlFlow() : true; - } -}; -} // end anonymous namespace - -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) { - if (const Stmt *S = GetNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager()); - - return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(), - getSourceManager()); -} - -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, - const ExplodedNode* N) { - - // Slow, but probably doesn't matter. - if (os.str().empty()) - os << ' '; - - const PathDiagnosticLocation &Loc = ExecutionContinues(N); - - if (Loc.asStmt()) - os << "Execution continues on line " - << getSourceManager().getInstantiationLineNumber(Loc.asLocation()) - << '.'; - else { - os << "Execution jumps to the end of the "; - const Decl *D = N->getLocationContext()->getDecl(); - if (isa<ObjCMethodDecl>(D)) - os << "method"; - else if (isa<FunctionDecl>(D)) - os << "function"; - else { - assert(isa<BlockDecl>(D)); - os << "anonymous block"; - } - os << '.'; - } - - return Loc; -} - -static bool IsNested(const Stmt *S, ParentMap &PM) { - if (isa<Expr>(S) && PM.isConsumedExpr(cast<Expr>(S))) - return true; - - const Stmt *Parent = PM.getParentIgnoreParens(S); - - if (Parent) - switch (Parent->getStmtClass()) { - case Stmt::ForStmtClass: - case Stmt::DoStmtClass: - case Stmt::WhileStmtClass: - return true; - default: - break; - } - - return false; -} - -PathDiagnosticLocation -PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt* passed to getEnclosingStmtLocation"); - ParentMap &P = getParentMap(); - SourceManager &SMgr = getSourceManager(); - - while (IsNested(S, P)) { - const Stmt *Parent = P.getParentIgnoreParens(S); - - if (!Parent) - break; - - switch (Parent->getStmtClass()) { - case Stmt::BinaryOperatorClass: { - const BinaryOperator *B = cast<BinaryOperator>(Parent); - if (B->isLogicalOp()) - return PathDiagnosticLocation(S, SMgr); - break; - } - case Stmt::CompoundStmtClass: - case Stmt::StmtExprClass: - return PathDiagnosticLocation(S, SMgr); - case Stmt::ChooseExprClass: - // Similar to '?' if we are referring to condition, just have the edge - // point to the entire choose expression. - if (cast<ChooseExpr>(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); - else - return PathDiagnosticLocation(S, SMgr); - case Stmt::ConditionalOperatorClass: - // For '?', if we are referring to condition, just have the edge point - // to the entire '?' expression. - if (cast<ConditionalOperator>(Parent)->getCond() == S) - return PathDiagnosticLocation(Parent, SMgr); - else - return PathDiagnosticLocation(S, SMgr); - case Stmt::DoStmtClass: - return PathDiagnosticLocation(S, SMgr); - case Stmt::ForStmtClass: - if (cast<ForStmt>(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); - break; - case Stmt::IfStmtClass: - if (cast<IfStmt>(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); - break; - case Stmt::ObjCForCollectionStmtClass: - if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S) - return PathDiagnosticLocation(S, SMgr); - break; - case Stmt::WhileStmtClass: - if (cast<WhileStmt>(Parent)->getCond() != S) - return PathDiagnosticLocation(S, SMgr); - break; - default: - break; - } - - S = Parent; - } - - assert(S && "Cannot have null Stmt for PathDiagnosticLocation"); - - // Special case: DeclStmts can appear in for statement declarations, in which - // case the ForStmt is the context. - if (isa<DeclStmt>(S)) { - if (const Stmt *Parent = P.getParent(S)) { - switch (Parent->getStmtClass()) { - case Stmt::ForStmtClass: - case Stmt::ObjCForCollectionStmtClass: - return PathDiagnosticLocation(Parent, SMgr); - default: - break; - } - } - } - else if (isa<BinaryOperator>(S)) { - // Special case: the binary operator represents the initialization - // code in a for statement (this can happen when the variable being - // initialized is an old variable. - if (const ForStmt *FS = - dyn_cast_or_null<ForStmt>(P.getParentIgnoreParens(S))) { - if (FS->getInit() == S) - return PathDiagnosticLocation(FS, SMgr); - } - } - - return PathDiagnosticLocation(S, SMgr); -} - -//===----------------------------------------------------------------------===// -// ScanNotableSymbols: closure-like callback for scanning Store bindings. -//===----------------------------------------------------------------------===// - -static const VarDecl* -GetMostRecentVarDeclBinding(const ExplodedNode* N, - GRStateManager& 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 GRState* PrevSt; - const Stmt* S; - GRStateManager& VMgr; - const ExplodedNode* Pred; - PathDiagnostic& PD; - BugReporter& BR; - -public: - - NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, - GRStateManager& 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 contruction 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. - FullSourceLoc L(S->getLocStart(), BR.getSourceManager()); - - if (Loc::IsLocType(VD->getType())) { - std::string msg = "'" + std::string(VD->getNameAsString()) + - "' now aliases '" + MostRecent->getNameAsString() + "'"; - - PD.push_front(new PathDiagnosticEventPiece(L, msg)); - } - - return true; - } -}; -} - -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 GRState* 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? - GRStateManager& 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 GenerateMinimalPathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { - - SourceManager& SMgr = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() - ? NULL : *(N->pred_begin()); - while (NextNode) { - N = NextNode; - NextNode = GetPredecessorNode(N); - - ProgramPoint P = N->getLocation(); - - if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) { - CFGBlock* Src = BE->getSrc(); - CFGBlock* Dst = BE->getDst(); - Stmt* T = Src->getTerminator(); - - if (!T) - continue; - - FullSourceLoc Start(T->getLocStart(), SMgr); - - switch (T->getStmtClass()) { - default: - break; - - case Stmt::GotoStmtClass: - case Stmt::IndirectGotoStmtClass: { - const Stmt* S = GetNextStmt(N); - - if (!S) - continue; - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); - - os << "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - break; - } - - case Stmt::SwitchStmtClass: { - // Figure out what case arm we took. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (Stmt* S = Dst->getLabel()) { - PathDiagnosticLocation End(S, SMgr); - - switch (S->getStmtClass()) { - default: - os << "No cases match in the switch statement. " - "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); - break; - case Stmt::DefaultStmtClass: - os << "Control jumps to the 'default' case at line " - << End.asLocation().getInstantiationLineNumber(); - break; - - case Stmt::CaseStmtClass: { - os << "Control jumps to 'case "; - CaseStmt* Case = cast<CaseStmt>(S); - Expr* LHS = Case->getLHS()->IgnoreParenCasts(); - - // Determine if it is an enum. - bool GetRawInt = true; - - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) { - // FIXME: Maybe this should be an assertion. Are there cases - // were it is not an EnumConstantDecl? - EnumConstantDecl* D = - dyn_cast<EnumConstantDecl>(DR->getDecl()); - - if (D) { - GetRawInt = false; - os << D->getNameAsString(); - } - } - - if (GetRawInt) - os << LHS->EvaluateAsInt(PDB.getASTContext()); - - os << ":' at line " - << End.asLocation().getInstantiationLineNumber(); - break; - } - } - PD.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, - os.str())); - } - - break; - } - - case Stmt::BreakStmtClass: - case Stmt::ContinueStmtClass: { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - break; - } - - // Determine control-flow for ternary '?'. - case Stmt::ConditionalOperatorClass: { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "'?' condition is "; - - if (*(Src->succ_begin()+1) == Dst) - os << "false"; - else - os << "true"; - - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - break; - } - - // Determine control-flow for short-circuited '&&' and '||'. - case Stmt::BinaryOperatorClass: { - if (!PDB.supportsLogicalOpControlFlow()) - break; - - BinaryOperator *B = cast<BinaryOperator>(T); - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Left side of '"; - - if (B->getOpcode() == BinaryOperator::LAnd) { - os << "&&" << "' is "; - - if (*(Src->succ_begin()+1) == Dst) { - os << "false"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - os << "true"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - } - else { - assert(B->getOpcode() == BinaryOperator::LOr); - os << "||" << "' is "; - - if (*(Src->succ_begin()+1) == Dst) { - os << "false"; - PathDiagnosticLocation Start(B->getLHS(), SMgr); - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - os << "true"; - PathDiagnosticLocation End(B->getLHS(), SMgr); - PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - } - - break; - } - - case Stmt::DoStmtClass: { - if (*(Src->succ_begin()) == Dst) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Loop condition is true. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Loop condition is false. Exiting loop")); - } - - break; - } - - case Stmt::WhileStmtClass: - case Stmt::ForStmtClass: { - if (*(Src->succ_begin()+1) == Dst) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Loop condition is false. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - os.str())); - } - else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Loop condition is true. Entering loop body")); - } - - break; - } - - case Stmt::IfStmtClass: { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - if (*(Src->succ_begin()+1) == Dst) - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Taking false branch")); - else - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Taking true branch")); - - break; - } - } - } - - if (NextNode) { - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) - PD.push_front(p); - } - } - - 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()); -} - -//===----------------------------------------------------------------------===// -// "Extensive" PathDiagnostic generation. -//===----------------------------------------------------------------------===// - -static bool IsControlFlowExpr(const Stmt *S) { - const Expr *E = dyn_cast<Expr>(S); - - if (!E) - return false; - - E = E->IgnoreParenCasts(); - - if (isa<ConditionalOperator>(E)) - return true; - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) - if (B->isLogicalOp()) - return true; - - return false; -} - -namespace { -class ContextLocation : public PathDiagnosticLocation { - bool IsDead; -public: - ContextLocation(const PathDiagnosticLocation &L, bool isdead = false) - : PathDiagnosticLocation(L), IsDead(isdead) {} - - void markDead() { IsDead = true; } - bool isDead() const { return IsDead; } -}; - -class EdgeBuilder { - std::vector<ContextLocation> CLocs; - typedef std::vector<ContextLocation>::iterator iterator; - PathDiagnostic &PD; - PathDiagnosticBuilder &PDB; - PathDiagnosticLocation PrevLoc; - - bool IsConsumedExpr(const PathDiagnosticLocation &L); - - bool containsLocation(const PathDiagnosticLocation &Container, - const PathDiagnosticLocation &Containee); - - PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L); - - PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, - bool firstCharOnly = false) { - if (const Stmt *S = L.asStmt()) { - const Stmt *Original = S; - while (1) { - // Adjust the location for some expressions that are best referenced - // by one of their subexpressions. - switch (S->getStmtClass()) { - default: - break; - case Stmt::ParenExprClass: - S = cast<ParenExpr>(S)->IgnoreParens(); - firstCharOnly = true; - continue; - case Stmt::ConditionalOperatorClass: - S = cast<ConditionalOperator>(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::ChooseExprClass: - S = cast<ChooseExpr>(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::BinaryOperatorClass: - S = cast<BinaryOperator>(S)->getLHS(); - firstCharOnly = true; - continue; - } - - break; - } - - if (S != Original) - L = PathDiagnosticLocation(S, L.getManager()); - } - - if (firstCharOnly) - L = PathDiagnosticLocation(L.asLocation()); - - return L; - } - - void popLocation() { - if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) { - // For contexts, we only one the first character as the range. - rawAddEdge(cleanUpLocation(CLocs.back(), true)); - } - CLocs.pop_back(); - } - - PathDiagnosticLocation IgnoreParens(const PathDiagnosticLocation &L); - -public: - EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb) - : PD(pd), PDB(pdb) { - - // 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 (const Stmt *S = PrevLoc.asStmt()) - addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - } - - ~EdgeBuilder() { - while (!CLocs.empty()) popLocation(); - - // Finally, add an initial edge from the start location of the first - // statement (if it doesn't already exist). - // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const CompoundStmt *CS = - PDB.getCodeDecl().getCompoundBody()) - if (!CS->body_empty()) { - SourceLocation Loc = (*CS->body_begin())->getLocStart(); - rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager())); - } - - } - - void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); - - void addEdge(const Stmt *S, bool alwaysAdd = false) { - addEdge(PathDiagnosticLocation(S, PDB.getSourceManager()), alwaysAdd); - } - - void rawAddEdge(PathDiagnosticLocation NewLoc); - - void addContext(const Stmt *S); - void addExtendedContext(const Stmt *S); -}; -} // end anonymous namespace - - -PathDiagnosticLocation -EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) { - if (const Stmt *S = L.asStmt()) { - if (IsControlFlowExpr(S)) - return L; - - return PDB.getEnclosingStmtLocation(S); - } - - return L; -} - -bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, - const PathDiagnosticLocation &Containee) { - - if (Container == Containee) - return true; - - if (Container.asDecl()) - return true; - - if (const Stmt *S = Containee.asStmt()) - if (const Stmt *ContainerS = Container.asStmt()) { - while (S) { - if (S == ContainerS) - return true; - S = PDB.getParent(S); - } - return false; - } - - // Less accurate: compare using source ranges. - SourceRange ContainerR = Container.asRange(); - SourceRange ContaineeR = Containee.asRange(); - - SourceManager &SM = PDB.getSourceManager(); - SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin()); - SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd()); - SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin()); - SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd()); - - unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg); - unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd); - unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg); - unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd); - - assert(ContainerBegLine <= ContainerEndLine); - assert(ContaineeBegLine <= ContaineeEndLine); - - return (ContainerBegLine <= ContaineeBegLine && - ContainerEndLine >= ContaineeEndLine && - (ContainerBegLine != ContaineeBegLine || - SM.getInstantiationColumnNumber(ContainerRBeg) <= - SM.getInstantiationColumnNumber(ContaineeRBeg)) && - (ContainerEndLine != ContaineeEndLine || - SM.getInstantiationColumnNumber(ContainerREnd) >= - SM.getInstantiationColumnNumber(ContainerREnd))); -} - -PathDiagnosticLocation -EdgeBuilder::IgnoreParens(const PathDiagnosticLocation &L) { - if (const Expr* E = dyn_cast_or_null<Expr>(L.asStmt())) - return PathDiagnosticLocation(E->IgnoreParenCasts(), - PDB.getSourceManager()); - return L; -} - -void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { - if (!PrevLoc.isValid()) { - PrevLoc = NewLoc; - return; - } - - const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc); - const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc); - - if (NewLocClean.asLocation() == PrevLocClean.asLocation()) - return; - - // FIXME: Ignore intra-macro edges for now. - if (NewLocClean.asLocation().getInstantiationLoc() == - PrevLocClean.asLocation().getInstantiationLoc()) - return; - - PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); - PrevLoc = NewLoc; -} - -void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { - - if (!alwaysAdd && NewLoc.asLocation().isMacroID()) - return; - - const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc); - - while (!CLocs.empty()) { - ContextLocation &TopContextLoc = CLocs.back(); - - // Is the top location context the same as the one for the new location? - if (TopContextLoc == CLoc) { - if (alwaysAdd) { - if (IsConsumedExpr(TopContextLoc) && - !IsControlFlowExpr(TopContextLoc.asStmt())) - TopContextLoc.markDead(); - - rawAddEdge(NewLoc); - } - - return; - } - - if (containsLocation(TopContextLoc, CLoc)) { - if (alwaysAdd) { - rawAddEdge(NewLoc); - - if (IsConsumedExpr(CLoc) && !IsControlFlowExpr(CLoc.asStmt())) { - CLocs.push_back(ContextLocation(CLoc, true)); - return; - } - } - - CLocs.push_back(CLoc); - return; - } - - // Context does not contain the location. Flush it. - popLocation(); - } - - // If we reach here, there is no enclosing context. Just add the edge. - rawAddEdge(NewLoc); -} - -bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) { - if (const Expr *X = dyn_cast_or_null<Expr>(L.asStmt())) - return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X); - - return false; -} - -void EdgeBuilder::addExtendedContext(const Stmt *S) { - if (!S) - return; - - const Stmt *Parent = PDB.getParent(S); - while (Parent) { - if (isa<CompoundStmt>(Parent)) - Parent = PDB.getParent(Parent); - else - break; - } - - if (Parent) { - switch (Parent->getStmtClass()) { - case Stmt::DoStmtClass: - case Stmt::ObjCAtSynchronizedStmtClass: - addContext(Parent); - default: - break; - } - } - - addContext(S); -} - -void EdgeBuilder::addContext(const Stmt *S) { - if (!S) - return; - - PathDiagnosticLocation L(S, PDB.getSourceManager()); - - while (!CLocs.empty()) { - const PathDiagnosticLocation &TopContextLoc = CLocs.back(); - - // Is the top location context the same as the one for the new location? - if (TopContextLoc == L) - return; - - if (containsLocation(TopContextLoc, L)) { - CLocs.push_back(L); - return; - } - - // Context does not contain the location. Flush it. - popLocation(); - } - - CLocs.push_back(L); -} - -static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { - - - EdgeBuilder EB(PD, PDB); - - const ExplodedNode* NextNode = N->pred_empty() - ? NULL : *(N->pred_begin()); - while (NextNode) { - N = NextNode; - NextNode = GetPredecessorNode(N); - ProgramPoint P = N->getLocation(); - - do { - // Block edges. - 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, PDB.getSourceManager()); - const CompoundStmt *CS = NULL; - - if (!Term) { - if (const ForStmt *FS = dyn_cast<ForStmt>(Loop)) - CS = dyn_cast<CompoundStmt>(FS->getBody()); - else if (const WhileStmt *WS = dyn_cast<WhileStmt>(Loop)) - CS = dyn_cast<CompoundStmt>(WS->getBody()); - } - - PathDiagnosticEventPiece *p = - new PathDiagnosticEventPiece(L, - "Looping back to the head of the loop"); - - EB.addEdge(p->getLocation(), true); - PD.push_front(p); - - if (CS) { - PathDiagnosticLocation BL(CS->getRBracLoc(), - PDB.getSourceManager()); - BL = PathDiagnosticLocation(BL.asLocation()); - EB.addEdge(BL); - } - } - - if (Term) - EB.addContext(Term); - - break; - } - - if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { - if (const Stmt* S = BE->getFirstStmt()) { - if (IsControlFlowExpr(S)) { - // Add the proper context for '&&', '||', and '?'. - EB.addContext(S); - } - else - EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - - break; - } - } while (0); - - if (!NextNode) - continue; - - for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), - E = PDB.visitor_end(); I!=E; ++I) { - if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) { - const PathDiagnosticLocation &Loc = p->getLocation(); - EB.addEdge(Loc, true); - PD.push_front(p); - if (const Stmt *S = Loc.asStmt()) - EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - } - } -} - -//===----------------------------------------------------------------------===// -// Methods for BugType and subclasses. -//===----------------------------------------------------------------------===// -BugType::~BugType() { - // Free up the equivalence class objects. Observe that we get a pointer to - // the object first before incrementing the iterator, as destroying the - // node before doing so means we will read from freed memory. - for (iterator I = begin(), E = end(); I !=E; ) { - BugReportEquivClass *EQ = &*I; - ++I; - delete EQ; - } -} -void BugType::FlushReports(BugReporter &BR) {} - -//===----------------------------------------------------------------------===// -// Methods for BugReport and subclasses. -//===----------------------------------------------------------------------===// -BugReport::~BugReport() {} -RangedBugReport::~RangedBugReport() {} - -const Stmt* BugReport::getStmt() const { - ProgramPoint ProgP = EndNode->getLocation(); - const Stmt *S = NULL; - - if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP)) { - CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); - if (BE->getBlock() == &Exit) - S = GetPreviousStmt(EndNode); - } - if (!S) - S = GetStmt(ProgP); - - return S; -} - -PathDiagnosticPiece* -BugReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndPathNode) { - - const Stmt* S = getStmt(); - - if (!S) - return NULL; - - const SourceRange *Beg, *End; - getRanges(Beg, End); - PathDiagnosticLocation L(S, BRC.getSourceManager()); - - // Only add the statement itself as a range if we didn't specify any - // special ranges for this report. - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(), - Beg == End); - - for (; Beg != End; ++Beg) - P->addRange(*Beg); - - return P; -} - -void BugReport::getRanges(const SourceRange*& beg, const SourceRange*& end) { - if (const Expr* E = dyn_cast_or_null<Expr>(getStmt())) { - R = E->getSourceRange(); - assert(R.isValid()); - beg = &R; - end = beg+1; - } - else - beg = end = 0; -} - -SourceLocation BugReport::getLocation() const { - if (EndNode) - if (const Stmt* S = GetCurrentOrPreviousStmt(EndNode)) { - // For member expressions, return the location of the '.' or '->'. - if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) - return ME->getMemberLoc(); - // For binary operators, return the location of the operator. - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) - return B->getOperatorLoc(); - - return S->getLocStart(); - } - - return FullSourceLoc(); -} - -PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext &BRC) { - return NULL; -} - -//===----------------------------------------------------------------------===// -// Methods for BugReporter and subclasses. -//===----------------------------------------------------------------------===// - -BugReportEquivClass::~BugReportEquivClass() { - for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; -} - -GRBugReporter::~GRBugReporter() { } -BugReporterData::~BugReporterData() {} - -ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } - -GRStateManager& -GRBugReporter::getStateManager() { return Eng.getStateManager(); } - -BugReporter::~BugReporter() { FlushReports(); } - -void BugReporter::FlushReports() { - if (BugTypes.isEmpty()) - return; - - // First flush the warnings for each BugType. This may end up creating new - // warnings and new BugTypes. Because ImmutableSet is a functional data - // structure, we do not need to worry about the iterators being invalidated. - for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) - const_cast<BugType*>(*I)->FlushReports(*this); - - // Iterate through BugTypes a second time. BugTypes may have been updated - // with new BugType objects and new warnings. - for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) { - BugType *BT = const_cast<BugType*>(*I); - - typedef llvm::FoldingSet<BugReportEquivClass> SetTy; - SetTy& EQClasses = BT->EQClasses; - - for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ - BugReportEquivClass& EQ = *EI; - FlushReport(EQ); - } - - // Delete the BugType object. - delete BT; - } - - // Remove all references to the BugType objects. - BugTypes = F.GetEmptySet(); -} - -//===----------------------------------------------------------------------===// -// PathDiagnostics generation. -//===----------------------------------------------------------------------===// - -static std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, - std::pair<ExplodedNode*, unsigned> > -MakeReportGraph(const ExplodedGraph* G, - const ExplodedNode** NStart, - const ExplodedNode** NEnd) { - - // Create the trimmed graph. It will contain the shortest paths from the - // error nodes to the root. In the new graph we should only have one - // error node unless there are two or more error nodes with the same minimum - // path length. - ExplodedGraph* GTrim; - InterExplodedGraphMap* NMap; - - llvm::DenseMap<const void*, const void*> InverseMap; - llvm::tie(GTrim, NMap) = G->Trim(NStart, NEnd, &InverseMap); - - // 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); - - // 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 - // in the new graph. - - std::queue<const ExplodedNode*> WS; - typedef llvm::DenseMap<const ExplodedNode*, unsigned> IndexMapTy; - IndexMapTy IndexMap; - - for (const ExplodedNode** I = NStart; I != NEnd; ++I) - if (const ExplodedNode *N = NMap->getMappedNode(*I)) { - unsigned NodeIndex = (I - NStart) / sizeof(*I); - WS.push(N); - IndexMap[*I] = NodeIndex; - } - - assert(!WS.empty() && "No error node found in the trimmed graph."); - - // Create a new (third!) graph with a single path. This is the graph - // that will be returned to the caller. - ExplodedGraph *GNew = new ExplodedGraph(GTrim->getContext()); - - // Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS - // to the root node, and then construct a new graph that contains only - // a single path. - llvm::DenseMap<const void*,unsigned> Visited; - - unsigned cnt = 0; - const ExplodedNode* Root = 0; - - while (!WS.empty()) { - const ExplodedNode* Node = WS.front(); - WS.pop(); - - if (Visited.find(Node) != Visited.end()) - continue; - - Visited[Node] = cnt++; - - if (Node->pred_empty()) { - Root = Node; - break; - } - - for (ExplodedNode::const_pred_iterator I=Node->pred_begin(), - E=Node->pred_end(); I!=E; ++I) - WS.push(*I); - } - - assert(Root); - - // Now walk from the root down the BFS path, always taking the successor - // with the lowest number. - ExplodedNode *Last = 0, *First = 0; - NodeBackMap *BM = new NodeBackMap(); - unsigned NodeIndex = 0; - - for ( const ExplodedNode *N = Root ;;) { - // Lookup the number associated with the current node. - llvm::DenseMap<const void*,unsigned>::iterator I = Visited.find(N); - assert(I != Visited.end()); - - // Create the equivalent node in the new graph with the same state - // and location. - ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState()); - - // Store the mapping to the original node. - llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N); - assert(IMitr != InverseMap.end() && "No mapping to original node."); - (*BM)[NewN] = (const ExplodedNode*) IMitr->second; - - // Link up the new node with the previous node. - if (Last) - NewN->addPredecessor(Last, *GNew); - - Last = NewN; - - // Are we at the final node? - IndexMapTy::iterator IMI = - IndexMap.find((const ExplodedNode*)(IMitr->second)); - if (IMI != IndexMap.end()) { - First = NewN; - NodeIndex = IMI->second; - break; - } - - // Find the next successor node. We choose the node that is marked - // with the lowest DFS number. - ExplodedNode::const_succ_iterator SI = N->succ_begin(); - ExplodedNode::const_succ_iterator SE = N->succ_end(); - N = 0; - - for (unsigned MinVal = 0; SI != SE; ++SI) { - - I = Visited.find(*SI); - - if (I == Visited.end()) - continue; - - if (!N || I->second < MinVal) { - N = *SI; - MinVal = I->second; - } - } - - assert(N); - } - - assert(First); - - return std::make_pair(std::make_pair(GNew, BM), - std::make_pair(First, NodeIndex)); -} - -/// 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; - - typedef std::vector<PathDiagnosticPiece*> - PiecesTy; - - MacroStackTy MacroStack; - PiecesTy Pieces; - - for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) { - // Get the location of the PathDiagnosticPiece. - const FullSourceLoc Loc = I->getLocation().asLocation(); - - // Determine the instantiation location, which is the location we group - // related PathDiagnosticPieces. - SourceLocation InstantiationLoc = Loc.isMacroID() ? - SM.getInstantiationLoc(Loc) : - SourceLocation(); - - if (Loc.isFileID()) { - MacroStack.clear(); - Pieces.push_back(&*I); - continue; - } - - assert(Loc.isMacroID()); - - // Is the PathDiagnosticPiece within the same macro group? - if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { - MacroStack.back().first->push_back(&*I); - 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; - - SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? - SM.getInstantiationLoc(Loc) : - SourceLocation(); - - // Walk the entire macro stack. - while (!MacroStack.empty()) { - if (InstantiationLoc == MacroStack.back().second) { - MacroGroup = MacroStack.back().first; - break; - } - - if (ParentInstantiationLoc == MacroStack.back().second) { - MacroGroup = MacroStack.back().first; - break; - } - - MacroStack.pop_back(); - } - - if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { - // Create a new macro group and add it to the stack. - PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); - - if (MacroGroup) - MacroGroup->push_back(NewGroup); - else { - assert(InstantiationLoc.isFileID()); - Pieces.push_back(NewGroup); - } - - MacroGroup = NewGroup; - MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc)); - } - - // Finally, add the PathDiagnosticPiece to the group. - MacroGroup->push_back(&*I); - } - - // Now take the pieces and construct a new PathDiagnostic. - PD.resetPath(false); - - 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); - } -} - -void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - BugReportEquivClass& EQ) { - - std::vector<const ExplodedNode*> Nodes; - - for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { - const ExplodedNode* N = I->getEndNode(); - if (N) Nodes.push_back(N); - } - - if (Nodes.empty()) - return; - - // Construct a new graph that contains only a single path from the error - // node to a root. - const std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, - std::pair<ExplodedNode*, unsigned> >& - GPair = MakeReportGraph(&getGraph(), &Nodes[0], &Nodes[0] + Nodes.size()); - - // Find the BugReport with the original location. - BugReport *R = 0; - unsigned i = 0; - for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I, ++i) - if (i == GPair.second.second) { R = *I; break; } - - assert(R && "No original report found for sliced graph."); - - llvm::OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); - llvm::OwningPtr<NodeBackMap> BackMap(GPair.first.second); - const ExplodedNode *N = GPair.second.first; - - // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient()); - - if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N)) - PD.push_back(Piece); - else - return; - - R->registerInitialVisitors(PDB, N); - - switch (PDB.getGenerationScheme()) { - case PathDiagnosticClient::Extensive: - GenerateExtensivePathDiagnostic(PD, PDB, N); - break; - case PathDiagnosticClient::Minimal: - GenerateMinimalPathDiagnostic(PD, PDB, N); - break; - } -} - -void BugReporter::Register(BugType *BT) { - BugTypes = F.Add(BugTypes, BT); -} - -void BugReporter::EmitReport(BugReport* R) { - // Compute the bug report's hash to determine its equivalence class. - llvm::FoldingSetNodeID ID; - R->Profile(ID); - - // Lookup the equivance class. If there isn't one, create it. - BugType& BT = R->getBugType(); - Register(&BT); - void *InsertPos; - BugReportEquivClass* EQ = BT.EQClasses.FindNodeOrInsertPos(ID, InsertPos); - - if (!EQ) { - EQ = new BugReportEquivClass(R); - BT.EQClasses.InsertNode(EQ, InsertPos); - } - else - EQ->AddReport(R); -} - - -//===----------------------------------------------------------------------===// -// Emitting reports in equivalence classes. -//===----------------------------------------------------------------------===// - -namespace { -struct FRIEC_WLItem { - const ExplodedNode *N; - ExplodedNode::const_succ_iterator I, E; - - FRIEC_WLItem(const ExplodedNode *n) - : N(n), I(N->succ_begin()), E(N->succ_end()) {} -}; -} - -static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) { - BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); - assert(I != E); - BugReport *R = *I; - BugType& BT = R->getBugType(); - - if (!BT.isSuppressOnSink()) - return R; - - // For bug reports that should be suppressed when all paths are post-dominated - // by a sink node, iterate through the reports in the equivalence class - // until we find one that isn't post-dominated (if one exists). We use a - // DFS traversal of the ExplodedGraph to find a non-sink node. We could write - // this as a recursive function, but we don't want to risk blowing out the - // stack for very long paths. - for (; I != E; ++I) { - R = *I; - const ExplodedNode *N = R->getEndNode(); - - if (!N) - continue; - - if (N->isSink()) { - assert(false && - "BugType::isSuppressSink() should not be 'true' for sink end nodes"); - return R; - } - - if (N->succ_empty()) - return R; - - // At this point we know that 'N' is not a sink and it has at least one - // successor. Use a DFS worklist to find a non-sink end-of-path node. - typedef FRIEC_WLItem WLItem; - typedef llvm::SmallVector<WLItem, 10> DFSWorkList; - llvm::DenseMap<const ExplodedNode *, unsigned> Visited; - - DFSWorkList WL; - WL.push_back(N); - Visited[N] = 1; - - while (!WL.empty()) { - WLItem &WI = WL.back(); - assert(!WI.N->succ_empty()); - - for (; WI.I != WI.E; ++WI.I) { - const ExplodedNode *Succ = *WI.I; - // End-of-path node? - if (Succ->succ_empty()) { - // If we found an end-of-path node that is not a sink, then return - // this report. - if (!Succ->isSink()) - return R; - - // Found a sink? Continue on to the next successor. - continue; - } - - // Mark the successor as visited. If it hasn't been explored, - // enqueue it to the DFS worklist. - unsigned &mark = Visited[Succ]; - if (!mark) { - mark = 1; - WL.push_back(Succ); - break; - } - } - - if (&WL.back() == &WI) - WL.pop_back(); - } - } - - // If we reach here, the end nodes for all reports in the equivalence - // class are post-dominated by a sink node. - return NULL; -} - - -//===----------------------------------------------------------------------===// -// DiagnosticCache. This is a hack to cache analyzer diagnostics. It -// uses global state, which eventually should go elsewhere. -//===----------------------------------------------------------------------===// -namespace { -class DiagCacheItem : public llvm::FoldingSetNode { - llvm::FoldingSetNodeID ID; -public: - DiagCacheItem(BugReport *R, PathDiagnostic *PD) { - ID.AddString(R->getBugType().getName()); - ID.AddString(R->getBugType().getCategory()); - ID.AddString(R->getDescription()); - ID.AddInteger(R->getLocation().getRawEncoding()); - PD->Profile(ID); - } - - void Profile(llvm::FoldingSetNodeID &id) { - id = ID; - } - - llvm::FoldingSetNodeID &getID() { return ID; } -}; -} - -static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { - // FIXME: Eventually this diagnostic cache should reside in something - // like AnalysisManager instead of being a static variable. This is - // really unsafe in the long term. - typedef llvm::FoldingSet<DiagCacheItem> DiagnosticCache; - static DiagnosticCache DC; - - void *InsertPos; - DiagCacheItem *Item = new DiagCacheItem(R, PD); - - if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { - delete Item; - return true; - } - - DC.InsertNode(Item, InsertPos); - return false; -} - -void BugReporter::FlushReport(BugReportEquivClass& EQ) { - BugReport *R = FindReportInEquivalenceClass(EQ); - - if (!R) - return; - - PathDiagnosticClient* PD = getPathDiagnosticClient(); - - // FIXME: Make sure we use the 'R' for the path that was actually used. - // Probably doesn't make a difference in practice. - BugType& BT = R->getBugType(); - - llvm::OwningPtr<PathDiagnostic> - D(new PathDiagnostic(R->getBugType().getName(), - !PD || PD->useVerboseDescription() - ? R->getDescription() : R->getShortDescription(), - BT.getCategory())); - - GeneratePathDiagnostic(*D.get(), EQ); - - if (IsCachedDiagnostic(R, D.get())) - return; - - // Get the meta data. - std::pair<const char**, const char**> Meta = R->getExtraDescriptiveText(); - for (const char** s = Meta.first; s != Meta.second; ++s) - D->addMeta(*s); - - // Emit a summary diagnostic to the regular Diagnostics engine. - const SourceRange *Beg = 0, *End = 0; - R->getRanges(Beg, End); - Diagnostic& Diag = getDiagnostic(); - FullSourceLoc L(R->getLocation(), getSourceManager()); - - // Search the description for '%', as that will be interpretted as a - // format character by FormatDiagnostics. - llvm::StringRef desc = R->getShortDescription(); - unsigned ErrorDiag; - { - llvm::SmallString<512> TmpStr; - llvm::raw_svector_ostream Out(TmpStr); - for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) - if (*I == '%') - Out << "%%"; - else - Out << *I; - - Out.flush(); - ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr); - } - - switch (End-Beg) { - default: assert(0 && "Don't handle this many ranges yet!"); - case 0: Diag.Report(L, ErrorDiag); break; - case 1: Diag.Report(L, ErrorDiag) << Beg[0]; break; - case 2: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1]; break; - case 3: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1] << Beg[2]; break; - } - - // Emit a full diagnostic for the path if we have a PathDiagnosticClient. - if (!PD) - return; - - if (D->empty()) { - PathDiagnosticPiece* piece = - new PathDiagnosticEventPiece(L, R->getDescription()); - - for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->push_back(piece); - } - - PD->HandlePathDiagnostic(D.take()); -} - -void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, - SourceLocation Loc, - SourceRange* RBeg, unsigned NumRanges) { - EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); -} - -void BugReporter::EmitBasicReport(llvm::StringRef name, - llvm::StringRef category, - llvm::StringRef str, SourceLocation Loc, - SourceRange* RBeg, unsigned NumRanges) { - - // 'BT' will be owned by BugReporter as soon as we call 'EmitReport'. - BugType *BT = new BugType(name, category); - FullSourceLoc L = getContext().getFullLoc(Loc); - RangedBugReport *R = new DiagBugReport(*BT, str, L); - for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); - EmitReport(R); -} diff --git a/lib/Analysis/BugReporterVisitors.cpp b/lib/Analysis/BugReporterVisitors.cpp deleted file mode 100644 index 87de30a..0000000 --- a/lib/Analysis/BugReporterVisitors.cpp +++ /dev/null @@ -1,349 +0,0 @@ -// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- 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 set of BugReporter "visitors" which can be used to -// enhance the diagnostics reported for a bug. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/Expr.h" -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -using namespace clang; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -const Stmt *clang::bugreporter::GetDerefExpr(const ExplodedNode *N) { - // Pattern match for a few useful cases (do something smarter later): - // a[0], p->f, *p - const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - - if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { - if (U->getOpcode() == UnaryOperator::Deref) - return U->getSubExpr()->IgnoreParenCasts(); - } - else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { - return ME->getBase()->IgnoreParenCasts(); - } - else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { - // Retrieve the base for arrays since BasicStoreManager doesn't know how - // to reason about them. - return AE->getBase(); - } - - return NULL; -} - -const Stmt* -clang::bugreporter::GetReceiverExpr(const ExplodedNode *N){ - const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) - return ME->getReceiver(); - return NULL; -} - -const Stmt* -clang::bugreporter::GetDenomExpr(const ExplodedNode *N) { - const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); - if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S)) - return BE->getRHS(); - return NULL; -} - -const Stmt* -clang::bugreporter::GetCalleeExpr(const ExplodedNode *N) { - // Callee is checked as a PreVisit to the CallExpr. - const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) - return CE->getCallee(); - return NULL; -} - -const Stmt* -clang::bugreporter::GetRetValExpr(const ExplodedNode *N) { - const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) - return RS->getRetValue(); - return NULL; -} - -//===----------------------------------------------------------------------===// -// Definitions for bug reporter visitors. -//===----------------------------------------------------------------------===// - -namespace { -class FindLastStoreBRVisitor : public BugReporterVisitor { - const MemRegion *R; - SVal V; - bool satisfied; - const ExplodedNode *StoreSite; -public: - FindLastStoreBRVisitor(SVal v, const MemRegion *r) - : R(r), V(v), satisfied(false), StoreSite(0) {} - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - - if (satisfied) - return NULL; - - if (!StoreSite) { - const ExplodedNode *Node = N, *Last = NULL; - - for ( ; Node ; Last = 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()) { - Last = Node; - break; - } - } - - if (Node->getState()->getSVal(R) != V) - break; - } - - if (!Node || !Last) { - satisfied = true; - return NULL; - } - - StoreSite = Last; - } - - if (StoreSite != N) - return NULL; - - satisfied = true; - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (const PostStmt *PS = N->getLocationAs<PostStmt>()) { - if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << "Variable '" << VR->getDecl()->getNameAsString() << "' "; - } - else - return NULL; - - if (isa<loc::ConcreteInt>(V)) { - bool b = false; - ASTContext &C = BRC.getASTContext(); - if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - if (TR->getValueType(C)->isObjCObjectPointerType()) { - os << "initialized to nil"; - b = true; - } - } - } - - if (!b) - os << "initialized to a null pointer value"; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); - } - else if (V.isUndef()) { - if (isa<VarRegion>(R)) { - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; - } - } - } - } - - if (os.str().empty()) { - if (isa<loc::ConcreteInt>(V)) { - bool b = false; - ASTContext &C = BRC.getASTContext(); - if (R->isBoundable()) { - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - if (TR->getValueType(C)->isObjCObjectPointerType()) { - os << "nil object reference stored to "; - b = true; - } - } - } - - if (!b) - os << "Null pointer value stored to "; - } - else if (V.isUndef()) { - os << "Uninitialized value stored to "; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() - << " is assigned to "; - } - else - return NULL; - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << '\'' << VR->getDecl()->getNameAsString() << '\''; - } - else - return NULL; - } - - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); - - if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); - } - else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { - S = PS->getStmt(); - } - - if (!S) - return NULL; - - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); - } -}; - - -static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, - SVal V) { - BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); -} - -class TrackConstraintBRVisitor : public BugReporterVisitor { - DefinedSVal Constraint; - const bool Assumption; - bool isSatisfied; -public: - TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) - : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} - - PathDiagnosticPiece* VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext& BRC) { - if (isSatisfied) - return NULL; - - // Check if in the previous state it was feasible for this constraint - // to *not* be true. - if (PrevN->getState()->Assume(Constraint, !Assumption)) { - - isSatisfied = true; - - // As a sanity check, make sure that the negation of the constraint - // was infeasible in the current state. If it is feasible, we somehow - // missed the transition point. - if (N->getState()->Assume(Constraint, !Assumption)) - return NULL; - - // We found the transition point for the constraint. We now need to - // pretty-print the constraint. (work-in-progress) - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (isa<Loc>(Constraint)) { - os << "Assuming pointer value is "; - os << (Assumption ? "non-null" : "null"); - } - - if (os.str().empty()) - return NULL; - - // FIXME: Refactor this into BugReporterContext. - const Stmt *S = 0; - ProgramPoint P = N->getLocation(); - - if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); - } - else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { - S = PS->getStmt(); - } - - if (!S) - return NULL; - - // Construct a new PathDiagnosticPiece. - PathDiagnosticLocation L(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(L, os.str()); - } - - return NULL; - } -}; -} // end anonymous namespace - -static void registerTrackConstraint(BugReporterContext& BRC, - DefinedSVal Constraint, - bool Assumption) { - BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); -} - -void clang::bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, - const void *data, - const ExplodedNode* N) { - - const Stmt *S = static_cast<const Stmt*>(data); - - if (!S) - return; - - GRStateManager &StateMgr = BRC.getStateManager(); - const GRState *state = N->getState(); - - 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()); - - // What did we load? - SVal V = state->getSVal(S); - - if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) - || V.isUndef()) { - registerFindLastStore(BRC, R, V); - } - } - } - - SVal V = state->getSValAsScalarOrLoc(S); - - // Uncomment this to find cases where we aren't properly getting the - // base value that was dereferenced. - // assert(!V.isUnknownOrUndef()); - - // Is it a symbolic value? - if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { - const SubRegion *R = cast<SubRegion>(L->getRegion()); - while (R && !isa<SymbolicRegion>(R)) { - R = dyn_cast<SubRegion>(R->getSuperRegion()); - } - - if (R) { - assert(isa<SymbolicRegion>(R)); - registerTrackConstraint(BRC, loc::MemRegionVal(R), false); - } - } -} diff --git a/lib/Analysis/BuiltinFunctionChecker.cpp b/lib/Analysis/BuiltinFunctionChecker.cpp deleted file mode 100644 index a89ad21..0000000 --- a/lib/Analysis/BuiltinFunctionChecker.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This checker evaluates clang builtin functions. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/Basic/Builtins.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; - -namespace { - -class BuiltinFunctionChecker : public Checker { -public: - static void *getTag() { static int tag = 0; return &tag; } - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); -}; - -} - -void clang::RegisterBuiltinFunctionChecker(GRExprEngine &Eng) { - Eng.registerCheck(new BuiltinFunctionChecker()); -} - -bool BuiltinFunctionChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE){ - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); - - if (!FD) - return false; - - unsigned id = FD->getBuiltinID(); - - if (!id) - return false; - - switch (id) { - case Builtin::BI__builtin_expect: { - // For __builtin_expect, just return the value of the subexpression. - assert (CE->arg_begin() != CE->arg_end()); - SVal X = state->getSVal(*(CE->arg_begin())); - C.GenerateNode(state->BindExpr(CE, X)); - return true; - } - - case Builtin::BI__builtin_alloca: { - // FIXME: Refactor into StoreManager itself? - MemRegionManager& RM = C.getStoreManager().getRegionManager(); - const MemRegion* R = - RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), - C.getPredecessor()->getLocationContext()); - - // Set the extent of the region in bytes. This enables us to use the - // SVal of the argument directly. If we save the extent in bits, we - // cannot represent values like symbol*8. - SVal Extent = state->getSVal(*(CE->arg_begin())); - state = C.getStoreManager().setExtent(state, R, Extent); - C.GenerateNode(state->BindExpr(CE, loc::MemRegionVal(R))); - return true; - } - } - - return false; -} diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp deleted file mode 100644 index 5a15fbf..0000000 --- a/lib/Analysis/CFRefCount.cpp +++ /dev/null @@ -1,3790 +0,0 @@ -// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the methods for CFRefCount, which implements -// a reference count checker for Core Foundation (Mac OS X). -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/SymbolManager.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/ImmutableMap.h" -#include "llvm/ADT/ImmutableList.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/STLExtras.h" -#include <stdarg.h> - -using namespace clang; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -// The "fundamental rule" for naming conventions of methods: -// (url broken into two lines) -// http://developer.apple.com/documentation/Cocoa/Conceptual/ -// MemoryMgmt/Tasks/MemoryManagementRules.html -// -// "You take ownership of an object if you create it using a method whose name -// begins with "alloc" or "new" or contains "copy" (for example, alloc, -// newObject, or mutableCopy), or if you send it a retain message. You are -// responsible for relinquishing ownership of objects you own using release -// or autorelease. Any other time you receive an object, you must -// not release it." -// - -using llvm::StrInStrNoCase; -using llvm::StringRef; - -enum NamingConvention { NoConvention, CreateRule, InitRule }; - -static inline bool isWordEnd(char ch, char prev, char next) { - return ch == '\0' - || (islower(prev) && isupper(ch)) // xxxC - || (isupper(prev) && isupper(ch) && islower(next)) // XXCreate - || !isalpha(ch); -} - -static inline const char* parseWord(const char* s) { - char ch = *s, prev = '\0'; - assert(ch != '\0'); - char next = *(s+1); - while (!isWordEnd(ch, prev, next)) { - prev = ch; - ch = next; - next = *((++s)+1); - } - return s; -} - -static NamingConvention deriveNamingConvention(Selector S) { - IdentifierInfo *II = S.getIdentifierInfoForSlot(0); - - if (!II) - return NoConvention; - - const char *s = II->getNameStart(); - - // A method/function name may contain a prefix. We don't know it is there, - // however, until we encounter the first '_'. - bool InPossiblePrefix = true; - bool AtBeginning = true; - NamingConvention C = NoConvention; - - while (*s != '\0') { - // Skip '_'. - if (*s == '_') { - if (InPossiblePrefix) { - // If we already have a convention, return it. Otherwise, skip - // the prefix as if it wasn't there. - if (C != NoConvention) - break; - - InPossiblePrefix = false; - AtBeginning = true; - assert(C == NoConvention); - } - ++s; - continue; - } - - // Skip numbers, ':', etc. - if (!isalpha(*s)) { - ++s; - continue; - } - - const char *wordEnd = parseWord(s); - assert(wordEnd > s); - unsigned len = wordEnd - s; - - switch (len) { - default: - break; - case 3: - // Methods starting with 'new' follow the create rule. - if (AtBeginning && StringRef(s, len).equals_lower("new")) - C = CreateRule; - break; - case 4: - // Methods starting with 'alloc' or contain 'copy' follow the - // create rule - if (C == NoConvention && StringRef(s, len).equals_lower("copy")) - C = CreateRule; - else // Methods starting with 'init' follow the init rule. - if (AtBeginning && StringRef(s, len).equals_lower("init")) - C = InitRule; - break; - case 5: - if (AtBeginning && StringRef(s, len).equals_lower("alloc")) - C = CreateRule; - break; - } - - // If we aren't in the prefix and have a derived convention then just - // return it now. - if (!InPossiblePrefix && C != NoConvention) - return C; - - AtBeginning = false; - s = wordEnd; - } - - // We will get here if there wasn't more than one word - // after the prefix. - return C; -} - -static bool followsFundamentalRule(Selector S) { - return deriveNamingConvention(S) == CreateRule; -} - -static const ObjCMethodDecl* -ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { - ObjCInterfaceDecl *ID = - const_cast<ObjCInterfaceDecl*>(MD->getClassInterface()); - - return MD->isInstanceMethod() - ? ID->lookupInstanceMethod(MD->getSelector()) - : ID->lookupClassMethod(MD->getSelector()); -} - -namespace { -class GenericNodeBuilder { - GRStmtNodeBuilder *SNB; - Stmt *S; - const void *tag; - GREndPathNodeBuilder *ENB; -public: - GenericNodeBuilder(GRStmtNodeBuilder &snb, Stmt *s, - const void *t) - : SNB(&snb), S(s), tag(t), ENB(0) {} - - GenericNodeBuilder(GREndPathNodeBuilder &enb) - : SNB(0), S(0), tag(0), ENB(&enb) {} - - ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) { - if (SNB) - return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag), - state, Pred); - - assert(ENB); - return ENB->generateNode(state, Pred); - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Type querying functions. -//===----------------------------------------------------------------------===// - -static bool isRefType(QualType RetTy, const char* prefix, - ASTContext* Ctx = 0, const char* name = 0) { - - // Recursively walk the typedef stack, allowing typedefs of reference types. - while (TypedefType* TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) { - llvm::StringRef TDName = TD->getDecl()->getIdentifier()->getName(); - if (TDName.startswith(prefix) && TDName.endswith("Ref")) - return true; - - RetTy = TD->getDecl()->getUnderlyingType(); - } - - if (!Ctx || !name) - return false; - - // Is the type void*? - const PointerType* PT = RetTy->getAs<PointerType>(); - if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy)) - return false; - - // Does the name start with the prefix? - return llvm::StringRef(name).startswith(prefix); -} - -//===----------------------------------------------------------------------===// -// Primitives used for constructing summaries for function/method calls. -//===----------------------------------------------------------------------===// - -/// ArgEffect is used to summarize a function/method call's effect on a -/// particular argument. -enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, - DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, - NewAutoreleasePool, SelfOwn, StopTracking }; - -namespace llvm { -template <> struct FoldingSetTrait<ArgEffect> { -static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) { - ID.AddInteger((unsigned) X); -} -}; -} // end llvm namespace - -/// ArgEffects summarizes the effects of a function/method call on all of -/// its arguments. -typedef llvm::ImmutableMap<unsigned,ArgEffect> ArgEffects; - -namespace { - -/// RetEffect is used to summarize a function/method call's behavior with -/// respect to its return value. -class RetEffect { -public: - enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, - NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, - OwnedWhenTrackedReceiver }; - - enum ObjKind { CF, ObjC, AnyObj }; - -private: - Kind K; - ObjKind O; - unsigned index; - - RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {} - RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {} - -public: - Kind getKind() const { return K; } - - ObjKind getObjKind() const { return O; } - - unsigned getIndex() const { - assert(getKind() == Alias); - return index; - } - - bool isOwned() const { - return K == OwnedSymbol || K == OwnedAllocatedSymbol || - K == OwnedWhenTrackedReceiver; - } - - static RetEffect MakeOwnedWhenTrackedReceiver() { - return RetEffect(OwnedWhenTrackedReceiver, ObjC); - } - - static RetEffect MakeAlias(unsigned Idx) { - return RetEffect(Alias, Idx); - } - static RetEffect MakeReceiverAlias() { - return RetEffect(ReceiverAlias); - } - static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { - return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); - } - static RetEffect MakeNotOwned(ObjKind o) { - return RetEffect(NotOwnedSymbol, o); - } - static RetEffect MakeGCNotOwned() { - return RetEffect(GCNotOwnedSymbol, ObjC); - } - - static RetEffect MakeNoRet() { - return RetEffect(NoRet); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned)K); - ID.AddInteger((unsigned)O); - ID.AddInteger(index); - } -}; - -//===----------------------------------------------------------------------===// -// Reference-counting logic (typestate + counts). -//===----------------------------------------------------------------------===// - -class RefVal { -public: - enum Kind { - Owned = 0, // Owning reference. - NotOwned, // Reference is not owned by still valid (not freed). - Released, // Object has been released. - ReturnedOwned, // Returned object passes ownership to caller. - ReturnedNotOwned, // Return object does not pass ownership to caller. - ERROR_START, - ErrorDeallocNotOwned, // -dealloc called on non-owned object. - ErrorDeallocGC, // Calling -dealloc with GC enabled. - ErrorUseAfterRelease, // Object used after released. - ErrorReleaseNotOwned, // Release of an object that was not owned. - ERROR_LEAK_START, - ErrorLeak, // A memory leak due to excessive reference counts. - ErrorLeakReturned, // A memory leak due to the returning method not having - // the correct naming conventions. - ErrorGCLeakReturned, - ErrorOverAutorelease, - ErrorReturnedNotOwned - }; - -private: - Kind kind; - RetEffect::ObjKind okind; - unsigned Cnt; - unsigned ACnt; - QualType T; - - RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) - : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} - - RefVal(Kind k, unsigned cnt = 0) - : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {} - -public: - Kind getKind() const { return kind; } - - RetEffect::ObjKind getObjKind() const { return okind; } - - unsigned getCount() const { return Cnt; } - unsigned getAutoreleaseCount() const { return ACnt; } - unsigned getCombinedCounts() const { return Cnt + ACnt; } - void clearCounts() { Cnt = 0; ACnt = 0; } - void setCount(unsigned i) { Cnt = i; } - void setAutoreleaseCount(unsigned i) { ACnt = i; } - - QualType getType() const { return T; } - - // Useful predicates. - - static bool isError(Kind k) { return k >= ERROR_START; } - - static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; } - - bool isOwned() const { - return getKind() == Owned; - } - - bool isNotOwned() const { - return getKind() == NotOwned; - } - - bool isReturnedOwned() const { - return getKind() == ReturnedOwned; - } - - bool isReturnedNotOwned() const { - return getKind() == ReturnedNotOwned; - } - - bool isNonLeakError() const { - Kind k = getKind(); - return isError(k) && !isLeak(k); - } - - static RefVal makeOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 1) { - return RefVal(Owned, o, Count, 0, t); - } - - static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, - unsigned Count = 0) { - return RefVal(NotOwned, o, Count, 0, t); - } - - // Comparison, profiling, and pretty-printing. - - bool operator==(const RefVal& X) const { - return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; - } - - RefVal operator-(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() - i, - getAutoreleaseCount(), getType()); - } - - RefVal operator+(size_t i) const { - return RefVal(getKind(), getObjKind(), getCount() + i, - getAutoreleaseCount(), getType()); - } - - RefVal operator^(Kind k) const { - return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), - getType()); - } - - RefVal autorelease() const { - return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, - getType()); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned) kind); - ID.AddInteger(Cnt); - ID.AddInteger(ACnt); - ID.Add(T); - } - - void print(llvm::raw_ostream& Out) const; -}; - -void RefVal::print(llvm::raw_ostream& Out) const { - if (!T.isNull()) - Out << "Tracked Type:" << T.getAsString() << '\n'; - - switch (getKind()) { - default: assert(false); - case Owned: { - Out << "Owned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case NotOwned: { - Out << "NotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedOwned: { - Out << "ReturnedOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case ReturnedNotOwned: { - Out << "ReturnedNotOwned"; - unsigned cnt = getCount(); - if (cnt) Out << " (+ " << cnt << ")"; - break; - } - - case Released: - Out << "Released"; - break; - - case ErrorDeallocGC: - Out << "-dealloc (GC)"; - break; - - case ErrorDeallocNotOwned: - Out << "-dealloc (not-owned)"; - break; - - case ErrorLeak: - Out << "Leaked"; - break; - - case ErrorLeakReturned: - Out << "Leaked (Bad naming)"; - break; - - case ErrorGCLeakReturned: - Out << "Leaked (GC-ed at return)"; - break; - - case ErrorUseAfterRelease: - Out << "Use-After-Release [ERROR]"; - break; - - case ErrorReleaseNotOwned: - Out << "Release of Not-Owned [ERROR]"; - break; - - case RefVal::ErrorOverAutorelease: - Out << "Over autoreleased"; - break; - - case RefVal::ErrorReturnedNotOwned: - Out << "Non-owned object returned instead of owned"; - break; - } - - if (ACnt) { - Out << " [ARC +" << ACnt << ']'; - } -} -} //end anonymous namespace - -//===----------------------------------------------------------------------===// -// RefBindings - State used to track object reference counts. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings; - -namespace clang { - template<> - struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> { - static void* GDMIndex() { - static int RefBIndex = 0; - return &RefBIndex; - } - }; -} - -//===----------------------------------------------------------------------===// -// Summaries -//===----------------------------------------------------------------------===// - -namespace { -class RetainSummary { - /// Args - an ordered vector of (index, ArgEffect) pairs, where index - /// specifies the argument (starting from 0). This can be sparsely - /// populated; arguments with no entry in Args use 'DefaultArgEffect'. - ArgEffects Args; - - /// DefaultArgEffect - The default ArgEffect to apply to arguments that - /// do not have an entry in Args. - ArgEffect DefaultArgEffect; - - /// Receiver - If this summary applies to an Objective-C message expression, - /// this is the effect applied to the state of the receiver. - ArgEffect Receiver; - - /// Ret - The effect on the return value. Used to indicate if the - /// function/method call returns a new tracked symbol, returns an - /// alias of one of the arguments in the call, and so on. - RetEffect Ret; - - /// EndPath - Indicates that execution of this method/function should - /// terminate the simulation of a path. - bool EndPath; - -public: - RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, - ArgEffect ReceiverEff, bool endpath = false) - : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R), - EndPath(endpath) {} - - /// getArg - Return the argument effect on the argument specified by - /// idx (starting from 0). - ArgEffect getArg(unsigned idx) const { - if (const ArgEffect *AE = Args.lookup(idx)) - return *AE; - - return DefaultArgEffect; - } - - /// setDefaultArgEffect - Set the default argument effect. - void setDefaultArgEffect(ArgEffect E) { - DefaultArgEffect = E; - } - - /// setArg - Set the argument effect on the argument specified by idx. - void setArgEffect(ArgEffects::Factory& AF, unsigned idx, ArgEffect E) { - Args = AF.Add(Args, idx, E); - } - - /// getRetEffect - Returns the effect on the return value of the call. - RetEffect getRetEffect() const { return Ret; } - - /// setRetEffect - Set the effect of the return value of the call. - void setRetEffect(RetEffect E) { Ret = E; } - - /// isEndPath - Returns true if executing the given method/function should - /// terminate the path. - bool isEndPath() const { return EndPath; } - - /// getReceiverEffect - Returns the effect on the receiver of the call. - /// This is only meaningful if the summary applies to an ObjCMessageExpr*. - ArgEffect getReceiverEffect() const { return Receiver; } - - /// setReceiverEffect - Set the effect on the receiver of the call. - void setReceiverEffect(ArgEffect E) { Receiver = E; } - - typedef ArgEffects::iterator ExprIterator; - - ExprIterator begin_args() const { return Args.begin(); } - ExprIterator end_args() const { return Args.end(); } - - static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects A, - RetEffect RetEff, ArgEffect DefaultEff, - ArgEffect ReceiverEff, bool EndPath) { - ID.Add(A); - ID.Add(RetEff); - ID.AddInteger((unsigned) DefaultEff); - ID.AddInteger((unsigned) ReceiverEff); - ID.AddInteger((unsigned) EndPath); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath); - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Data structures for constructing summaries. -//===----------------------------------------------------------------------===// - -namespace { -class ObjCSummaryKey { - IdentifierInfo* II; - Selector S; -public: - ObjCSummaryKey(IdentifierInfo* ii, Selector s) - : II(ii), S(s) {} - - ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s) - : II(d ? d->getIdentifier() : 0), S(s) {} - - ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s) - : II(d ? d->getIdentifier() : ii), S(s) {} - - ObjCSummaryKey(Selector s) - : II(0), S(s) {} - - IdentifierInfo* getIdentifier() const { return II; } - Selector getSelector() const { return S; } -}; -} - -namespace llvm { -template <> struct DenseMapInfo<ObjCSummaryKey> { - static inline ObjCSummaryKey getEmptyKey() { - return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(), - DenseMapInfo<Selector>::getEmptyKey()); - } - - static inline ObjCSummaryKey getTombstoneKey() { - return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(), - DenseMapInfo<Selector>::getTombstoneKey()); - } - - static unsigned getHashValue(const ObjCSummaryKey &V) { - return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier()) - & 0x88888888) - | (DenseMapInfo<Selector>::getHashValue(V.getSelector()) - & 0x55555555); - } - - static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { - return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(), - RHS.getIdentifier()) && - DenseMapInfo<Selector>::isEqual(LHS.getSelector(), - RHS.getSelector()); - } - -}; -template <> -struct isPodLike<ObjCSummaryKey> { static const bool value = true; }; -} // end llvm namespace - -namespace { -class ObjCSummaryCache { - typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy; - MapTy M; -public: - ObjCSummaryCache() {} - - RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName, - Selector S) { - // Lookup the method using the decl for the class @interface. If we - // have no decl, lookup using the class name. - return D ? find(D, S) : find(ClsName, S); - } - - RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) { - // Do a lookup with the (D,S) pair. If we find a match return - // the iterator. - ObjCSummaryKey K(D, S); - MapTy::iterator I = M.find(K); - - if (I != M.end() || !D) - return I->second; - - // Walk the super chain. If we find a hit with a parent, we'll end - // up returning that summary. We actually allow that key (null,S), as - // we cache summaries for the null ObjCInterfaceDecl* to allow us to - // generate initial summaries without having to worry about NSObject - // being declared. - // FIXME: We may change this at some point. - for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) { - if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) - break; - - if (!C) - return NULL; - } - - // Cache the summary with original key to make the next lookup faster - // and return the iterator. - RetainSummary *Summ = I->second; - M[K] = Summ; - return Summ; - } - - - RetainSummary* find(Expr* Receiver, Selector S) { - return find(getReceiverDecl(Receiver), S); - } - - RetainSummary* find(IdentifierInfo* II, Selector S) { - // FIXME: Class method lookup. Right now we dont' have a good way - // of going between IdentifierInfo* and the class hierarchy. - MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); - - if (I == M.end()) - I = M.find(ObjCSummaryKey(S)); - - return I == M.end() ? NULL : I->second; - } - - const ObjCInterfaceDecl* getReceiverDecl(Expr* E) { - if (const ObjCObjectPointerType* PT = - E->getType()->getAs<ObjCObjectPointerType>()) - return PT->getInterfaceDecl(); - - return NULL; - } - - RetainSummary*& operator[](ObjCMessageExpr* ME) { - - Selector S = ME->getSelector(); - - if (Expr* Receiver = ME->getReceiver()) { - const ObjCInterfaceDecl* OD = getReceiverDecl(Receiver); - return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S]; - } - - return M[ObjCSummaryKey(ME->getClassName(), S)]; - } - - RetainSummary*& operator[](ObjCSummaryKey K) { - return M[K]; - } - - RetainSummary*& operator[](Selector S) { - return M[ ObjCSummaryKey(S) ]; - } -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Data structures for managing collections of summaries. -//===----------------------------------------------------------------------===// - -namespace { -class RetainSummaryManager { - - //==-----------------------------------------------------------------==// - // Typedefs. - //==-----------------------------------------------------------------==// - - typedef llvm::DenseMap<FunctionDecl*, RetainSummary*> - FuncSummariesTy; - - typedef ObjCSummaryCache ObjCMethodSummariesTy; - - //==-----------------------------------------------------------------==// - // Data. - //==-----------------------------------------------------------------==// - - /// Ctx - The ASTContext object for the analyzed ASTs. - ASTContext& Ctx; - - /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier - /// "CFDictionaryCreate". - IdentifierInfo* CFDictionaryCreateII; - - /// GCEnabled - Records whether or not the analyzed code runs in GC mode. - const bool GCEnabled; - - /// FuncSummaries - A map from FunctionDecls to summaries. - FuncSummariesTy FuncSummaries; - - /// ObjCClassMethodSummaries - A map from selectors (for instance methods) - /// to summaries. - ObjCMethodSummariesTy ObjCClassMethodSummaries; - - /// ObjCMethodSummaries - A map from selectors to summaries. - ObjCMethodSummariesTy ObjCMethodSummaries; - - /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, - /// and all other data used by the checker. - llvm::BumpPtrAllocator BPAlloc; - - /// AF - A factory for ArgEffects objects. - ArgEffects::Factory AF; - - /// ScratchArgs - A holding buffer for construct ArgEffects. - ArgEffects ScratchArgs; - - /// ObjCAllocRetE - Default return effect for methods returning Objective-C - /// objects. - RetEffect ObjCAllocRetE; - - /// ObjCInitRetE - Default return effect for init methods returning - /// Objective-C objects. - RetEffect ObjCInitRetE; - - RetainSummary DefaultSummary; - RetainSummary* StopSummary; - - //==-----------------------------------------------------------------==// - // Methods. - //==-----------------------------------------------------------------==// - - /// getArgEffects - Returns a persistent ArgEffects object based on the - /// data in ScratchArgs. - ArgEffects getArgEffects(); - - enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; - -public: - RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } - - RetainSummary *getDefaultSummary() { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - return new (Summ) RetainSummary(DefaultSummary); - } - - RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); - - RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD); - RetainSummary* getCFSummaryGetRule(FunctionDecl* FD); - RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, const char* FName); - - RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape, - bool isEndPath = false); - - RetainSummary* getPersistentSummary(RetEffect RE, - ArgEffect ReceiverEff = DoNothing, - ArgEffect DefaultEff = MayEscape) { - return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); - } - - RetainSummary *getPersistentStopSummary() { - if (StopSummary) - return StopSummary; - - StopSummary = getPersistentSummary(RetEffect::MakeNoRet(), - StopTracking, StopTracking); - - return StopSummary; - } - - RetainSummary *getInitMethodSummary(QualType RetTy); - - void InitializeClassMethodSummaries(); - void InitializeMethodSummaries(); - - bool isTrackedObjCObjectType(QualType T); - bool isTrackedCFObjectType(QualType T); - -private: - - void addClsMethSummary(IdentifierInfo* ClsII, Selector S, - RetainSummary* Summ) { - ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { - ObjCClassMethodSummaries[S] = Summ; - } - - void addNSObjectMethSummary(Selector S, RetainSummary *Summ) { - ObjCMethodSummaries[S] = Summ; - } - - void addClassMethSummary(const char* Cls, const char* nullaryName, - RetainSummary *Summ) { - IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); - Selector S = GetNullarySelector(nullaryName, Ctx); - ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addInstMethSummary(const char* Cls, const char* nullaryName, - RetainSummary *Summ) { - IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); - Selector S = GetNullarySelector(nullaryName, Ctx); - ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - Selector generateSelector(va_list argp) { - llvm::SmallVector<IdentifierInfo*, 10> II; - - while (const char* s = va_arg(argp, const char*)) - II.push_back(&Ctx.Idents.get(s)); - - return Ctx.Selectors.getSelector(II.size(), &II[0]); - } - - void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, - RetainSummary* Summ, va_list argp) { - Selector S = generateSelector(argp); - Summaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - - void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); - va_end(argp); - } - - void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); - va_end(argp); - } - - void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) { - va_list argp; - va_start(argp, Summ); - addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp); - va_end(argp); - } - - void addPanicSummary(const char* Cls, ...) { - RetainSummary* Summ = getPersistentSummary(AF.GetEmptyMap(), - RetEffect::MakeNoRet(), - DoNothing, DoNothing, true); - va_list argp; - va_start (argp, Cls); - addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); - va_end(argp); - } - -public: - - RetainSummaryManager(ASTContext& ctx, bool gcenabled) - : Ctx(ctx), - CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), - GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.GetEmptyMap()), - ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwned(RetEffect::ObjC, true)), - ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwnedWhenTrackedReceiver()), - DefaultSummary(AF.GetEmptyMap() /* per-argument effects (none) */, - RetEffect::MakeNoRet() /* return effect */, - MayEscape, /* default argument effect */ - DoNothing /* receiver effect */), - StopSummary(0) { - - InitializeClassMethodSummaries(); - InitializeMethodSummaries(); - } - - ~RetainSummaryManager(); - - RetainSummary* getSummary(FunctionDecl* FD); - - RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, - const GRState *state, - const LocationContext *LC); - - RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME, - const ObjCInterfaceDecl* ID) { - return getInstanceMethodSummary(ME->getSelector(), ME->getClassName(), - ID, ME->getMethodDecl(), ME->getType()); - } - - RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, - const ObjCMethodDecl *MD, - QualType RetTy); - - RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy); - - RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) { - return getClassMethodSummary(ME->getSelector(), ME->getClassName(), - ME->getClassInfo().first, - ME->getMethodDecl(), ME->getType()); - } - - /// getMethodSummary - This version of getMethodSummary is used to query - /// the summary for the current method being analyzed. - RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { - // FIXME: Eventually this should be unneeded. - const ObjCInterfaceDecl *ID = MD->getClassInterface(); - Selector S = MD->getSelector(); - IdentifierInfo *ClsName = ID->getIdentifier(); - QualType ResultTy = MD->getResultType(); - - // Resolve the method decl last. - if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD)) - MD = InterfaceMD; - - if (MD->isInstanceMethod()) - return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy); - else - return getClassMethodSummary(S, ClsName, ID, MD, ResultTy); - } - - RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD, - Selector S, QualType RetTy); - - void updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD); - - void updateSummaryFromAnnotations(RetainSummary &Summ, - const FunctionDecl *FD); - - bool isGCEnabled() const { return GCEnabled; } - - RetainSummary *copySummary(RetainSummary *OldSumm) { - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(*OldSumm); - return Summ; - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Implementation of checker data structures. -//===----------------------------------------------------------------------===// - -RetainSummaryManager::~RetainSummaryManager() {} - -ArgEffects RetainSummaryManager::getArgEffects() { - ArgEffects AE = ScratchArgs; - ScratchArgs = AF.GetEmptyMap(); - return AE; -} - -RetainSummary* -RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, - ArgEffect ReceiverEff, - ArgEffect DefaultEff, - bool isEndPath) { - // Create the summary and return it. - RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath); - return Summ; -} - -//===----------------------------------------------------------------------===// -// Predicates. -//===----------------------------------------------------------------------===// - -bool RetainSummaryManager::isTrackedObjCObjectType(QualType Ty) { - if (!Ty->isObjCObjectPointerType()) - return false; - - const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>(); - - // Can be true for objects with the 'NSObject' attribute. - if (!PT) - return true; - - // We assume that id<..>, id, and "Class" all represent tracked objects. - if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || - PT->isObjCClassType()) - return true; - - // Does the interface subclass NSObject? - // FIXME: We can memoize here if this gets too expensive. - const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); - - // Assume that anything declared with a forward declaration and no - // @interface subclasses NSObject. - if (ID->isForwardDecl()) - return true; - - IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - - for ( ; ID ; ID = ID->getSuperClass()) - if (ID->getIdentifier() == NSObjectII) - return true; - - return false; -} - -bool RetainSummaryManager::isTrackedCFObjectType(QualType T) { - return isRefType(T, "CF") || // Core Foundation. - isRefType(T, "CG") || // Core Graphics. - isRefType(T, "DADisk") || // Disk Arbitration API. - isRefType(T, "DADissenter") || - isRefType(T, "DASessionRef"); -} - -//===----------------------------------------------------------------------===// -// Summary creation for functions (largely uses of Core Foundation). -//===----------------------------------------------------------------------===// - -static bool isRetain(FunctionDecl* FD, const char* FName) { - const char* loc = strstr(FName, "Retain"); - return loc && loc[sizeof("Retain")-1] == '\0'; -} - -static bool isRelease(FunctionDecl* FD, const char* FName) { - const char* loc = strstr(FName, "Release"); - return loc && loc[sizeof("Release")-1] == '\0'; -} - -RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { - // Look up a summary in our cache of FunctionDecls -> Summaries. - FuncSummariesTy::iterator I = FuncSummaries.find(FD); - if (I != FuncSummaries.end()) - return I->second; - - // No summary? Generate one. - RetainSummary *S = 0; - - do { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) { - S = getPersistentStopSummary(); - break; - } - - // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the - // function's type. - const FunctionType* FT = FD->getType()->getAs<FunctionType>(); - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - break; - - const char* FName = II->getNameStart(); - - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - while (*FName == '_') ++FName; - - // Inspect the result type. - QualType RetTy = FT->getResultType(); - - // FIXME: This should all be refactored into a chain of "summary lookup" - // filters. - assert(ScratchArgs.isEmpty()); - - switch (strlen(FName)) { - default: break; - case 14: - if (!memcmp(FName, "pthread_create", 14)) { - // Part of: <rdar://problem/7299394>. This will be addressed - // better with IPA. - S = getPersistentStopSummary(); - } - break; - - case 17: - // Handle: id NSMakeCollectable(CFTypeRef) - if (!memcmp(FName, "NSMakeCollectable", 17)) { - S = (RetTy->isObjCIdType()) - ? getUnarySummary(FT, cfmakecollectable) - : getPersistentStopSummary(); - } - else if (!memcmp(FName, "IOBSDNameMatching", 17) || - !memcmp(FName, "IOServiceMatching", 17)) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 21: - if (!memcmp(FName, "IOServiceNameMatching", 21)) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 24: - if (!memcmp(FName, "IOServiceAddNotification", 24)) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - ScratchArgs = AF.Add(ScratchArgs, 2, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing,DoNothing); - } - break; - - case 25: - if (!memcmp(FName, "IORegistryEntryIDMatching", 25)) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 26: - if (!memcmp(FName, "IOOpenFirmwarePathMatching", 26)) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing, DoNothing); - } - break; - - case 27: - if (!memcmp(FName, "IOServiceGetMatchingService", 27)) { - // Part of <rdar://problem/6961230>. - // This should be addressed using a API table. - ScratchArgs = AF.Add(ScratchArgs, 1, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } - break; - - case 28: - if (!memcmp(FName, "IOServiceGetMatchingServices", 28)) { - // FIXES: <rdar://problem/6326900> - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - ScratchArgs = AF.Add(ScratchArgs, 1, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing); - } - else if (!memcmp(FName, "CVPixelBufferCreateWithBytes", 28)) { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithBytes is released via - // a callback and doing full IPA to make sure this is done correctly. - // FIXME: This function has an out parameter that returns an - // allocated object. - ScratchArgs = AF.Add(ScratchArgs, 7, StopTracking); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing); - } - break; - - case 29: - if (!memcmp(FName, "CGBitmapContextCreateWithData", 29)) { - // FIXES: <rdar://problem/7358899> - // Eventually this can be improved by recognizing that 'releaseInfo' - // passed to CGBitmapContextCreateWithData is released via - // a callback and doing full IPA to make sure this is done correctly. - ScratchArgs = AF.Add(ScratchArgs, 8, StopTracking); - S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), - DoNothing,DoNothing); - } - break; - - case 32: - if (!memcmp(FName, "IOServiceAddMatchingNotification", 32)) { - // Part of <rdar://problem/6961230>. - // This should be addressed using a API table. - ScratchArgs = AF.Add(ScratchArgs, 2, DecRef); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); - } - break; - - case 34: - if (!memcmp(FName, "CVPixelBufferCreateWithPlanarBytes", 34)) { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithPlanarBytes is released - // via a callback and doing full IPA to make sure this is done - // correctly. - ScratchArgs = AF.Add(ScratchArgs, 12, StopTracking); - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing); - } - break; - } - - // Did we get a summary? - if (S) - break; - - // Enable this code once the semantics of NSDeallocateObject are resolved - // for GC. <rdar://problem/6619988> -#if 0 - // Handle: NSDeallocateObject(id anObject); - // This method does allow 'nil' (although we don't check it now). - if (strcmp(FName, "NSDeallocateObject") == 0) { - return RetTy == Ctx.VoidTy - ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) - : getPersistentStopSummary(); - } -#endif - - if (RetTy->isPointerType()) { - // For CoreFoundation ('CF') types. - if (isRefType(RetTy, "CF", &Ctx, FName)) { - if (isRetain(FD, FName)) - S = getUnarySummary(FT, cfretain); - else if (strstr(FName, "MakeCollectable")) - S = getUnarySummary(FT, cfmakecollectable); - else - S = getCFCreateGetRuleSummary(FD, FName); - - break; - } - - // For CoreGraphics ('CG') types. - if (isRefType(RetTy, "CG", &Ctx, FName)) { - if (isRetain(FD, FName)) - S = getUnarySummary(FT, cfretain); - else - S = getCFCreateGetRuleSummary(FD, FName); - - break; - } - - // For the Disk Arbitration API (DiskArbitration/DADisk.h) - if (isRefType(RetTy, "DADisk") || - isRefType(RetTy, "DADissenter") || - isRefType(RetTy, "DASessionRef")) { - S = getCFCreateGetRuleSummary(FD, FName); - break; - } - - break; - } - - // Check for release functions, the only kind of functions that we care - // about that don't return a pointer type. - if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { - // Test for 'CGCF'. - if (FName[1] == 'G' && FName[2] == 'C' && FName[3] == 'F') - FName += 4; - else - FName += 2; - - if (isRelease(FD, FName)) - S = getUnarySummary(FT, cfrelease); - else { - assert (ScratchArgs.isEmpty()); - // Remaining CoreFoundation and CoreGraphics functions. - // We use to assume that they all strictly followed the ownership idiom - // and that ownership cannot be transferred. While this is technically - // correct, many methods allow a tracked object to escape. For example: - // - // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); - // CFDictionaryAddValue(y, key, x); - // CFRelease(x); - // ... it is okay to use 'x' since 'y' has a reference to it - // - // We handle this and similar cases with the follow heuristic. If the - // function name contains "InsertValue", "SetValue", "AddValue", - // "AppendValue", or "SetAttribute", then we assume that arguments may - // "escape." This means that something else holds on to the object, - // allowing it be used even after its local retain count drops to 0. - ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos|| - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos|| - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) - ? MayEscape : DoNothing; - - S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E); - } - } - } - while (0); - - if (!S) - S = getDefaultSummary(); - - // Annotations override defaults. - assert(S); - updateSummaryFromAnnotations(*S, FD); - - FuncSummaries[FD] = S; - return S; -} - -RetainSummary* -RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD, - const char* FName) { - - if (strstr(FName, "Create") || strstr(FName, "Copy")) - return getCFSummaryCreateRule(FD); - - if (strstr(FName, "Get")) - return getCFSummaryGetRule(FD); - - return getDefaultSummary(); -} - -RetainSummary* -RetainSummaryManager::getUnarySummary(const FunctionType* FT, - UnaryFuncKind func) { - - // Sanity check that this is *really* a unary function. This can - // happen if people do weird things. - const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT); - if (!FTP || FTP->getNumArgs() != 1) - return getPersistentStopSummary(); - - assert (ScratchArgs.isEmpty()); - - switch (func) { - case cfretain: { - ScratchArgs = AF.Add(ScratchArgs, 0, IncRef); - return getPersistentSummary(RetEffect::MakeAlias(0), - DoNothing, DoNothing); - } - - case cfrelease: { - ScratchArgs = AF.Add(ScratchArgs, 0, DecRef); - return getPersistentSummary(RetEffect::MakeNoRet(), - DoNothing, DoNothing); - } - - case cfmakecollectable: { - ScratchArgs = AF.Add(ScratchArgs, 0, MakeCollectable); - return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing); - } - - default: - assert (false && "Not a supported unary function."); - return getDefaultSummary(); - } -} - -RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { - assert (ScratchArgs.isEmpty()); - - if (FD->getIdentifier() == CFDictionaryCreateII) { - ScratchArgs = AF.Add(ScratchArgs, 1, DoNothingByRef); - ScratchArgs = AF.Add(ScratchArgs, 2, DoNothingByRef); - } - - return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); -} - -RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) { - assert (ScratchArgs.isEmpty()); - return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), - DoNothing, DoNothing); -} - -//===----------------------------------------------------------------------===// -// Summary creation for Selectors. -//===----------------------------------------------------------------------===// - -RetainSummary* -RetainSummaryManager::getInitMethodSummary(QualType RetTy) { - assert(ScratchArgs.isEmpty()); - // 'init' methods conceptually return a newly allocated object and claim - // the receiver. - if (isTrackedObjCObjectType(RetTy) || isTrackedCFObjectType(RetTy)) - return getPersistentSummary(ObjCInitRetE, DecRefMsg); - - return getDefaultSummary(); -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const FunctionDecl *FD) { - if (!FD) - return; - - QualType RetTy = FD->getResultType(); - - // Determine if there is a special return effect for this method. - if (isTrackedObjCObjectType(RetTy)) { - if (FD->getAttr<NSReturnsRetainedAttr>()) { - Summ.setRetEffect(ObjCAllocRetE); - } - else if (FD->getAttr<CFReturnsRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - } - else if (RetTy->getAs<PointerType>()) { - if (FD->getAttr<CFReturnsRetainedAttr>()) { - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); - } - } -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, - const ObjCMethodDecl *MD) { - if (!MD) - return; - - bool isTrackedLoc = false; - - // Determine if there is a special return effect for this method. - if (isTrackedObjCObjectType(MD->getResultType())) { - if (MD->getAttr<NSReturnsRetainedAttr>()) { - Summ.setRetEffect(ObjCAllocRetE); - return; - } - - isTrackedLoc = true; - } - - if (!isTrackedLoc) - isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL; - - if (isTrackedLoc && MD->getAttr<CFReturnsRetainedAttr>()) - Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); -} - -RetainSummary* -RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, - Selector S, QualType RetTy) { - - if (MD) { - // Scan the method decl for 'void*' arguments. These should be treated - // as 'StopTracking' because they are often used with delegates. - // Delegates are a frequent form of false positives with the retain - // count checker. - unsigned i = 0; - for (ObjCMethodDecl::param_iterator I = MD->param_begin(), - E = MD->param_end(); I != E; ++I, ++i) - if (ParmVarDecl *PD = *I) { - QualType Ty = Ctx.getCanonicalType(PD->getType()); - if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) - ScratchArgs = AF.Add(ScratchArgs, i, StopTracking); - } - } - - // Any special effect for the receiver? - ArgEffect ReceiverEff = DoNothing; - - // If one of the arguments in the selector has the keyword 'delegate' we - // should stop tracking the reference count for the receiver. This is - // because the reference count is quite possibly handled by a delegate - // method. - if (S.isKeywordSelector()) { - const std::string &str = S.getAsString(); - assert(!str.empty()); - if (StrInStrNoCase(str, "delegate:") != StringRef::npos) - ReceiverEff = StopTracking; - } - - // Look for methods that return an owned object. - if (isTrackedObjCObjectType(RetTy)) { - // EXPERIMENTAL: Assume the Cocoa conventions for all objects returned - // by instance methods. - RetEffect E = followsFundamentalRule(S) - ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - // Look for methods that return an owned core foundation object. - if (isTrackedCFObjectType(RetTy)) { - RetEffect E = followsFundamentalRule(S) - ? RetEffect::MakeOwned(RetEffect::CF, true) - : RetEffect::MakeNotOwned(RetEffect::CF); - - return getPersistentSummary(E, ReceiverEff, MayEscape); - } - - if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing) - return getDefaultSummary(); - - return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); -} - -RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, - const GRState *state, - const LocationContext *LC) { - - // We need the type-information of the tracked receiver object - // Retrieve it from the state. - const Expr *Receiver = ME->getReceiver(); - const ObjCInterfaceDecl* ID = 0; - - // FIXME: Is this really working as expected? There are cases where - // we just use the 'ID' from the message expression. - SVal receiverV = state->getSValAsScalarOrLoc(Receiver); - - // FIXME: Eventually replace the use of state->get<RefBindings> with - // a generic API for reasoning about the Objective-C types of symbolic - // objects. - if (SymbolRef Sym = receiverV.getAsLocSymbol()) - if (const RefVal *T = state->get<RefBindings>(Sym)) - if (const ObjCObjectPointerType* PT = - T->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - - // FIXME: this is a hack. This may or may not be the actual method - // that is called. - if (!ID) { - if (const ObjCObjectPointerType *PT = - Receiver->getType()->getAs<ObjCObjectPointerType>()) - ID = PT->getInterfaceDecl(); - } - - // FIXME: The receiver could be a reference to a class, meaning that - // we should use the class method. - RetainSummary *Summ = getInstanceMethodSummary(ME, ID); - - // Special-case: are we sending a mesage to "self"? - // This is a hack. When we have full-IP this should be removed. - if (isa<ObjCMethodDecl>(LC->getDecl())) { - if (const loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&receiverV)) { - // Get the region associated with 'self'. - if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) { - SVal SelfVal = state->getSVal(state->getRegion(SelfDecl, LC)); - if (L->StripCasts() == SelfVal.getAsRegion()) { - // Update the summary to make the default argument effect - // 'StopTracking'. - Summ = copySummary(Summ); - Summ->setDefaultArgEffect(StopTracking); - } - } - } - } - - return Summ ? Summ : getDefaultSummary(); -} - -RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(Selector S, - IdentifierInfo *ClsName, - const ObjCInterfaceDecl* ID, - const ObjCMethodDecl *MD, - QualType RetTy) { - - // Look up a summary in our summary cache. - RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); - - if (!Summ) { - assert(ScratchArgs.isEmpty()); - - // "initXXX": pass-through for receiver. - if (deriveNamingConvention(S) == InitRule) - Summ = getInitMethodSummary(RetTy); - else - Summ = getCommonMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); - - // Memoize the summary. - ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -RetainSummary* -RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, - QualType RetTy) { - - assert(ClsName && "Class name must be specified."); - RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); - - if (!Summ) { - Summ = getCommonMethodSummary(MD, S, RetTy); - // Annotations override defaults. - updateSummaryFromAnnotations(*Summ, MD); - // Memoize the summary. - ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; - } - - return Summ; -} - -void RetainSummaryManager::InitializeClassMethodSummaries() { - assert(ScratchArgs.isEmpty()); - RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE); - - // Create the summaries for "alloc", "new", and "allocWithZone:" for - // NSObject and its derivatives. - addNSObjectClsMethSummary(GetNullarySelector("alloc", Ctx), Summ); - addNSObjectClsMethSummary(GetNullarySelector("new", Ctx), Summ); - addNSObjectClsMethSummary(GetUnarySelector("allocWithZone", Ctx), Summ); - - // Create the [NSAssertionHandler currentHander] summary. - addClassMethSummary("NSAssertionHandler", "currentHandler", - getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); - - // Create the [NSAutoreleasePool addObject:] summary. - ScratchArgs = AF.Add(ScratchArgs, 0, Autorelease); - addClassMethSummary("NSAutoreleasePool", "addObject", - getPersistentSummary(RetEffect::MakeNoRet(), - DoNothing, Autorelease)); - - // Create a summary for [NSCursor dragCopyCursor]. - addClassMethSummary("NSCursor", "dragCopyCursor", - getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, - DoNothing)); - - // Create the summaries for [NSObject performSelector...]. We treat - // these as 'stop tracking' for the arguments because they are often - // used for delegates that can release the object. When we have better - // inter-procedural analysis we can potentially do something better. This - // workaround is to remove false positives. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); - IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", - "afterDelay", "inModes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", - "withObject", "waitUntilDone", "modes", NULL); - addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", - "withObject", NULL); - - // Specially handle NSData. - RetainSummary *dataWithBytesNoCopySumm = - getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC), DoNothing, - DoNothing); - addClsMethSummary("NSData", dataWithBytesNoCopySumm, - "dataWithBytesNoCopy", "length", NULL); - addClsMethSummary("NSData", dataWithBytesNoCopySumm, - "dataWithBytesNoCopy", "length", "freeWhenDone", NULL); -} - -void RetainSummaryManager::InitializeMethodSummaries() { - - assert (ScratchArgs.isEmpty()); - - // Create the "init" selector. It just acts as a pass-through for the - // receiver. - RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); - addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); - - // awakeAfterUsingCoder: behaves basically like an 'init' method. It - // claims the receiver and returns a retained object. - addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), - InitSumm); - - // The next methods are allocators. - RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); - RetainSummary *CFAllocSumm = - getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); - - // Create the "copy" selector. - addNSObjectMethSummary(GetNullarySelector("copy", Ctx), AllocSumm); - - // Create the "mutableCopy" selector. - addNSObjectMethSummary(GetNullarySelector("mutableCopy", Ctx), AllocSumm); - - // Create the "retain" selector. - RetEffect E = RetEffect::MakeReceiverAlias(); - RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); - addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); - - // Create the "release" selector. - Summ = getPersistentSummary(E, DecRefMsg); - addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - - // Create the "drain" selector. - Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); - addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); - - // Create the -dealloc summary. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); - addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); - - // Create the "autorelease" selector. - Summ = getPersistentSummary(E, Autorelease); - addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - - // Specially handle NSAutoreleasePool. - addInstMethSummary("NSAutoreleasePool", "init", - getPersistentSummary(RetEffect::MakeReceiverAlias(), - NewAutoreleasePool)); - - // For NSWindow, allocated objects are (initially) self-owned. - // FIXME: For now we opt for false negatives with NSWindow, as these objects - // self-own themselves. However, they only do this once they are displayed. - // Thus, we need to track an NSWindow's display status. - // This is tracked in <rdar://problem/6062711>. - // See also http://llvm.org/bugs/show_bug.cgi?id=3714. - RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), - StopTracking, - StopTracking); - - addClassMethSummary("NSWindow", "alloc", NoTrackYet); - -#if 0 - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - - // For NSPanel (which subclasses NSWindow), allocated objects are not - // self-owned. - // FIXME: For now we don't track NSPanels. object for the same reason - // as for NSWindow objects. - addClassMethSummary("NSPanel", "alloc", NoTrackYet); - -#if 0 - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - - // Don't track allocated autorelease pools yet, as it is okay to prematurely - // exit a method. - addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); - - // Create NSAssertionHandler summaries. - addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file", - "lineNumber", "description", NULL); - - addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object", - "file", "lineNumber", "description", NULL); - - // Create summaries QCRenderer/QCView -createSnapShotImageOfType: - addInstMethSummary("QCRenderer", AllocSumm, - "createSnapshotImageOfType", NULL); - addInstMethSummary("QCView", AllocSumm, - "createSnapshotImageOfType", NULL); - - // Create summaries for CIContext, 'createCGImage' and - // 'createCGLayerWithSize'. These objects are CF objects, and are not - // automatically garbage collected. - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", NULL); - addInstMethSummary("CIContext", CFAllocSumm, - "createCGImage", "fromRect", "format", "colorSpace", NULL); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", - "info", NULL); -} - -//===----------------------------------------------------------------------===// -// AutoreleaseBindings - State used to track objects in autorelease pools. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; -typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; -typedef llvm::ImmutableList<SymbolRef> ARStack; - -static int AutoRCIndex = 0; -static int AutoRBIndex = 0; - -namespace { class AutoreleasePoolContents {}; } -namespace { class AutoreleaseStack {}; } - -namespace clang { -template<> struct GRStateTrait<AutoreleaseStack> - : public GRStatePartialTrait<ARStack> { - static inline void* GDMIndex() { return &AutoRBIndex; } -}; - -template<> struct GRStateTrait<AutoreleasePoolContents> - : public GRStatePartialTrait<ARPoolContents> { - static inline void* GDMIndex() { return &AutoRCIndex; } -}; -} // end clang namespace - -static SymbolRef GetCurrentAutoreleasePool(const GRState* state) { - ARStack stack = state->get<AutoreleaseStack>(); - return stack.isEmpty() ? SymbolRef() : stack.getHead(); -} - -static const GRState * SendAutorelease(const GRState *state, - ARCounts::Factory &F, SymbolRef sym) { - - SymbolRef pool = GetCurrentAutoreleasePool(state); - const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool); - ARCounts newCnts(0); - - if (cnts) { - const unsigned *cnt = (*cnts).lookup(sym); - newCnts = F.Add(*cnts, sym, cnt ? *cnt + 1 : 1); - } - else - newCnts = F.Add(F.GetEmptyMap(), sym, 1); - - return state->set<AutoreleasePoolContents>(pool, newCnts); -} - -//===----------------------------------------------------------------------===// -// Transfer functions. -//===----------------------------------------------------------------------===// - -namespace { - -class CFRefCount : public GRTransferFuncs { -public: - class BindingsPrinter : public GRState::Printer { - public: - virtual void Print(llvm::raw_ostream& Out, const GRState* state, - const char* nl, const char* sep); - }; - -private: - typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*> - SummaryLogTy; - - RetainSummaryManager Summaries; - SummaryLogTy SummaryLog; - const LangOptions& LOpts; - ARCounts::Factory ARCountFactory; - - BugType *useAfterRelease, *releaseNotOwned; - BugType *deallocGC, *deallocNotOwned; - BugType *leakWithinFunction, *leakAtReturn; - BugType *overAutorelease; - BugType *returnNotOwnedForOwned; - BugReporter *BR; - - const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E, - RefVal::Kind& hasErr); - - void ProcessNonLeakError(ExplodedNodeSet& Dst, - GRStmtNodeBuilder& Builder, - Expr* NodeExpr, Expr* ErrorExpr, - ExplodedNode* Pred, - const GRState* St, - RefVal::Kind hasErr, SymbolRef Sym); - - const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, - llvm::SmallVectorImpl<SymbolRef> &Leaked); - - ExplodedNode* ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl<SymbolRef> &Leaked, - GenericNodeBuilder &Builder, - GRExprEngine &Eng, - ExplodedNode *Pred = 0); - -public: - CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) - : Summaries(Ctx, gcenabled), - LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), - deallocGC(0), deallocNotOwned(0), - leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), - returnNotOwnedForOwned(0), BR(0) {} - - virtual ~CFRefCount() {} - - void RegisterChecks(GRExprEngine &Eng); - - virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) { - Printers.push_back(new BindingsPrinter()); - } - - bool isGCEnabled() const { return Summaries.isGCEnabled(); } - const LangOptions& getLangOptions() const { return LOpts; } - - const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { - SummaryLogTy::const_iterator I = SummaryLog.find(N); - return I == SummaryLog.end() ? 0 : I->second; - } - - // Calls. - - void EvalSummary(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - Expr* Ex, - Expr* Receiver, - const RetainSummary& Summ, - const MemRegion *Callee, - ExprIterator arg_beg, ExprIterator arg_end, - ExplodedNode* Pred, const GRState *state); - - virtual void EvalCall(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, - ExplodedNode* Pred); - - - virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred, - const GRState *state); - - bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred); - - // Stores. - virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val); - - // End-of-path. - - virtual void EvalEndPath(GRExprEngine& Engine, - GREndPathNodeBuilder& Builder); - - virtual void EvalDeadSymbols(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ExplodedNode* Pred, - Stmt* S, const GRState* state, - SymbolReaper& SymReaper); - - std::pair<ExplodedNode*, const GRState *> - HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, - ExplodedNode* Pred, GRExprEngine &Eng, - SymbolRef Sym, RefVal V, bool &stop); - // Return statements. - - virtual void EvalReturn(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ReturnStmt* S, - ExplodedNode* Pred); - - // Assumptions. - - virtual const GRState *EvalAssume(const GRState* state, SVal condition, - bool assumption); -}; - -} // end anonymous namespace - -static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym, - const GRState *state) { - Out << ' '; - if (Sym) - Out << Sym->getSymbolID(); - else - Out << "<pool>"; - Out << ":{"; - - // Get the contents of the pool. - if (const ARCounts *cnts = state->get<AutoreleasePoolContents>(Sym)) - for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J) - Out << '(' << J.getKey() << ',' << J.getData() << ')'; - - Out << '}'; -} - -void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out, - const GRState* state, - const char* nl, const char* sep) { - - RefBindings B = state->get<RefBindings>(); - - if (!B.isEmpty()) - Out << sep << nl; - - for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - Out << (*I).first << " : "; - (*I).second.print(Out); - Out << nl; - } - - // Print the autorelease stack. - Out << sep << nl << "AR pool stack:"; - ARStack stack = state->get<AutoreleaseStack>(); - - PrintPool(Out, SymbolRef(), state); // Print the caller's pool. - for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I) - PrintPool(Out, *I, state); - - Out << nl; -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -namespace { - - //===-------------===// - // Bug Descriptions. // - //===-------------===// - - class CFRefBug : public BugType { - protected: - CFRefCount& TF; - - CFRefBug(CFRefCount* tf, llvm::StringRef name) - : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} - public: - - CFRefCount& getTF() { return TF; } - const CFRefCount& getTF() const { return TF; } - - // FIXME: Eventually remove. - virtual const char* getDescription() const = 0; - - virtual bool isLeak() const { return false; } - }; - - class UseAfterRelease : public CFRefBug { - public: - UseAfterRelease(CFRefCount* tf) - : CFRefBug(tf, "Use-after-release") {} - - const char* getDescription() const { - return "Reference-counted object is used after it is released"; - } - }; - - class BadRelease : public CFRefBug { - public: - BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {} - - const char* getDescription() const { - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - } - }; - - class DeallocGC : public CFRefBug { - public: - DeallocGC(CFRefCount *tf) - : CFRefBug(tf, "-dealloc called while using garbage collection") {} - - const char *getDescription() const { - return "-dealloc called while using garbage collection"; - } - }; - - class DeallocNotOwned : public CFRefBug { - public: - DeallocNotOwned(CFRefCount *tf) - : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} - - const char *getDescription() const { - return "-dealloc sent to object that may be referenced elsewhere"; - } - }; - - class OverAutorelease : public CFRefBug { - public: - OverAutorelease(CFRefCount *tf) : - CFRefBug(tf, "Object sent -autorelease too many times") {} - - const char *getDescription() const { - return "Object sent -autorelease too many times"; - } - }; - - class ReturnedNotOwnedForOwned : public CFRefBug { - public: - ReturnedNotOwnedForOwned(CFRefCount *tf) : - CFRefBug(tf, "Method should return an owned object") {} - - const char *getDescription() const { - return "Object with +0 retain counts returned to caller where a +1 " - "(owning) retain count is expected"; - } - }; - - class Leak : public CFRefBug { - const bool isReturn; - protected: - Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) - : CFRefBug(tf, name), isReturn(isRet) {} - public: - - const char* getDescription() const { return ""; } - - bool isLeak() const { return true; } - }; - - class LeakAtReturn : public Leak { - public: - LeakAtReturn(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, true) {} - }; - - class LeakWithinFunction : public Leak { - public: - LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) - : Leak(tf, name, false) {} - }; - - //===---------===// - // Bug Reports. // - //===---------===// - - class CFRefReport : public RangedBugReport { - protected: - SymbolRef Sym; - const CFRefCount &TF; - public: - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym) - : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} - - CFRefReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, llvm::StringRef endText) - : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {} - - virtual ~CFRefReport() {} - - CFRefBug& getBugType() { - return (CFRefBug&) RangedBugReport::getBugType(); - } - const CFRefBug& getBugType() const { - return (const CFRefBug&) RangedBugReport::getBugType(); - } - - virtual void getRanges(const SourceRange*& beg, const SourceRange*& end) { - if (!getBugType().isLeak()) - RangedBugReport::getRanges(beg, end); - else - beg = end = 0; - } - - SymbolRef getSymbol() const { return Sym; } - - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); - - std::pair<const char**,const char**> getExtraDescriptiveText(); - - PathDiagnosticPiece* VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC); - }; - - class CFRefLeakReport : public CFRefReport { - SourceLocation AllocSite; - const MemRegion* AllocBinding; - public: - CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, SymbolRef sym, - GRExprEngine& Eng); - - PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, - const ExplodedNode* N); - - SourceLocation getLocation() const { return AllocSite; } - }; -} // end anonymous namespace - - - -static const char* Msgs[] = { - // GC only - "Code is compiled to only use garbage collection", - // No GC. - "Code is compiled to use reference counts", - // Hybrid, with GC. - "Code is compiled to use either garbage collection (GC) or reference counts" - " (non-GC). The bug occurs with GC enabled", - // Hybrid, without GC - "Code is compiled to use either garbage collection (GC) or reference counts" - " (non-GC). The bug occurs in non-GC mode" -}; - -std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() { - CFRefCount& TF = static_cast<CFRefBug&>(getBugType()).getTF(); - - switch (TF.getLangOptions().getGCMode()) { - default: - assert(false); - - case LangOptions::GCOnly: - assert (TF.isGCEnabled()); - return std::make_pair(&Msgs[0], &Msgs[0]+1); - - case LangOptions::NonGC: - assert (!TF.isGCEnabled()); - return std::make_pair(&Msgs[1], &Msgs[1]+1); - - case LangOptions::HybridGC: - if (TF.isGCEnabled()) - return std::make_pair(&Msgs[2], &Msgs[2]+1); - else - return std::make_pair(&Msgs[3], &Msgs[3]+1); - } -} - -static inline bool contains(const llvm::SmallVectorImpl<ArgEffect>& V, - ArgEffect X) { - for (llvm::SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end(); - I!=E; ++I) - if (*I == X) return true; - - return false; -} - -PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - BugReporterContext& BRC) { - - if (!isa<PostStmt>(N->getLocation())) - return NULL; - - // Check if the type state has changed. - const GRState *PrevSt = PrevN->getState(); - const GRState *CurrSt = N->getState(); - - const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); - if (!CurrT) return NULL; - - const RefVal &CurrV = *CurrT; - const RefVal *PrevT = PrevSt->get<RefBindings>(Sym); - - // Create a string buffer to constain all the useful things we want - // to tell the user. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - // This is the allocation site since the previous node had no bindings - // for this symbol. - if (!PrevT) { - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { - // Get the name of the callee (if it is available). - SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee()); - if (const FunctionDecl* FD = X.getAsFunctionDecl()) - os << "Call to function '" << FD->getNameAsString() <<'\''; - else - os << "function call"; - } - else { - assert (isa<ObjCMessageExpr>(S)); - os << "Method"; - } - - if (CurrV.getObjKind() == RetEffect::CF) { - os << " returns a Core Foundation object with a "; - } - else { - assert (CurrV.getObjKind() == RetEffect::ObjC); - os << " returns an Objective-C object with a "; - } - - if (CurrV.isOwned()) { - os << "+1 retain count (owning reference)."; - - if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) { - assert(CurrV.getObjKind() == RetEffect::CF); - os << " " - "Core Foundation objects are not automatically garbage collected."; - } - } - else { - assert (CurrV.isNotOwned()); - os << "+0 retain count (non-owning reference)."; - } - - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); - return new PathDiagnosticEventPiece(Pos, os.str()); - } - - // Gather up the effects that were performed on the object at this - // program point - llvm::SmallVector<ArgEffect, 2> AEffects; - - if (const RetainSummary *Summ = - TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) { - // We only have summaries attached to nodes after evaluating CallExpr and - // ObjCMessageExprs. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - - if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { - // Iterate through the parameter expressions and see if the symbol - // was ever passed as an argument. - unsigned i = 0; - - for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end(); - AI!=AE; ++AI, ++i) { - - // Retrieve the value of the argument. Is it the symbol - // we are interested in? - if (CurrSt->getSValAsScalarOrLoc(*AI).getAsLocSymbol() != Sym) - continue; - - // We have an argument. Get the effect! - AEffects.push_back(Summ->getArg(i)); - } - } - else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { - if (const Expr *receiver = ME->getReceiver()) - if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) { - // The symbol we are tracking is the receiver. - AEffects.push_back(Summ->getReceiverEffect()); - } - } - } - - do { - // Get the previous type state. - RefVal PrevV = *PrevT; - - // Specially handle -dealloc. - if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) { - // Determine if the object's reference count was pushed to zero. - assert(!(PrevV == CurrV) && "The typestate *must* have changed."); - // We may not have transitioned to 'release' if we hit an error. - // This case is handled elsewhere. - if (CurrV.getKind() == RefVal::Released) { - assert(CurrV.getCombinedCounts() == 0); - os << "Object released by directly sending the '-dealloc' message"; - break; - } - } - - // Specially handle CFMakeCollectable and friends. - if (contains(AEffects, MakeCollectable)) { - // Get the name of the function. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - SVal X = CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - const std::string& FName = FD->getNameAsString(); - - if (TF.isGCEnabled()) { - // Determine if the object's reference count was pushed to zero. - assert(!(PrevV == CurrV) && "The typestate *must* have changed."); - - os << "In GC mode a call to '" << FName - << "' decrements an object's retain count and registers the " - "object with the garbage collector. "; - - if (CurrV.getKind() == RefVal::Released) { - assert(CurrV.getCount() == 0); - os << "Since it now has a 0 retain count the object can be " - "automatically collected by the garbage collector."; - } - else - os << "An object must have a 0 retain count to be garbage collected. " - "After this call its retain count is +" << CurrV.getCount() - << '.'; - } - else - os << "When GC is not enabled a call to '" << FName - << "' has no effect on its argument."; - - // Nothing more to say. - break; - } - - // Determine if the typestate has changed. - if (!(PrevV == CurrV)) - switch (CurrV.getKind()) { - case RefVal::Owned: - case RefVal::NotOwned: - - if (PrevV.getCount() == CurrV.getCount()) { - // Did an autorelease message get sent? - if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) - return 0; - - assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); - os << "Object sent -autorelease message"; - break; - } - - if (PrevV.getCount() > CurrV.getCount()) - os << "Reference count decremented."; - else - os << "Reference count incremented."; - - if (unsigned Count = CurrV.getCount()) - os << " The object now has a +" << Count << " retain count."; - - if (PrevV.getKind() == RefVal::Released) { - assert(TF.isGCEnabled() && CurrV.getCount() > 0); - os << " The object is not eligible for garbage collection until the " - "retain count reaches 0 again."; - } - - break; - - case RefVal::Released: - os << "Object released."; - break; - - case RefVal::ReturnedOwned: - os << "Object returned to caller as an owning reference (single retain " - "count transferred to caller)."; - break; - - case RefVal::ReturnedNotOwned: - os << "Object returned to caller with a +0 (non-owning) retain count."; - break; - - default: - return NULL; - } - - // Emit any remaining diagnostics for the argument effects (if any). - for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(), - E=AEffects.end(); I != E; ++I) { - - // A bunch of things have alternate behavior under GC. - if (TF.isGCEnabled()) - switch (*I) { - default: break; - case Autorelease: - os << "In GC mode an 'autorelease' has no effect."; - continue; - case IncRefMsg: - os << "In GC mode the 'retain' message has no effect."; - continue; - case DecRefMsg: - os << "In GC mode the 'release' message has no effect."; - continue; - } - } - } while (0); - - if (os.str().empty()) - return 0; // We have nothing to say! - - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - PathDiagnosticLocation Pos(S, BRC.getSourceManager()); - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str()); - - // Add the range by scanning the children of the statement for any bindings - // to Sym. - for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); - I!=E; ++I) - if (const Expr* Exp = dyn_cast_or_null<Expr>(*I)) - if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { - P->addRange(Exp->getSourceRange()); - break; - } - - return P; -} - -namespace { - class FindUniqueBinding : - public StoreManager::BindingsHandler { - SymbolRef Sym; - const MemRegion* Binding; - bool First; - - public: - FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {} - - bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, - SVal val) { - - SymbolRef SymV = val.getAsSymbol(); - if (!SymV || SymV != Sym) - return true; - - if (Binding) { - First = false; - return false; - } - else - Binding = R; - - return true; - } - - operator bool() { return First && Binding; } - const MemRegion* getRegion() { return Binding; } - }; -} - -static std::pair<const ExplodedNode*,const MemRegion*> -GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N, - SymbolRef Sym) { - - // Find both first node that referred to the tracked symbol and the - // memory location that value was store to. - const ExplodedNode* Last = N; - const MemRegion* FirstBinding = 0; - - while (N) { - const GRState* St = N->getState(); - RefBindings B = St->get<RefBindings>(); - - if (!B.lookup(Sym)) - break; - - FindUniqueBinding FB(Sym); - StateMgr.iterBindings(St, FB); - if (FB) FirstBinding = FB.getRegion(); - - Last = N; - N = N->pred_empty() ? NULL : *(N->pred_begin()); - } - - return std::make_pair(Last, FirstBinding); -} - -PathDiagnosticPiece* -CFRefReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN) { - // Tell the BugReporterContext to report cases when the tracked symbol is - // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); - return RangedBugReport::getEndPath(BRC, EndN); -} - -PathDiagnosticPiece* -CFRefLeakReport::getEndPath(BugReporterContext& BRC, - const ExplodedNode* EndN){ - - // Tell the BugReporterContext to report cases when the tracked symbol is - // assigned to different variables, etc. - BRC.addNotableSymbol(Sym); - - // We are reporting a leak. Walk up the graph to get to the first node where - // the symbol appeared, and also get the first VarDecl that tracked object - // is stored to. - const ExplodedNode* AllocNode = 0; - const MemRegion* FirstBinding = 0; - - llvm::tie(AllocNode, FirstBinding) = - GetAllocationSite(BRC.getStateManager(), EndN, Sym); - - // Get the allocate site. - assert(AllocNode); - const Stmt* FirstStmt = cast<PostStmt>(AllocNode->getLocation()).getStmt(); - - SourceManager& SMgr = BRC.getSourceManager(); - unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart()); - - // Compute an actual location for the leak. Sometimes a leak doesn't - // occur at an actual statement (e.g., transition between blocks; end - // of function) so we need to walk the graph and compute a real location. - const ExplodedNode* LeakN = EndN; - PathDiagnosticLocation L; - - while (LeakN) { - ProgramPoint P = LeakN->getLocation(); - - if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { - L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr); - break; - } - else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - if (const Stmt* Term = BE->getSrc()->getTerminator()) { - L = PathDiagnosticLocation(Term->getLocStart(), SMgr); - break; - } - } - - LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin()); - } - - if (!L.isValid()) { - const Decl &D = EndN->getCodeDecl(); - L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr); - } - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Object allocated on line " << AllocLine; - - if (FirstBinding) - os << " and stored into '" << FirstBinding->getString() << '\''; - - // Get the retain count. - const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); - - if (RV->getKind() == RefVal::ErrorLeakReturned) { - // FIXME: Per comments in rdar://6320065, "create" only applies to CF - // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership - // to the caller for NS objects. - ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); - os << " is returned from a method whose name ('" - << MD.getSelector().getAsString() - << "') does not contain 'copy' or otherwise starts with" - " 'new' or 'alloc'. This violates the naming convention rules given" - " in the Memory Management Guide for Cocoa (object leaked)"; - } - else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { - ObjCMethodDecl& MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); - os << " and returned from method '" << MD.getSelector().getAsString() - << "' is potentially leaked when using garbage collection. Callers " - "of this method do not expect a returned object with a +1 retain " - "count since they expect the object to be managed by the garbage " - "collector"; - } - else - os << " is no longer referenced after this point and has a retain count of" - " +" << RV->getCount() << " (object leaked)"; - - return new PathDiagnosticEventPiece(L, os.str()); -} - -CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, - ExplodedNode *n, - SymbolRef sym, GRExprEngine& Eng) -: CFRefReport(D, tf, n, sym) { - - // Most bug reports are cached at the location where they occured. - // With leaks, we want to unique them by the location where they were - // allocated, and only report a single path. To do this, we need to find - // the allocation site of a piece of tracked memory, which we do via a - // call to GetAllocationSite. This will walk the ExplodedGraph backwards. - // Note that this is *not* the trimmed graph; we are guaranteed, however, - // that all ancestor nodes that represent the allocation site have the - // same SourceLocation. - const ExplodedNode* AllocNode = 0; - - llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. - GetAllocationSite(Eng.getStateManager(), getEndNode(), getSymbol()); - - // Get the SourceLocation for the allocation site. - ProgramPoint P = AllocNode->getLocation(); - AllocSite = cast<PostStmt>(P).getStmt()->getLocStart(); - - // Fill in the description of the bug. - Description.clear(); - llvm::raw_string_ostream os(Description); - SourceManager& SMgr = Eng.getContext().getSourceManager(); - unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite); - os << "Potential leak "; - if (tf.isGCEnabled()) { - os << "(when using garbage collection) "; - } - os << "of an object allocated on line " << AllocLine; - - // FIXME: AllocBinding doesn't get populated for RegionStore yet. - if (AllocBinding) - os << " and stored into '" << AllocBinding->getString() << '\''; -} - -//===----------------------------------------------------------------------===// -// Main checker logic. -//===----------------------------------------------------------------------===// - -/// GetReturnType - Used to get the return type of a message expression or -/// function call with the intention of affixing that type to a tracked symbol. -/// While the the return type can be queried directly from RetEx, when -/// invoking class methods we augment to the return type to be that of -/// a pointer to the class (as opposed it just being id). -static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { - QualType RetTy = RetE->getType(); - // If RetE is not a message expression just return its type. - // If RetE is a message expression, return its types if it is something - /// more specific than id. - if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE)) - if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>()) - if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || - PT->isObjCClassType()) { - // At this point we know the return type of the message expression is - // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this - // is a call to a class method whose type we can resolve. In such - // cases, promote the return type to XXX* (where XXX is the class). - const ObjCInterfaceDecl *D = ME->getClassInfo().first; - return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D)); - } - - return RetTy; -} - -void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - Expr* Ex, - Expr* Receiver, - const RetainSummary& Summ, - const MemRegion *Callee, - ExprIterator arg_beg, ExprIterator arg_end, - ExplodedNode* Pred, const GRState *state) { - - // Evaluate the effect of the arguments. - RefVal::Kind hasErr = (RefVal::Kind) 0; - unsigned idx = 0; - Expr* ErrorExpr = NULL; - SymbolRef ErrorSym = 0; - - llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate; - - for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { - SVal V = state->getSValAsScalarOrLoc(*I); - SymbolRef Sym = V.getAsLocSymbol(); - - if (Sym) - if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) { - state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); - if (hasErr) { - ErrorExpr = *I; - ErrorSym = Sym; - break; - } - continue; - } - - tryAgain: - if (isa<Loc>(V)) { - if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(&V)) { - if (Summ.getArg(idx) == DoNothingByRef) - continue; - - // Invalidate the value of the variable passed by reference. - const MemRegion *R = MR->getRegion(); - - // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underying 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 - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // 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 - // still allowing us to do checker-specific logic (e.g., - // invalidating reference counts), probably via callbacks. - if (ER->getElementType()->isIntegralType()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || - isa<ObjCIvarRegion>(superReg)) - R = cast<TypedRegion>(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - continue; - } - else { - // Nuke all other arguments passed by reference. - // FIXME: is this necessary or correct? This handles the non-Region - // cases. Is it ever valid to store to these? - state = state->unbindLoc(cast<Loc>(V)); - } - } - else if (isa<nonloc::LocAsInteger>(V)) { - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - V = cast<nonloc::LocAsInteger>(V).getLoc(); - goto tryAgain; - } - } - - // Block calls result in all captured values passed-via-reference to be - // invalidated. - if (const BlockDataRegion *BR = dyn_cast_or_null<BlockDataRegion>(Callee)) { - RegionsToInvalidate.push_back(BR); - } - - // Invalidate regions we designed for invalidation use the batch invalidation - // API. - if (!RegionsToInvalidate.empty()) { - // FIXME: We can have collisions on the conjured symbol if the - // expression *I also creates conjured symbols. We probably want - // to identify conjured symbols by an expression pair: the enclosing - // expression (the context) and the expression itself. This should - // disambiguate conjured symbols. - unsigned Count = Builder.getCurrentBlockCount(); - StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); - - - StoreManager::InvalidatedSymbols IS; - state = StoreMgr.InvalidateRegions(state, RegionsToInvalidate.data(), - RegionsToInvalidate.data() + - RegionsToInvalidate.size(), - Ex, Count, &IS); - for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), - E = IS.end(); I!=E; ++I) { - // Remove any existing reference-count binding. - state = state->remove<RefBindings>(*I); - } - } - - // Evaluate the effect on the message receiver. - if (!ErrorExpr && Receiver) { - SymbolRef Sym = state->getSValAsScalarOrLoc(Receiver).getAsLocSymbol(); - if (Sym) { - if (const RefVal* T = state->get<RefBindings>(Sym)) { - state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr); - if (hasErr) { - ErrorExpr = Receiver; - ErrorSym = Sym; - } - } - } - } - - // Process any errors. - if (hasErr) { - ProcessNonLeakError(Dst, Builder, Ex, ErrorExpr, Pred, state, - hasErr, ErrorSym); - return; - } - - // Consult the summary for the return value. - RetEffect RE = Summ.getRetEffect(); - - if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { - assert(Receiver); - SVal V = state->getSValAsScalarOrLoc(Receiver); - bool found = false; - if (SymbolRef Sym = V.getAsLocSymbol()) - if (state->get<RefBindings>(Sym)) { - found = true; - RE = Summaries.getObjAllocRetEffect(); - } - - if (!found) - RE = RetEffect::MakeNoRet(); - } - - switch (RE.getKind()) { - default: - assert (false && "Unhandled RetEffect."); break; - - case RetEffect::NoRet: { - // Make up a symbol for the return value (not reference counted). - // FIXME: Most of this logic is not specific to the retain/release - // checker. - - // FIXME: We eventually should handle structs and other compound types - // that are returned by value. - - QualType T = Ex->getType(); - - // For CallExpr, use the result type to know if it returns a reference. - if (const CallExpr *CE = dyn_cast<CallExpr>(Ex)) { - const Expr *Callee = CE->getCallee(); - if (const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl()) - T = FD->getResultType(); - } - else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(Ex)) { - if (const ObjCMethodDecl *MD = ME->getMethodDecl()) - T = MD->getResultType(); - } - - if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { - unsigned Count = Builder.getCurrentBlockCount(); - ValueManager &ValMgr = Eng.getValueManager(); - SVal X = ValMgr.getConjuredSymbolVal(NULL, Ex, T, Count); - state = state->BindExpr(Ex, X, false); - } - - break; - } - - case RetEffect::Alias: { - unsigned idx = RE.getIndex(); - assert (arg_end >= arg_beg); - assert (idx < (unsigned) (arg_end - arg_beg)); - SVal V = state->getSValAsScalarOrLoc(*(arg_beg+idx)); - state = state->BindExpr(Ex, V, false); - break; - } - - case RetEffect::ReceiverAlias: { - assert (Receiver); - SVal V = state->getSValAsScalarOrLoc(Receiver); - state = state->BindExpr(Ex, V, false); - break; - } - - case RetEffect::OwnedAllocatedSymbol: - case RetEffect::OwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - ValueManager &ValMgr = Eng.getValueManager(); - SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count); - QualType RetT = GetReturnType(Ex, ValMgr.getContext()); - state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false); - - // FIXME: Add a flag to the checker where allocations are assumed to - // *not fail. -#if 0 - if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { - bool isFeasible; - state = state.Assume(loc::SymbolVal(Sym), true, isFeasible); - assert(isFeasible && "Cannot assume fresh symbol is non-null."); - } -#endif - - break; - } - - case RetEffect::GCNotOwnedSymbol: - case RetEffect::NotOwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - ValueManager &ValMgr = Eng.getValueManager(); - SymbolRef Sym = ValMgr.getConjuredSymbol(Ex, Count); - QualType RetT = GetReturnType(Ex, ValMgr.getContext()); - state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, ValMgr.makeLoc(Sym), false); - break; - } - } - - // Generate a sink node if we are at the end of a path. - ExplodedNode *NewNode = - Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state) - : Builder.MakeNode(Dst, Ex, Pred, state); - - // Annotate the edge with summary we used. - if (NewNode) SummaryLog[NewNode] = &Summ; -} - - -void CFRefCount::EvalCall(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, - ExplodedNode* Pred) { - - RetainSummary *Summ = 0; - - // FIXME: Better support for blocks. For now we stop tracking anything - // that is passed to blocks. - // FIXME: Need to handle variables that are "captured" by the block. - if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { - Summ = Summaries.getPersistentStopSummary(); - } - else { - const FunctionDecl* FD = L.getAsFunctionDecl(); - Summ = !FD ? Summaries.getDefaultSummary() : - Summaries.getSummary(const_cast<FunctionDecl*>(FD)); - } - - assert(Summ); - EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(), - CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred)); -} - -void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred, - const GRState *state) { - RetainSummary *Summ = - ME->getReceiver() - ? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext()) - : Summaries.getClassMethodSummary(ME); - - assert(Summ && "RetainSummary is null"); - EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, NULL, - ME->arg_begin(), ME->arg_end(), Pred, state); -} - -namespace { -class StopTrackingCallback : public SymbolVisitor { - const GRState *state; -public: - StopTrackingCallback(const GRState *st) : state(st) {} - const GRState *getState() const { return state; } - - bool VisitSymbol(SymbolRef sym) { - state = state->remove<RefBindings>(sym); - return true; - } -}; -} // end anonymous namespace - - -void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) { - // Are we storing to something that causes the value to "escape"? - bool escapes = false; - - // A value escapes in three possible cases (this may change): - // - // (1) we are binding to something that is not a memory region. - // (2) we are binding to a memregion that does not have stack storage - // (3) we are binding to a memregion with stack storage that the store - // does not understand. - const GRState *state = B.getState(); - - if (!isa<loc::MemRegionVal>(location)) - escapes = true; - else { - const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion(); - escapes = !R->hasStackStorage(); - - if (!escapes) { - // To test (3), generate a new state with the binding removed. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - escapes = (state == (state->bindLoc(cast<Loc>(location), UnknownVal()))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return; - - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - B.MakeNode(state->scanReachableSymbols<StopTrackingCallback>(val).getState()); -} - - // Return statements. - -void CFRefCount::EvalReturn(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - ReturnStmt* S, - ExplodedNode* Pred) { - - Expr* RetE = S->getRetValue(); - if (!RetE) - return; - - const GRState *state = Builder.GetState(Pred); - SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); - - if (!Sym) - return; - - // Get the reference count binding (if any). - const RefVal* T = state->get<RefBindings>(Sym); - - if (!T) - return; - - // Change the reference count. - RefVal X = *T; - - switch (X.getKind()) { - case RefVal::Owned: { - unsigned cnt = X.getCount(); - assert (cnt > 0); - X.setCount(cnt - 1); - X = X ^ RefVal::ReturnedOwned; - break; - } - - case RefVal::NotOwned: { - unsigned cnt = X.getCount(); - if (cnt) { - X.setCount(cnt - 1); - X = X ^ RefVal::ReturnedOwned; - } - else { - X = X ^ RefVal::ReturnedNotOwned; - } - break; - } - - default: - return; - } - - // Update the binding. - state = state->set<RefBindings>(Sym, X); - Pred = Builder.MakeNode(Dst, S, Pred, state); - - // Did we cache out? - if (!Pred) - return; - - // Update the autorelease counts. - static unsigned autoreleasetag = 0; - GenericNodeBuilder Bd(Builder, S, &autoreleasetag); - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym, - X, stop); - - // Did we cache out? - if (!Pred || stop) - return; - - // Get the updated binding. - T = state->get<RefBindings>(Sym); - assert(T); - X = *T; - - // Any leaks or other errors? - if (X.isReturnedOwned() && X.getCount() == 0) { - Decl const *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - RetEffect RE = Summ.getRetEffect(); - bool hasError = false; - - if (RE.getKind() != RetEffect::NoRet) { - if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { - // Things are more complicated with garbage collection. If the - // returned object is suppose to be an Objective-C object, we have - // a leak (as the caller expects a GC'ed object) because no - // method should return ownership unless it returns a CF object. - hasError = true; - X = X ^ RefVal::ErrorGCLeakReturned; - } - else if (!RE.isOwned()) { - // Either we are using GC and the returned object is a CF type - // or we aren't using GC. In either case, we expect that the - // enclosing method is expected to return ownership. - hasError = true; - X = X ^ RefVal::ErrorLeakReturned; - } - } - - if (hasError) { - // Generate an error node. - static int ReturnOwnLeakTag = 0; - state = state->set<RefBindings>(Sym, X); - ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnOwnLeakTag), state, Pred); - if (N) { - CFRefReport *report = - new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this, - N, Sym, Eng); - BR->EmitReport(report); - } - } - } - } - else if (X.isReturnedNotOwned()) { - Decl const *CD = &Pred->getCodeDecl(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) { - const RetainSummary &Summ = *Summaries.getMethodSummary(MD); - if (Summ.getRetEffect().isOwned()) { - // Trying to return a not owned object to a caller expecting an - // owned object. - - static int ReturnNotOwnedForOwnedTag = 0; - state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); - if (ExplodedNode *N = - Builder.generateNode(PostStmt(S, Pred->getLocationContext(), - &ReturnNotOwnedForOwnedTag), - state, Pred)) { - CFRefReport *report = - new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), - *this, N, Sym); - BR->EmitReport(report); - } - } - } - } -} - -// Assumptions. - -const GRState* CFRefCount::EvalAssume(const GRState *state, - SVal Cond, bool Assumption) { - - // FIXME: We may add to the interface of EvalAssume the list of symbols - // whose assumptions have changed. For now we just iterate through the - // bindings and check if any of the tracked symbols are NULL. This isn't - // too bad since the number of symbols we will track in practice are - // probably small and EvalAssume is only called at branches and a few - // other places. - RefBindings B = state->get<RefBindings>(); - - if (B.isEmpty()) - return state; - - bool changed = false; - RefBindings::Factory& RefBFactory = state->get_context<RefBindings>(); - - for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { - // Check if the symbol is null (or equal to any constant). - // If this is the case, stop tracking the symbol. - if (state->getSymVal(I.getKey())) { - changed = true; - B = RefBFactory.Remove(B, I.getKey()); - } - } - - if (changed) - state = state->set<RefBindings>(B); - - return state; -} - -const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, - RefVal V, ArgEffect E, - RefVal::Kind& hasErr) { - - // In GC mode [... release] and [... retain] do nothing. - switch (E) { - default: break; - case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; - case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; - case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; - case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : - NewAutoreleasePool; break; - } - - // Handle all use-after-releases. - if (!isGCEnabled() && V.getKind() == RefVal::Released) { - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - return state->set<RefBindings>(sym, V); - } - - switch (E) { - default: - assert (false && "Unhandled CFRef transition."); - - case Dealloc: - // Any use of -dealloc in GC is *bad*. - if (isGCEnabled()) { - V = V ^ RefVal::ErrorDeallocGC; - hasErr = V.getKind(); - break; - } - - switch (V.getKind()) { - default: - assert(false && "Invalid case."); - case RefVal::Owned: - // The object immediately transitions to the released state. - V = V ^ RefVal::Released; - V.clearCounts(); - return state->set<RefBindings>(sym, V); - case RefVal::NotOwned: - V = V ^ RefVal::ErrorDeallocNotOwned; - hasErr = V.getKind(); - break; - } - break; - - case NewAutoreleasePool: - assert(!isGCEnabled()); - return state->add<AutoreleaseStack>(sym); - - case MayEscape: - if (V.getKind() == RefVal::Owned) { - V = V ^ RefVal::NotOwned; - break; - } - - // Fall-through. - - case DoNothingByRef: - case DoNothing: - return state; - - case Autorelease: - if (isGCEnabled()) - return state; - - // Update the autorelease counts. - state = SendAutorelease(state, ARCountFactory, sym); - V = V.autorelease(); - break; - - case StopTracking: - return state->remove<RefBindings>(sym); - - case IncRef: - switch (V.getKind()) { - default: - assert(false); - - case RefVal::Owned: - case RefVal::NotOwned: - V = V + 1; - break; - case RefVal::Released: - // Non-GC cases are handled above. - assert(isGCEnabled()); - V = (V ^ RefVal::Owned) + 1; - break; - } - break; - - case SelfOwn: - V = V ^ RefVal::NotOwned; - // Fall-through. - case DecRef: - switch (V.getKind()) { - default: - // case 'RefVal::Released' handled above. - assert (false); - - case RefVal::Owned: - assert(V.getCount() > 0); - if (V.getCount() == 1) V = V ^ RefVal::Released; - V = V - 1; - break; - - case RefVal::NotOwned: - if (V.getCount() > 0) - V = V - 1; - else { - V = V ^ RefVal::ErrorReleaseNotOwned; - hasErr = V.getKind(); - } - break; - - case RefVal::Released: - // Non-GC cases are handled above. - assert(isGCEnabled()); - V = V ^ RefVal::ErrorUseAfterRelease; - hasErr = V.getKind(); - break; - } - break; - } - return state->set<RefBindings>(sym, V); -} - -//===----------------------------------------------------------------------===// -// Handle dead symbols and end-of-path. -//===----------------------------------------------------------------------===// - -std::pair<ExplodedNode*, const GRState *> -CFRefCount::HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, - ExplodedNode* Pred, - GRExprEngine &Eng, - SymbolRef Sym, RefVal V, bool &stop) { - - unsigned ACnt = V.getAutoreleaseCount(); - stop = false; - - // No autorelease counts? Nothing to be done. - if (!ACnt) - return std::make_pair(Pred, state); - - assert(!isGCEnabled() && "Autorelease counts in GC mode?"); - unsigned Cnt = V.getCount(); - - // FIXME: Handle sending 'autorelease' to already released object. - - if (V.getKind() == RefVal::ReturnedOwned) - ++Cnt; - - if (ACnt <= Cnt) { - if (ACnt == Cnt) { - V.clearCounts(); - if (V.getKind() == RefVal::ReturnedOwned) - V = V ^ RefVal::ReturnedNotOwned; - else - V = V ^ RefVal::NotOwned; - } - else { - V.setCount(Cnt - ACnt); - V.setAutoreleaseCount(0); - } - state = state->set<RefBindings>(Sym, V); - ExplodedNode *N = Bd.MakeNode(state, Pred); - stop = (N == 0); - return std::make_pair(N, state); - } - - // Woah! More autorelease counts then retain counts left. - // Emit hard error. - stop = true; - V = V ^ RefVal::ErrorOverAutorelease; - state = state->set<RefBindings>(Sym, V); - - if (ExplodedNode *N = Bd.MakeNode(state, Pred)) { - N->markAsSink(); - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Object over-autoreleased: object was sent -autorelease"; - if (V.getAutoreleaseCount() > 1) - os << V.getAutoreleaseCount() << " times"; - os << " but the object has "; - if (V.getCount() == 0) - os << "zero (locally visible)"; - else - os << "+" << V.getCount(); - os << " retain counts"; - - CFRefReport *report = - new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), - *this, N, Sym, os.str()); - BR->EmitReport(report); - } - - return std::make_pair((ExplodedNode*)0, state); -} - -const GRState * -CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, - llvm::SmallVectorImpl<SymbolRef> &Leaked) { - - bool hasLeak = V.isOwned() || - ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0); - - if (!hasLeak) - return state->remove<RefBindings>(sid); - - Leaked.push_back(sid); - return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak); -} - -ExplodedNode* -CFRefCount::ProcessLeaks(const GRState * state, - llvm::SmallVectorImpl<SymbolRef> &Leaked, - GenericNodeBuilder &Builder, - GRExprEngine& Eng, - ExplodedNode *Pred) { - - if (Leaked.empty()) - return Pred; - - // Generate an intermediate node representing the leak point. - ExplodedNode *N = Builder.MakeNode(state, Pred); - - if (N) { - for (llvm::SmallVectorImpl<SymbolRef>::iterator - I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { - - CFRefBug *BT = static_cast<CFRefBug*>(Pred ? leakWithinFunction - : leakAtReturn); - assert(BT && "BugType not initialized."); - CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng); - BR->EmitReport(report); - } - } - - return N; -} - -void CFRefCount::EvalEndPath(GRExprEngine& Eng, - GREndPathNodeBuilder& Builder) { - - const GRState *state = Builder.getState(); - GenericNodeBuilder Bd(Builder); - RefBindings B = state->get<RefBindings>(); - ExplodedNode *Pred = 0; - - for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, - (*I).first, - (*I).second, stop); - - if (stop) - return; - } - - B = state->get<RefBindings>(); - llvm::SmallVector<SymbolRef, 10> Leaked; - - for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked); - - ProcessLeaks(state, Leaked, Bd, Eng, Pred); -} - -void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst, - GRExprEngine& Eng, - GRStmtNodeBuilder& Builder, - ExplodedNode* Pred, - Stmt* S, - const GRState* state, - SymbolReaper& SymReaper) { - - RefBindings B = state->get<RefBindings>(); - - // Update counts from autorelease pools - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - if (const RefVal* T = B.lookup(Sym)){ - // Use the symbol as the tag. - // FIXME: This might not be as unique as we would like. - GenericNodeBuilder Bd(Builder, S, Sym); - bool stop = false; - llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, - Sym, *T, stop); - if (stop) - return; - } - } - - B = state->get<RefBindings>(); - llvm::SmallVector<SymbolRef, 10> Leaked; - - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - if (const RefVal* T = B.lookup(*I)) - state = HandleSymbolDeath(state, *I, *T, Leaked); - } - - static unsigned LeakPPTag = 0; - { - GenericNodeBuilder Bd(Builder, S, &LeakPPTag); - Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred); - } - - // Did we cache out? - if (!Pred) - return; - - // Now generate a new node that nukes the old bindings. - RefBindings::Factory& F = state->get_context<RefBindings>(); - - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I!=E; ++I) B = F.Remove(B, *I); - - state = state->set<RefBindings>(B); - Builder.MakeNode(Dst, S, Pred, state); -} - -void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, - GRStmtNodeBuilder& Builder, - Expr* NodeExpr, Expr* ErrorExpr, - ExplodedNode* Pred, - const GRState* St, - RefVal::Kind hasErr, SymbolRef Sym) { - Builder.BuildSinks = true; - ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St); - - if (!N) - return; - - CFRefBug *BT = 0; - - switch (hasErr) { - default: - assert(false && "Unhandled error."); - return; - case RefVal::ErrorUseAfterRelease: - BT = static_cast<CFRefBug*>(useAfterRelease); - break; - case RefVal::ErrorReleaseNotOwned: - BT = static_cast<CFRefBug*>(releaseNotOwned); - break; - case RefVal::ErrorDeallocGC: - BT = static_cast<CFRefBug*>(deallocGC); - break; - case RefVal::ErrorDeallocNotOwned: - BT = static_cast<CFRefBug*>(deallocNotOwned); - break; - } - - CFRefReport *report = new CFRefReport(*BT, *this, N, Sym); - report->addRange(ErrorExpr->getSourceRange()); - BR->EmitReport(report); -} - -//===----------------------------------------------------------------------===// -// Pieces of the retain/release checker implemented using a CheckerVisitor. -// More pieces of the retain/release checker will be migrated to this interface -// (ideally, all of it some day). -//===----------------------------------------------------------------------===// - -namespace { -class RetainReleaseChecker - : public CheckerVisitor<RetainReleaseChecker> { - CFRefCount *TF; -public: - RetainReleaseChecker(CFRefCount *tf) : TF(tf) {} - static void* getTag() { static int x = 0; return &x; } - - void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); -}; -} // end anonymous namespace - - -void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C, - const BlockExpr *BE) { - - // Scan the BlockDecRefExprs for any object the retain/release checker - // may be tracking. - if (!BE->hasBlockDeclRefExprs()) - return; - - const GRState *state = C.getState(); - const BlockDataRegion *R = - cast<BlockDataRegion>(state->getSVal(BE).getAsRegion()); - - BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), - E = R->referenced_vars_end(); - - if (I == E) - return; - - // FIXME: For now we invalidate the tracking of all symbols passed to blocks - // via captured variables, even though captured variables result in a copy - // and in implicit increment/decrement of a retain count. - llvm::SmallVector<const MemRegion*, 10> Regions; - const LocationContext *LC = C.getPredecessor()->getLocationContext(); - MemRegionManager &MemMgr = C.getValueManager().getRegionManager(); - - for ( ; I != E; ++I) { - const VarRegion *VR = *I; - if (VR->getSuperRegion() == R) { - VR = MemMgr.getVarRegion(VR->getDecl(), LC); - } - Regions.push_back(VR); - } - - state = - state->scanReachableSymbols<StopTrackingCallback>(Regions.data(), - Regions.data() + Regions.size()).getState(); - C.addTransition(state); -} - -//===----------------------------------------------------------------------===// -// Transfer function creation for external clients. -//===----------------------------------------------------------------------===// - -void CFRefCount::RegisterChecks(GRExprEngine& Eng) { - BugReporter &BR = Eng.getBugReporter(); - - useAfterRelease = new UseAfterRelease(this); - BR.Register(useAfterRelease); - - releaseNotOwned = new BadRelease(this); - BR.Register(releaseNotOwned); - - deallocGC = new DeallocGC(this); - BR.Register(deallocGC); - - deallocNotOwned = new DeallocNotOwned(this); - BR.Register(deallocNotOwned); - - overAutorelease = new OverAutorelease(this); - BR.Register(overAutorelease); - - returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); - BR.Register(returnNotOwnedForOwned); - - // First register "return" leaks. - const char* name = 0; - - if (isGCEnabled()) - name = "Leak of returned object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of returned object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak of returned object"; - } - - // Leaks should not be reported if they are post-dominated by a sink. - leakAtReturn = new LeakAtReturn(this, name); - leakAtReturn->setSuppressOnSink(true); - BR.Register(leakAtReturn); - - // Second, register leaks within a function/method. - if (isGCEnabled()) - name = "Leak of object when using garbage collection"; - else if (getLangOptions().getGCMode() == LangOptions::HybridGC) - name = "Leak of object when not using garbage collection (GC) in " - "dual GC/non-GC code"; - else { - assert(getLangOptions().getGCMode() == LangOptions::NonGC); - name = "Leak"; - } - - // Leaks should not be reported if they are post-dominated by sinks. - leakWithinFunction = new LeakWithinFunction(this, name); - leakWithinFunction->setSuppressOnSink(true); - BR.Register(leakWithinFunction); - - // Save the reference to the BugReporter. - this->BR = &BR; - - // Register the RetainReleaseChecker with the GRExprEngine object. - // Functionality in CFRefCount will be migrated to RetainReleaseChecker - // over time. - Eng.registerCheck(new RetainReleaseChecker(this)); -} - -GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, - const LangOptions& lopts) { - return new CFRefCount(Ctx, GCEnabled, lopts); -} diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 521f1be..4f8259e 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -2,67 +2,10 @@ set(LLVM_NO_RTTI 1) add_clang_library(clangAnalysis AnalysisContext.cpp - ArrayBoundChecker.cpp - AttrNonNullChecker.cpp - BasicConstraintManager.cpp - BasicObjCFoundationChecks.cpp - BasicStore.cpp - BasicValueFactory.cpp - BugReporter.cpp - BugReporterVisitors.cpp - BuiltinFunctionChecker.cpp CFG.cpp - CFRefCount.cpp - CallAndMessageChecker.cpp - CallInliner.cpp - CastToStructChecker.cpp - CheckDeadStores.cpp - CheckObjCDealloc.cpp - CheckObjCInstMethSignature.cpp - CheckObjCUnusedIVars.cpp - CheckSecuritySyntaxOnly.cpp - CheckSizeofPointer.cpp - Checker.cpp - DereferenceChecker.cpp - DivZeroChecker.cpp - Environment.cpp - ExplodedGraph.cpp - FixedAddressChecker.cpp - GRBlockCounter.cpp - GRCoreEngine.cpp - GRExprEngine.cpp - GRExprEngineExperimentalChecks.cpp - GRState.cpp LiveVariables.cpp - MallocChecker.cpp - ManagerRegistry.cpp - MemRegion.cpp - NoReturnFunctionChecker.cpp - NSAutoreleasePoolChecker.cpp - NSErrorChecker.cpp - OSAtomicChecker.cpp - PathDiagnostic.cpp - PointerArithChecker.cpp - PointerSubChecker.cpp - PthreadLockChecker.cpp - RangeConstraintManager.cpp - RegionStore.cpp - ReturnPointerRangeChecker.cpp - ReturnStackAddressChecker.cpp - ReturnUndefChecker.cpp - SVals.cpp - SValuator.cpp - SimpleConstraintManager.cpp - SimpleSValuator.cpp - Store.cpp - SymbolManager.cpp - UndefBranchChecker.cpp - UndefResultChecker.cpp - UndefinedArraySubscriptChecker.cpp - UndefinedAssignmentChecker.cpp + PrintfFormatString.cpp UninitializedValues.cpp - VLASizeChecker.cpp - ValueManager.cpp ) add_dependencies(clangAnalysis ClangDiagnosticAnalysis) diff --git a/lib/Analysis/CallAndMessageChecker.cpp b/lib/Analysis/CallAndMessageChecker.cpp deleted file mode 100644 index c287354..0000000 --- a/lib/Analysis/CallAndMessageChecker.cpp +++ /dev/null @@ -1,252 +0,0 @@ -//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines CallAndMessageChecker, a builtin checker that checks for various -// errors of call and objc message expressions. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/ParentMap.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class CallAndMessageChecker - : public CheckerVisitor<CallAndMessageChecker> { - BugType *BT_call_null; - BugType *BT_call_undef; - BugType *BT_call_arg; - BugType *BT_msg_undef; - BugType *BT_msg_arg; - BugType *BT_msg_ret; -public: - CallAndMessageChecker() : - BT_call_null(0), BT_call_undef(0), BT_call_arg(0), - BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {} - - static void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); - bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME); - -private: - void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); - void EmitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME, - ExplodedNode *N); - - void HandleNilReceiver(CheckerContext &C, const GRState *state, - const ObjCMessageExpr *ME); -}; -} // end anonymous namespace - -void clang::RegisterCallAndMessageChecker(GRExprEngine &Eng) { - Eng.registerCheck(new CallAndMessageChecker()); -} - -void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, - const CallExpr *CE) { - ExplodedNode *N = C.GenerateSink(); - if (!N) - return; - - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetCalleeExpr(N)); - C.EmitReport(R); -} - -void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, - const CallExpr *CE){ - - const Expr *Callee = CE->getCallee()->IgnoreParens(); - SVal L = C.getState()->getSVal(Callee); - - if (L.isUndef()) { - if (!BT_call_undef) - BT_call_undef = - new BuiltinBug("Called function pointer is an undefined pointer value"); - EmitBadCall(BT_call_undef, C, CE); - return; - } - - if (isa<loc::ConcreteInt>(L)) { - if (!BT_call_null) - BT_call_null = - new BuiltinBug("Called function pointer is null (null dereference)"); - EmitBadCall(BT_call_null, C, CE); - } - - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) { - if (C.getState()->getSVal(*I).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_call_arg) - BT_call_arg = new BuiltinBug("Pass-by-value argument in function call" - " is undefined"); - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT_call_arg, - BT_call_arg->getName(), N); - R->addRange((*I)->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); - C.EmitReport(R); - return; - } - } - } -} - -void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - - const GRState *state = C.getState(); - - if (const Expr *receiver = ME->getReceiver()) - if (state->getSVal(receiver).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_msg_undef) - BT_msg_undef = - new BuiltinBug("Receiver in message expression is a garbage value"); - EnhancedBugReport *R = - new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); - R->addRange(receiver->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - receiver); - C.EmitReport(R); - } - return; - } - - // Check for any arguments that are uninitialized/undefined. - for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), - E = ME->arg_end(); I != E; ++I) { - if (state->getSVal(*I).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_msg_arg) - BT_msg_arg = - new BuiltinBug("Pass-by-value argument in message expression" - " is undefined"); - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT_msg_arg, - BT_msg_arg->getName(), N); - R->addRange((*I)->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); - C.EmitReport(R); - return; - } - } - } -} - -bool CallAndMessageChecker::EvalNilReceiver(CheckerContext &C, - const ObjCMessageExpr *ME) { - HandleNilReceiver(C, C.getState(), ME); - return true; // Nil receiver is not handled elsewhere. -} - -void CallAndMessageChecker::EmitNilReceiverBug(CheckerContext &C, - const ObjCMessageExpr *ME, - ExplodedNode *N) { - - if (!BT_msg_ret) - BT_msg_ret = - new BuiltinBug("Receiver in message expression is " - "'nil' and returns a garbage value"); - - llvm::SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - os << "The receiver of message '" << ME->getSelector().getAsString() - << "' is nil and returns a value of type '" - << ME->getType().getAsString() << "' that will be garbage"; - - EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); - const Expr *receiver = ME->getReceiver(); - report->addRange(receiver->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - receiver); - C.EmitReport(report); -} - -static bool SupportsNilWithFloatRet(const llvm::Triple &triple) { - return triple.getVendor() == llvm::Triple::Apple && - triple.getDarwinMajorNumber() >= 9; -} - -void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, - const GRState *state, - const ObjCMessageExpr *ME) { - - // Check the return type of the message expression. A message to nil will - // return different values depending on the return type and the architecture. - QualType RetTy = ME->getType(); - - ASTContext &Ctx = C.getASTContext(); - CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); - - if (CanRetTy->isStructureType()) { - // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead - // have the "use of undefined value" be smarter about where the - // undefined value came from. - if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { - if (ExplodedNode* N = C.GenerateSink(state)) - EmitNilReceiverBug(C, ME, N); - return; - } - - // The result is not consumed by a surrounding expression. Just propagate - // the current state. - C.addTransition(state); - return; - } - - // Other cases: check if the return type is smaller than void*. - if (CanRetTy != Ctx.VoidTy && - C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { - // Compute: sizeof(void *) and sizeof(return type) - const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); - const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); - - if (voidPtrSize < returnTypeSize && - !(SupportsNilWithFloatRet(Ctx.Target.getTriple()) && - (Ctx.FloatTy == CanRetTy || - Ctx.DoubleTy == CanRetTy || - Ctx.LongDoubleTy == CanRetTy || - Ctx.LongLongTy == CanRetTy))) { - if (ExplodedNode* N = C.GenerateSink(state)) - EmitNilReceiverBug(C, ME, N); - return; - } - - // Handle the safe cases where the return value is 0 if the - // receiver is nil. - // - // FIXME: For now take the conservative approach that we only - // return null values if we *know* that the receiver is nil. - // This is because we can have surprises like: - // - // ... = [[NSScreens screens] objectAtIndex:0]; - // - // What can happen is that [... screens] could return nil, but - // it most likely isn't nil. We should assume the semantics - // of this case unless we have *a lot* more knowledge. - // - SVal V = C.getValueManager().makeZeroVal(ME->getType()); - C.GenerateNode(state->BindExpr(ME, V)); - return; - } - - C.addTransition(state); -} diff --git a/lib/Analysis/CallInliner.cpp b/lib/Analysis/CallInliner.cpp deleted file mode 100644 index d18bbcc..0000000 --- a/lib/Analysis/CallInliner.cpp +++ /dev/null @@ -1,113 +0,0 @@ -//===--- CallInliner.cpp - Transfer function that inlines callee ----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the callee inlining transfer function. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/LocalCheckers.h" - -using namespace clang; - -namespace { -class CallInliner : public Checker { -public: - static void *getTag() { - static int x; - return &x; - } - - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); - virtual void EvalEndPath(GREndPathNodeBuilder &B,void *tag,GRExprEngine &Eng); -}; -} - -void clang::RegisterCallInliner(GRExprEngine &Eng) { - Eng.registerCheck(new CallInliner()); -} - -bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - if (!FD->isThisDeclarationADefinition()) - return false; - - GRStmtNodeBuilder &Builder = C.getNodeBuilder(); - // Make a new LocationContext. - const StackFrameContext *LocCtx = C.getAnalysisManager().getStackFrame(FD, - C.getPredecessor()->getLocationContext(), CE, - Builder.getBlock(), Builder.getIndex()); - - CFGBlock const *Entry = &(LocCtx->getCFG()->getEntry()); - - assert (Entry->empty() && "Entry block must be empty."); - - assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); - - // Get the solitary successor. - CFGBlock const *SuccB = *(Entry->succ_begin()); - - // Construct an edge representing the starting location in the function. - BlockEdge Loc(Entry, SuccB, LocCtx); - - state = C.getStoreManager().EnterStackFrame(state, LocCtx); - // This is a hack. We really should not use the GRStmtNodeBuilder. - bool isNew; - GRExprEngine &Eng = C.getEngine(); - ExplodedNode *Pred = C.getPredecessor(); - - - ExplodedNode *SuccN = Eng.getGraph().getNode(Loc, state, &isNew); - SuccN->addPredecessor(Pred, Eng.getGraph()); - C.getNodeBuilder().Deferred.erase(Pred); - - if (isNew) - Builder.getWorkList()->Enqueue(SuccN); - - Builder.HasGeneratedNode = true; - - return true; -} - -void CallInliner::EvalEndPath(GREndPathNodeBuilder &B, void *tag, - GRExprEngine &Eng) { - const GRState *state = B.getState(); - ExplodedNode *Pred = B.getPredecessor(); - const StackFrameContext *LocCtx = - cast<StackFrameContext>(Pred->getLocationContext()); - - const Stmt *CE = LocCtx->getCallSite(); - - // Check if this is the top level stack frame. - if (!LocCtx->getParent()) - return; - - PostStmt NodeLoc(CE, LocCtx->getParent()); - - bool isNew; - ExplodedNode *Succ = Eng.getGraph().getNode(NodeLoc, state, &isNew); - Succ->addPredecessor(Pred, Eng.getGraph()); - - // When creating the new work list unit, increment the statement index to - // point to the statement after the CallExpr. - if (isNew) - B.getWorkList().Enqueue(Succ, - *const_cast<CFGBlock*>(LocCtx->getCallSiteBlock()), - LocCtx->getIndex() + 1); - - B.HasGeneratedNode = true; -} diff --git a/lib/Analysis/CastToStructChecker.cpp b/lib/Analysis/CastToStructChecker.cpp deleted file mode 100644 index 219c09f..0000000 --- a/lib/Analysis/CastToStructChecker.cpp +++ /dev/null @@ -1,77 +0,0 @@ -//=== CastToStructChecker.cpp - Fixed address usage checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines CastToStructChecker, a builtin checker that checks for -// cast from non-struct pointer to struct pointer. -// This check corresponds to CWE-588. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class CastToStructChecker - : public CheckerVisitor<CastToStructChecker> { - BuiltinBug *BT; -public: - CastToStructChecker() : BT(0) {} - static void *getTag(); - void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); -}; -} - -void *CastToStructChecker::getTag() { - static int x; - return &x; -} - -void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, - const CastExpr *CE) { - const Expr *E = CE->getSubExpr(); - ASTContext &Ctx = C.getASTContext(); - QualType OrigTy = Ctx.getCanonicalType(E->getType()); - QualType ToTy = Ctx.getCanonicalType(CE->getType()); - - PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); - PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); - - if (!ToPTy || !OrigPTy) - return; - - QualType OrigPointeeTy = OrigPTy->getPointeeType(); - QualType ToPointeeTy = ToPTy->getPointeeType(); - - if (!ToPointeeTy->isStructureType()) - return; - - // We allow cast from void*. - if (OrigPointeeTy->isVoidType()) - return; - - // Now the cast-to-type is struct pointer, the original type is not void*. - if (!OrigPointeeTy->isRecordType()) { - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Cast from non-struct type to struct type", - "Casting a non-structure type to a structure type " - "and accessing a field can lead to memory access " - "errors or data corruption."); - RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); - R->addRange(CE->getSourceRange()); - C.EmitReport(R); - } - } -} - -void clang::RegisterCastToStructChecker(GRExprEngine &Eng) { - Eng.registerCheck(new CastToStructChecker()); -} diff --git a/lib/Analysis/CheckDeadStores.cpp b/lib/Analysis/CheckDeadStores.cpp deleted file mode 100644 index 6e4d899..0000000 --- a/lib/Analysis/CheckDeadStores.cpp +++ /dev/null @@ -1,279 +0,0 @@ -//==- DeadStores.cpp - Check for stores to dead variables --------*- 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 DeadStores, a flow-sensitive checker that looks for -// stores to variables that are no longer live. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/ParentMap.h" -#include "llvm/ADT/SmallPtrSet.h" - -using namespace clang; - -namespace { - -class DeadStoreObs : public LiveVariables::ObserverTy { - ASTContext &Ctx; - BugReporter& BR; - ParentMap& Parents; - llvm::SmallPtrSet<VarDecl*, 20> Escaped; - - enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; - -public: - DeadStoreObs(ASTContext &ctx, BugReporter& br, ParentMap& parents, - llvm::SmallPtrSet<VarDecl*, 20> &escaped) - : Ctx(ctx), BR(br), Parents(parents), Escaped(escaped) {} - - virtual ~DeadStoreObs() {} - - void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) { - if (Escaped.count(V)) - return; - - std::string name = V->getNameAsString(); - - const char* BugType = 0; - std::string msg; - - switch (dsk) { - default: - assert(false && "Impossible dead store type."); - - case DeadInit: - BugType = "Dead initialization"; - msg = "Value stored to '" + name + - "' during its initialization is never read"; - break; - - case DeadIncrement: - BugType = "Dead increment"; - case Standard: - if (!BugType) BugType = "Dead assignment"; - msg = "Value stored to '" + name + "' is never read"; - break; - - case Enclosing: - BugType = "Dead nested assignment"; - msg = "Although the value stored to '" + name + - "' is used in the enclosing expression, the value is never actually" - " read from '" + name + "'"; - break; - } - - BR.EmitBasicReport(BugType, "Dead store", msg, L, R); - } - - void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, - DeadStoreKind dsk, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - - if (!VD->hasLocalStorage()) - return; - // Reference types confuse the dead stores checker. Skip them - // for now. - if (VD->getType()->getAs<ReferenceType>()) - return; - - if (!Live(VD, AD) && - !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) - Report(VD, dsk, Ex->getSourceRange().getBegin(), - Val->getSourceRange()); - } - - void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) - CheckVarDecl(VD, DR, Val, dsk, AD, Live); - } - - bool isIncrement(VarDecl* VD, BinaryOperator* B) { - if (B->isCompoundAssignmentOp()) - return true; - - Expr* RHS = B->getRHS()->IgnoreParenCasts(); - BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS); - - if (!BRHS) - return false; - - DeclRefExpr *DR; - - if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts()))) - if (DR->getDecl() == VD) - return true; - - if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts()))) - if (DR->getDecl() == VD) - return true; - - return false; - } - - virtual void ObserveStmt(Stmt* S, - const LiveVariables::AnalysisDataTy& AD, - const LiveVariables::ValTy& Live) { - - // Skip statements in macros. - if (S->getLocStart().isMacroID()) - return; - - if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { - if (!B->isAssignmentOp()) return; // Skip non-assignments. - - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS())) - if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - // Special case: check for assigning null to a pointer. - // This is a common form of defensive programming. - if (VD->getType()->isPointerType()) { - if (B->getRHS()->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) - return; - } - - Expr* RHS = B->getRHS()->IgnoreParenCasts(); - // Special case: self-assignments. These are often used to shut up - // "unused variable" compiler warnings. - if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS)) - if (VD == dyn_cast<VarDecl>(RhsDR->getDecl())) - return; - - // Otherwise, issue a warning. - DeadStoreKind dsk = Parents.isConsumedExpr(B) - ? Enclosing - : (isIncrement(VD,B) ? DeadIncrement : Standard); - - CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live); - } - } - else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) { - if (!U->isIncrementOp()) - return; - - // Handle: ++x within a subexpression. The solution is not warn - // about preincrements to dead variables when the preincrement occurs - // as a subexpression. This can lead to false negatives, e.g. "(++x);" - // A generalized dead code checker should find such issues. - if (U->isPrefix() && Parents.isConsumedExpr(U)) - return; - - Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); - - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex)) - CheckDeclRef(DR, U, DeadIncrement, AD, Live); - } - else if (DeclStmt* DS = dyn_cast<DeclStmt>(S)) - // Iterate through the decls. Warn if any initializers are complex - // expressions that are not live (never used). - for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); - DI != DE; ++DI) { - - VarDecl* V = dyn_cast<VarDecl>(*DI); - - if (!V) - continue; - - if (V->hasLocalStorage()) { - // Reference types confuse the dead stores checker. Skip them - // for now. - if (V->getType()->getAs<ReferenceType>()) - return; - - if (Expr* E = V->getInit()) { - // Don't warn on C++ objects (yet) until we can show that their - // constructors/destructors don't have side effects. - if (isa<CXXConstructExpr>(E)) - return; - - if (isa<CXXExprWithTemporaries>(E)) - return; - - // A dead initialization is a variable that is dead after it - // is initialized. We don't flag warnings for those variables - // marked 'unused'. - if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) { - // Special case: check for initializations with constants. - // - // e.g. : int x = 0; - // - // If x is EVER assigned a new value later, don't issue - // a warning. This is because such initialization can be - // due to defensive programming. - if (E->isConstantInitializer(Ctx)) - return; - - // Special case: check for initializations from constant - // variables. - // - // e.g. extern const int MyConstant; - // int x = MyConstant; - // - if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) - if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) - if (VD->hasGlobalStorage() && - VD->getType().isConstQualified()) return; - - Report(V, DeadInit, V->getLocation(), E->getSourceRange()); - } - } - } - } - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Driver function to invoke the Dead-Stores checker on a CFG. -//===----------------------------------------------------------------------===// - -namespace { -class FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{ - CFG *cfg; -public: - FindEscaped(CFG *c) : cfg(c) {} - - CFG& getCFG() { return *cfg; } - - llvm::SmallPtrSet<VarDecl*, 20> Escaped; - - void VisitUnaryOperator(UnaryOperator* U) { - // Check for '&'. Any VarDecl whose value has its address-taken we - // treat as escaped. - Expr* E = U->getSubExpr()->IgnoreParenCasts(); - if (U->getOpcode() == UnaryOperator::AddrOf) - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E)) - if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) { - Escaped.insert(VD); - return; - } - Visit(E); - } -}; -} // end anonymous namespace - - -void clang::CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &pmap, - BugReporter& BR) { - FindEscaped FS(&cfg); - FS.getCFG().VisitBlockStmts(FS); - DeadStoreObs A(BR.getContext(), BR, pmap, FS.Escaped); - L.runOnAllBlocks(cfg, &A); -} diff --git a/lib/Analysis/CheckObjCDealloc.cpp b/lib/Analysis/CheckObjCDealloc.cpp deleted file mode 100644 index 87c1f27..0000000 --- a/lib/Analysis/CheckObjCDealloc.cpp +++ /dev/null @@ -1,258 +0,0 @@ -//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 CheckObjCDealloc, a checker that -// analyzes an Objective-C class's implementation to determine if it -// correctly implements -dealloc. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Basic/LangOptions.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -static bool scan_dealloc(Stmt* S, Selector Dealloc) { - - if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) - if (ME->getSelector() == Dealloc) - if (ME->getReceiver()) - if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) - return isa<ObjCSuperExpr>(Receiver); - - // Recurse to children. - - for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) - if (*I && scan_dealloc(*I, Dealloc)) - return true; - - return false; -} - -static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, - const ObjCPropertyDecl* PD, - Selector Release, - IdentifierInfo* SelfII, - ASTContext& Ctx) { - - // [mMyIvar release] - if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) - if (ME->getSelector() == Release) - if (ME->getReceiver()) - if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) - if (ObjCIvarRefExpr* E = dyn_cast<ObjCIvarRefExpr>(Receiver)) - if (E->getDecl() == ID) - return true; - - // [self setMyIvar:nil]; - if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S)) - if (ME->getReceiver()) - if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts()) - if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(Receiver)) - if (E->getDecl()->getIdentifier() == SelfII) - if (ME->getMethodDecl() == PD->getSetterMethodDecl() && - ME->getNumArgs() == 1 && - ME->getArg(0)->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) - return true; - - // self.myIvar = nil; - if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S)) - if (BO->isAssignmentOp()) - if (ObjCPropertyRefExpr* PRE = - dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts())) - if (PRE->getProperty() == PD) - if (BO->getRHS()->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) { - // This is only a 'release' if the property kind is not - // 'assign'. - return PD->getSetterKind() != ObjCPropertyDecl::Assign;; - } - - // Recurse to children. - for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) - if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx)) - return true; - - return false; -} - -void clang::CheckObjCDealloc(const ObjCImplementationDecl* D, - const LangOptions& LOpts, BugReporter& BR) { - - assert (LOpts.getGCMode() != LangOptions::GCOnly); - - ASTContext& Ctx = BR.getContext(); - const ObjCInterfaceDecl* ID = D->getClassInterface(); - - // Does the class contain any ivars that are pointers (or id<...>)? - // If not, skip the check entirely. - // NOTE: This is motivated by PR 2517: - // http://llvm.org/bugs/show_bug.cgi?id=2517 - - bool containsPointerIvar = false; - - for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); - I!=E; ++I) { - - ObjCIvarDecl* ID = *I; - QualType T = ID->getType(); - - if (!T->isObjCObjectPointerType() || - ID->getAttr<IBOutletAttr>()) // Skip IBOutlets. - continue; - - containsPointerIvar = true; - break; - } - - if (!containsPointerIvar) - return; - - // Determine if the class subclasses NSObject. - IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - - - for ( ; ID ; ID = ID->getSuperClass()) { - IdentifierInfo *II = ID->getIdentifier(); - - if (II == NSObjectII) - break; - - // FIXME: For now, ignore classes that subclass SenTestCase, as these don't - // need to implement -dealloc. They implement tear down in another way, - // which we should try and catch later. - // http://llvm.org/bugs/show_bug.cgi?id=3187 - if (II == SenTestCaseII) - return; - } - - if (!ID) - return; - - // Get the "dealloc" selector. - IdentifierInfo* II = &Ctx.Idents.get("dealloc"); - Selector S = Ctx.Selectors.getSelector(0, &II); - ObjCMethodDecl* MD = 0; - - // Scan the instance methods for "dealloc". - for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), - E = D->instmeth_end(); I!=E; ++I) { - - if ((*I)->getSelector() == S) { - MD = *I; - break; - } - } - - if (!MD) { // No dealloc found. - - const char* name = LOpts.getGCMode() == LangOptions::NonGC - ? "missing -dealloc" - : "missing -dealloc (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "Objective-C class '" << D->getNameAsString() - << "' lacks a 'dealloc' instance method"; - - BR.EmitBasicReport(name, os.str(), D->getLocStart()); - return; - } - - // dealloc found. Scan for missing [super dealloc]. - if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { - - const char* name = LOpts.getGCMode() == LangOptions::NonGC - ? "missing [super dealloc]" - : "missing [super dealloc] (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "The 'dealloc' instance method in Objective-C class '" - << D->getNameAsString() - << "' does not send a 'dealloc' message to its super class" - " (missing [super dealloc])"; - - BR.EmitBasicReport(name, os.str(), D->getLocStart()); - return; - } - - // Get the "release" selector. - IdentifierInfo* RII = &Ctx.Idents.get("release"); - Selector RS = Ctx.Selectors.getSelector(0, &RII); - - // Get the "self" identifier - IdentifierInfo* SelfII = &Ctx.Idents.get("self"); - - // Scan for missing and extra releases of ivars used by implementations - // of synthesized properties - for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), - E = D->propimpl_end(); I!=E; ++I) { - - // We can only check the synthesized properties - if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) - continue; - - ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl(); - if (!ID) - continue; - - QualType T = ID->getType(); - if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars - continue; - - const ObjCPropertyDecl* PD = (*I)->getPropertyDecl(); - if (!PD) - continue; - - // ivars cannot be set via read-only properties, so we'll skip them - if (PD->isReadOnly()) - continue; - - // ivar must be released if and only if the kind of setter was not 'assign' - bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; - if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) - != requiresRelease) { - const char *name; - const char* category = "Memory (Core Foundation/Objective-C)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - - if (requiresRelease) { - name = LOpts.getGCMode() == LangOptions::NonGC - ? "missing ivar release (leak)" - : "missing ivar release (Hybrid MM, non-GC)"; - - os << "The '" << ID->getNameAsString() - << "' instance variable was retained by a synthesized property but " - "wasn't released in 'dealloc'"; - } else { - name = LOpts.getGCMode() == LangOptions::NonGC - ? "extra ivar release (use-after-release)" - : "extra ivar release (Hybrid MM, non-GC)"; - - os << "The '" << ID->getNameAsString() - << "' instance variable was not retained by a synthesized property " - "but was released in 'dealloc'"; - } - - BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); - } - } -} - diff --git a/lib/Analysis/CheckObjCInstMethSignature.cpp b/lib/Analysis/CheckObjCInstMethSignature.cpp deleted file mode 100644 index 10ba896..0000000 --- a/lib/Analysis/CheckObjCInstMethSignature.cpp +++ /dev/null @@ -1,119 +0,0 @@ -//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- 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 CheckObjCInstMethSignature, a flow-insenstive check -// that determines if an Objective-C class interface incorrectly redefines -// the method signature in a subclass. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Type.h" -#include "clang/AST/ASTContext.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -static bool AreTypesCompatible(QualType Derived, QualType Ancestor, - ASTContext& C) { - - // Right now don't compare the compatibility of pointers. That involves - // looking at subtyping relationships. FIXME: Future patch. - if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) - return true; - - return C.typesAreCompatible(Derived, Ancestor); -} - -static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, - const ObjCMethodDecl *MethAncestor, - BugReporter &BR, ASTContext &Ctx, - const ObjCImplementationDecl *ID) { - - QualType ResDerived = MethDerived->getResultType(); - QualType ResAncestor = MethAncestor->getResultType(); - - if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "The Objective-C class '" - << MethDerived->getClassInterface()->getNameAsString() - << "', which is derived from class '" - << MethAncestor->getClassInterface()->getNameAsString() - << "', defines the instance method '" - << MethDerived->getSelector().getAsString() - << "' whose return type is '" - << ResDerived.getAsString() - << "'. A method with the same name (same selector) is also defined in " - "class '" - << MethAncestor->getClassInterface()->getNameAsString() - << "' and has a return type of '" - << ResAncestor.getAsString() - << "'. These two types are incompatible, and may result in undefined " - "behavior for clients of these classes."; - - BR.EmitBasicReport("Incompatible instance method return type", - os.str(), MethDerived->getLocStart()); - } -} - -void clang::CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, - BugReporter& BR) { - - const ObjCInterfaceDecl* D = ID->getClassInterface(); - const ObjCInterfaceDecl* C = D->getSuperClass(); - - if (!C) - return; - - ASTContext& Ctx = BR.getContext(); - - // Build a DenseMap of the methods for quick querying. - typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; - MapTy IMeths; - unsigned NumMethods = 0; - - for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), - E=ID->instmeth_end(); I!=E; ++I) { - - ObjCMethodDecl* M = *I; - IMeths[M->getSelector()] = M; - ++NumMethods; - } - - // Now recurse the class hierarchy chain looking for methods with the - // same signatures. - while (C && NumMethods) { - for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), - E=C->instmeth_end(); I!=E; ++I) { - - ObjCMethodDecl* M = *I; - Selector S = M->getSelector(); - - MapTy::iterator MI = IMeths.find(S); - - if (MI == IMeths.end() || MI->second == 0) - continue; - - --NumMethods; - ObjCMethodDecl* MethDerived = MI->second; - MI->second = 0; - - CompareReturnTypes(MethDerived, M, BR, Ctx, ID); - } - - C = C->getSuperClass(); - } -} diff --git a/lib/Analysis/CheckObjCUnusedIVars.cpp b/lib/Analysis/CheckObjCUnusedIVars.cpp deleted file mode 100644 index d4067c9..0000000 --- a/lib/Analysis/CheckObjCUnusedIVars.cpp +++ /dev/null @@ -1,162 +0,0 @@ -//==- CheckObjCUnusedIVars.cpp - Check for unused ivars ----------*- 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 CheckObjCUnusedIvars, a checker that -// analyzes an Objective-C class's interface/implementation to determine if it -// has any ivars that are never accessed. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/DeclObjC.h" -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/SourceManager.h" - -using namespace clang; - -enum IVarState { Unused, Used }; -typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; - -static void Scan(IvarUsageMap& M, const Stmt* S) { - if (!S) - return; - - if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { - const ObjCIvarDecl *D = Ex->getDecl(); - IvarUsageMap::iterator I = M.find(D); - if (I != M.end()) - I->second = Used; - return; - } - - // Blocks can reference an instance variable of a class. - if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { - Scan(M, BE->getBody()); - return; - } - - for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) - Scan(M, *I); -} - -static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { - if (!D) - return; - - const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); - - if (!ID) - return; - - IvarUsageMap::iterator I = M.find(ID); - if (I != M.end()) - I->second = Used; -} - -static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { - // Scan the methods for accesses. - for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), - E = D->instmeth_end(); I!=E; ++I) - Scan(M, (*I)->getBody()); - - if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { - // Scan for @synthesized property methods that act as setters/getters - // to an ivar. - for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), - E = ID->propimpl_end(); I!=E; ++I) - Scan(M, *I); - - // Scan the associated categories as well. - for (const ObjCCategoryDecl *CD = - ID->getClassInterface()->getCategoryList(); CD ; - CD = CD->getNextClassCategory()) { - if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) - Scan(M, CID); - } - } -} - -static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, - SourceManager &SM) { - for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); - I!=E; ++I) - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { - SourceLocation L = FD->getLocStart(); - if (SM.getFileID(L) == FID) - Scan(M, FD->getBody()); - } -} - -void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, - BugReporter &BR) { - - const ObjCInterfaceDecl* ID = D->getClassInterface(); - IvarUsageMap M; - - // Iterate over the ivars. - for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), - E=ID->ivar_end(); I!=E; ++I) { - - const ObjCIvarDecl* ID = *I; - - // Ignore ivars that aren't private. - if (ID->getAccessControl() != ObjCIvarDecl::Private) - continue; - - // Skip IB Outlets. - if (ID->getAttr<IBOutletAttr>()) - continue; - - M[ID] = Unused; - } - - if (M.empty()) - return; - - // Now scan the implementation declaration. - Scan(M, D); - - - // Any potentially unused ivars? - bool hasUnused = false; - for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) - if (I->second == Unused) { - hasUnused = true; - break; - } - - if (!hasUnused) - return; - - // We found some potentially unused ivars. Scan the entire translation unit - // for functions inside the @implementation that reference these ivars. - // FIXME: In the future hopefully we can just use the lexical DeclContext - // to go from the ObjCImplementationDecl to the lexically "nested" - // C functions. - SourceManager &SM = BR.getSourceManager(); - Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); - - // Find ivars that are unused. - for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) - if (I->second == Unused) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Instance variable '" << I->first->getNameAsString() - << "' in class '" << ID->getNameAsString() - << "' is never used by the methods in its @implementation " - "(although it may be used by category methods)."; - - BR.EmitBasicReport("Unused instance variable", "Optimization", - os.str(), I->first->getLocation()); - } -} diff --git a/lib/Analysis/CheckSecuritySyntaxOnly.cpp b/lib/Analysis/CheckSecuritySyntaxOnly.cpp deleted file mode 100644 index f4874a5..0000000 --- a/lib/Analysis/CheckSecuritySyntaxOnly.cpp +++ /dev/null @@ -1,502 +0,0 @@ -//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a set of flow-insensitive security checks. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/LocalCheckers.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -static bool isArc4RandomAvailable(const ASTContext &Ctx) { - const llvm::Triple &T = Ctx.Target.getTriple(); - return T.getVendor() == llvm::Triple::Apple || - T.getOS() == llvm::Triple::FreeBSD; -} - -namespace { -class WalkAST : public StmtVisitor<WalkAST> { - BugReporter &BR; - IdentifierInfo *II_gets; - IdentifierInfo *II_getpw; - IdentifierInfo *II_mktemp; - enum { num_rands = 9 }; - IdentifierInfo *II_rand[num_rands]; - IdentifierInfo *II_random; - enum { num_setids = 6 }; - IdentifierInfo *II_setid[num_setids]; - - const bool CheckRand; - -public: - WalkAST(BugReporter &br) : BR(br), - II_gets(0), II_getpw(0), II_mktemp(0), - II_rand(), II_random(0), II_setid(), - CheckRand(isArc4RandomAvailable(BR.getContext())) {} - - // Statement visitor methods. - void VisitCallExpr(CallExpr *CE); - void VisitForStmt(ForStmt *S); - void VisitCompoundStmt (CompoundStmt *S); - void VisitStmt(Stmt *S) { VisitChildren(S); } - - void VisitChildren(Stmt *S); - - // Helpers. - IdentifierInfo *GetIdentifier(IdentifierInfo *& II, const char *str); - - // Checker-specific methods. - void CheckLoopConditionForFloat(const ForStmt *FS); - void CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD); - void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); - void CheckUncheckedReturnValue(CallExpr *CE); -}; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Helper methods. -//===----------------------------------------------------------------------===// - -IdentifierInfo *WalkAST::GetIdentifier(IdentifierInfo *& II, const char *str) { - if (!II) - II = &BR.getContext().Idents.get(str); - - return II; -} - -//===----------------------------------------------------------------------===// -// AST walking. -//===----------------------------------------------------------------------===// - -void WalkAST::VisitChildren(Stmt *S) { - for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (Stmt *child = *I) - Visit(child); -} - -void WalkAST::VisitCallExpr(CallExpr *CE) { - if (const FunctionDecl *FD = CE->getDirectCallee()) { - CheckCall_gets(CE, FD); - CheckCall_getpw(CE, FD); - CheckCall_mktemp(CE, FD); - if (CheckRand) { - CheckCall_rand(CE, FD); - CheckCall_random(CE, FD); - } - } - - // Recurse and check children. - VisitChildren(CE); -} - -void WalkAST::VisitCompoundStmt(CompoundStmt *S) { - for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (Stmt *child = *I) { - if (CallExpr *CE = dyn_cast<CallExpr>(child)) - CheckUncheckedReturnValue(CE); - Visit(child); - } -} - -void WalkAST::VisitForStmt(ForStmt *FS) { - CheckLoopConditionForFloat(FS); - - // Recurse and check children. - VisitChildren(FS); -} - -//===----------------------------------------------------------------------===// -// Check: floating poing variable used as loop counter. -// Originally: <rdar://problem/6336718> -// Implements: CERT security coding advisory FLP-30. -//===----------------------------------------------------------------------===// - -static const DeclRefExpr* -GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { - expr = expr->IgnoreParenCasts(); - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { - if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || - B->getOpcode() == BinaryOperator::Comma)) - return NULL; - - if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) - return lhs; - - if (const DeclRefExpr *rhs = GetIncrementedVar(B->getRHS(), x, y)) - return rhs; - - return NULL; - } - - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { - const NamedDecl *ND = DR->getDecl(); - return ND == x || ND == y ? DR : NULL; - } - - if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) - return U->isIncrementDecrementOp() - ? GetIncrementedVar(U->getSubExpr(), x, y) : NULL; - - return NULL; -} - -/// CheckLoopConditionForFloat - This check looks for 'for' statements that -/// use a floating point variable as a loop counter. -/// CERT: FLP30-C, FLP30-CPP. -/// -void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { - // Does the loop have a condition? - const Expr *condition = FS->getCond(); - - if (!condition) - return; - - // Does the loop have an increment? - const Expr *increment = FS->getInc(); - - if (!increment) - return; - - // Strip away '()' and casts. - condition = condition->IgnoreParenCasts(); - increment = increment->IgnoreParenCasts(); - - // Is the loop condition a comparison? - const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); - - if (!B) - return; - - // Is this a comparison? - if (!(B->isRelationalOp() || B->isEqualityOp())) - return; - - // Are we comparing variables? - const DeclRefExpr *drLHS = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParens()); - const DeclRefExpr *drRHS = dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParens()); - - // Does at least one of the variables have a floating point type? - drLHS = drLHS && drLHS->getType()->isFloatingType() ? drLHS : NULL; - drRHS = drRHS && drRHS->getType()->isFloatingType() ? drRHS : NULL; - - if (!drLHS && !drRHS) - return; - - const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : NULL; - const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : NULL; - - if (!vdLHS && !vdRHS) - return; - - // Does either variable appear in increment? - const DeclRefExpr *drInc = GetIncrementedVar(increment, vdLHS, vdRHS); - - if (!drInc) - return; - - // Emit the error. First figure out which DeclRefExpr in the condition - // referenced the compared variable. - const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; - - llvm::SmallVector<SourceRange, 2> ranges; - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Variable '" << drCond->getDecl()->getNameAsCString() - << "' with floating point type '" << drCond->getType().getAsString() - << "' should not be used as a loop counter"; - - ranges.push_back(drCond->getSourceRange()); - ranges.push_back(drInc->getSourceRange()); - - const char *bugType = "Floating point variable used as loop counter"; - BR.EmitBasicReport(bugType, "Security", os.str(), - FS->getLocStart(), ranges.data(), ranges.size()); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'gets' is insecure. -// Originally: <rdar://problem/6335715> -// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) -// CWE-242: Use of Inherently Dangerous Function -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_gets(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) - return; - - const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType()); - if (!FPT) - return; - - // Verify that the function takes a single argument. - if (FPT->getNumArgs() != 1) - return; - - // Is the argument a 'char*'? - const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", - "Security", - "Call to function 'gets' is extremely insecure as it can " - "always result in a buffer overflow", - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'getpwd' is insecure. -// CWE-477: Use of Obsolete Functions -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) - return; - - const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType()); - if (!FPT) - return; - - // Verify that the function takes two arguments. - if (FPT->getNumArgs() != 2) - return; - - // Verify the first argument type is integer. - if (!FPT->getArgType(0)->isIntegerType()) - return; - - // Verify the second argument type is char*. - const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(1)); - if (!PT) - return; - - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a warning. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", - "Security", - "The getpw() function is dangerous as it may overflow the " - "provided buffer. It is obsoleted by getpwuid().", - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: Any use of 'mktemp' is insecure.It is obsoleted by mkstemp(). -// CWE-377: Insecure Temporary File -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) - return; - - const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FD->getType()); - if(!FPT) - return; - - // Verify that the funcion takes a single argument. - if (FPT->getNumArgs() != 1) - return; - - // Verify that the argument is Pointer Type. - const PointerType *PT = dyn_cast<PointerType>(FPT->getArgType(0)); - if (!PT) - return; - - // Verify that the argument is a 'char*'. - if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) - return; - - // Issue a waring. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", - "Security", - "Call to function 'mktemp' is insecure as it always " - "creates or uses insecure temporary file", - CE->getLocStart(), &R, 1); -} - - -//===----------------------------------------------------------------------===// -// Check: Linear congruent random number generators should not be used -// Originally: <rdar://problem/63371000> -// CWE-338: Use of cryptographically weak prng -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { - if (II_rand[0] == NULL) { - // This check applies to these functions - static const char * const identifiers[num_rands] = { - "drand48", "erand48", "jrand48", "lrand48", "mrand48", "nrand48", - "lcong48", - "rand", "rand_r" - }; - - for (size_t i = 0; i < num_rands; i++) - II_rand[i] = &BR.getContext().Idents.get(identifiers[i]); - } - - const IdentifierInfo *id = FD->getIdentifier(); - size_t identifierid; - - for (identifierid = 0; identifierid < num_rands; identifierid++) - if (id == II_rand[identifierid]) - break; - - if (identifierid >= num_rands) - return; - - const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FD->getType()); - if (!FTP) - return; - - if (FTP->getNumArgs() == 1) { - // Is the argument an 'unsigned short *'? - // (Actually any integer type is allowed.) - const PointerType *PT = dyn_cast<PointerType>(FTP->getArgType(0)); - if (!PT) - return; - - if (! PT->getPointeeType()->isIntegerType()) - return; - } - else if (FTP->getNumArgs() != 0) - return; - - // Issue a warning. - std::string buf1; - llvm::raw_string_ostream os1(buf1); - os1 << "'" << FD->getNameAsString() << "' is a poor random number generator"; - - std::string buf2; - llvm::raw_string_ostream os2(buf2); - os2 << "Function '" << FD->getNameAsString() - << "' is obsolete because it implements a poor random number generator." - << " Use 'arc4random' instead"; - - SourceRange R = CE->getCallee()->getSourceRange(); - - BR.EmitBasicReport(os1.str(), "Security", os2.str(), - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: 'random' should not be used -// Originally: <rdar://problem/63371000> -//===----------------------------------------------------------------------===// - -void WalkAST::CheckCall_random(const CallExpr *CE, const FunctionDecl *FD) { - if (FD->getIdentifier() != GetIdentifier(II_random, "random")) - return; - - const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FD->getType()); - if (!FTP) - return; - - // Verify that the function takes no argument. - if (FTP->getNumArgs() != 0) - return; - - // Issue a warning. - SourceRange R = CE->getCallee()->getSourceRange(); - BR.EmitBasicReport("'random' is not a secure random number generator", - "Security", - "The 'random' function produces a sequence of values that " - "an adversary may be able to predict. Use 'arc4random' " - "instead", - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Check: Should check whether privileges are dropped successfully. -// Originally: <rdar://problem/6337132> -//===----------------------------------------------------------------------===// - -void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { - const FunctionDecl *FD = CE->getDirectCallee(); - if (!FD) - return; - - if (II_setid[0] == NULL) { - static const char * const identifiers[num_setids] = { - "setuid", "setgid", "seteuid", "setegid", - "setreuid", "setregid" - }; - - for (size_t i = 0; i < num_setids; i++) - II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); - } - - const IdentifierInfo *id = FD->getIdentifier(); - size_t identifierid; - - for (identifierid = 0; identifierid < num_setids; identifierid++) - if (id == II_setid[identifierid]) - break; - - if (identifierid >= num_setids) - return; - - const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FD->getType()); - if (!FTP) - return; - - // Verify that the function takes one or two arguments (depending on - // the function). - if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) - return; - - // The arguments must be integers. - for (unsigned i = 0; i < FTP->getNumArgs(); i++) - if (! FTP->getArgType(i)->isIntegerType()) - return; - - // Issue a warning. - std::string buf1; - llvm::raw_string_ostream os1(buf1); - os1 << "Return value is not checked in call to '" << FD->getNameAsString() - << "'"; - - std::string buf2; - llvm::raw_string_ostream os2(buf2); - os2 << "The return value from the call to '" << FD->getNameAsString() - << "' is not checked. If an error occurs in '" - << FD->getNameAsString() - << "', the following code may execute with unexpected privileges"; - - SourceRange R = CE->getCallee()->getSourceRange(); - - BR.EmitBasicReport(os1.str(), "Security", os2.str(), - CE->getLocStart(), &R, 1); -} - -//===----------------------------------------------------------------------===// -// Entry point for check. -//===----------------------------------------------------------------------===// - -void clang::CheckSecuritySyntaxOnly(const Decl *D, BugReporter &BR) { - WalkAST walker(BR); - walker.Visit(D->getBody()); -} diff --git a/lib/Analysis/CheckSizeofPointer.cpp b/lib/Analysis/CheckSizeofPointer.cpp deleted file mode 100644 index 4f5da9f..0000000 --- a/lib/Analysis/CheckSizeofPointer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- 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 check for unintended use of sizeof() on pointer -// expressions. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/Analysis/LocalCheckers.h" - -using namespace clang; - -namespace { -class WalkAST : public StmtVisitor<WalkAST> { - BugReporter &BR; - -public: - WalkAST(BugReporter &br) : BR(br) {} - void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); - void VisitStmt(Stmt *S) { VisitChildren(S); } - void VisitChildren(Stmt *S); -}; -} - -void WalkAST::VisitChildren(Stmt *S) { - for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) - if (Stmt *child = *I) - Visit(child); -} - -// CWE-467: Use of sizeof() on a Pointer Type -void WalkAST::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { - if (!E->isSizeOf()) - return; - - // If an explicit type is used in the code, usually the coder knows what he is - // doing. - if (E->isArgumentType()) - return; - - QualType T = E->getTypeOfArgument(); - if (T->isPointerType()) { - - // Many false positives have the form 'sizeof *p'. This is reasonable - // because people know what they are doing when they intentionally - // dereference the pointer. - Expr *ArgEx = E->getArgumentExpr(); - if (!isa<DeclRefExpr>(ArgEx->IgnoreParens())) - return; - - SourceRange R = ArgEx->getSourceRange(); - BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", - "Logic", - "The code calls sizeof() on a pointer type. " - "This can produce an unexpected result.", - E->getLocStart(), &R, 1); - } -} - -void clang::CheckSizeofPointer(const Decl *D, BugReporter &BR) { - WalkAST walker(BR); - walker.Visit(D->getBody()); -} diff --git a/lib/Analysis/Checker.cpp b/lib/Analysis/Checker.cpp deleted file mode 100644 index fb9d04d..0000000 --- a/lib/Analysis/Checker.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//== Checker.h - Abstract interface for checkers -----------------*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines Checker and CheckerVisitor, classes used for creating -// domain-specific checks. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/Checker.h" -using namespace clang; - -Checker::~Checker() {} - -CheckerContext::~CheckerContext() { - // Do we need to autotransition? 'Dst' can get populated in a variety of - // ways, including 'addTransition()' adding the predecessor node to Dst - // without actually generated a new node. We also shouldn't autotransition - // if we are building sinks or we generated a node and decided to not - // add it as a transition. - if (Dst.size() == size && !B.BuildSinks && !B.HasGeneratedNode) { - if (ST && ST != B.GetState(Pred)) { - static int autoTransitionTag = 0; - B.Tag = &autoTransitionTag; - addTransition(ST); - } - else - Dst.Add(Pred); - } -} diff --git a/lib/Analysis/DereferenceChecker.cpp b/lib/Analysis/DereferenceChecker.cpp deleted file mode 100644 index 9824387..0000000 --- a/lib/Analysis/DereferenceChecker.cpp +++ /dev/null @@ -1,135 +0,0 @@ -//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines NullDerefChecker, a builtin check in GRExprEngine that performs -// checks for null pointers at loads and stores. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class DereferenceChecker : public Checker { - BuiltinBug *BT_null; - BuiltinBug *BT_undef; - llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes; -public: - DereferenceChecker() : BT_null(0), BT_undef(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void VisitLocation(CheckerContext &C, const Stmt *S, SVal location); - - std::pair<ExplodedNode * const*, ExplodedNode * const*> - getImplicitNodes() const { - return std::make_pair(ImplicitNullDerefNodes.data(), - ImplicitNullDerefNodes.data() + - ImplicitNullDerefNodes.size()); - } -}; -} // end anonymous namespace - -void clang::RegisterDereferenceChecker(GRExprEngine &Eng) { - Eng.registerCheck(new DereferenceChecker()); -} - -std::pair<ExplodedNode * const *, ExplodedNode * const *> -clang::GetImplicitNullDereferences(GRExprEngine &Eng) { - DereferenceChecker *checker = Eng.getChecker<DereferenceChecker>(); - if (!checker) - return std::make_pair((ExplodedNode * const *) 0, - (ExplodedNode * const *) 0); - return checker->getImplicitNodes(); -} - -void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, - SVal l) { - // Check for dereference of an undefined value. - if (l.isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT_undef) - BT_undef = new BuiltinBug("Dereference of undefined pointer value"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - C.EmitReport(report); - } - return; - } - - DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); - - // Check for null dereferences. - if (!isa<Loc>(location)) - return; - - const GRState *state = C.getState(); - const GRState *notNullState, *nullState; - llvm::tie(notNullState, nullState) = state->Assume(location); - - // The explicit NULL case. - if (nullState) { - if (!notNullState) { - // Generate an error node. - ExplodedNode *N = C.GenerateSink(nullState); - if (!N) - return; - - // We know that 'location' cannot be non-null. This is what - // we call an "explicit" null dereference. - if (!BT_null) - BT_null = new BuiltinBug("Dereference of null pointer"); - - llvm::SmallString<100> buf; - - switch (S->getStmtClass()) { - case Stmt::UnaryOperatorClass: { - const UnaryOperator *U = cast<UnaryOperator>(S); - const Expr *SU = U->getSubExpr()->IgnoreParens(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SU)) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - llvm::raw_svector_ostream os(buf); - os << "Dereference of null pointer loaded from variable '" - << VD->getName() << '\''; - } - } - } - default: - break; - } - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_null, - buf.empty() ? BT_null->getDescription():buf.str(), - N); - - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDerefExpr(N)); - - C.EmitReport(report); - return; - } - else { - // Otherwise, we have the case where the location could either be - // null or not-null. Record the error node as an "implicit" null - // dereference. - if (ExplodedNode *N = C.GenerateSink(nullState)) - ImplicitNullDerefNodes.push_back(N); - } - } - - // From this point forward, we know that the location is not null. - C.addTransition(notNullState); -} diff --git a/lib/Analysis/DivZeroChecker.cpp b/lib/Analysis/DivZeroChecker.cpp deleted file mode 100644 index 266c236..0000000 --- a/lib/Analysis/DivZeroChecker.cpp +++ /dev/null @@ -1,84 +0,0 @@ -//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines DivZeroChecker, a builtin check in GRExprEngine that performs -// checks for division by zeros. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class DivZeroChecker : public CheckerVisitor<DivZeroChecker> { - BuiltinBug *BT; -public: - DivZeroChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} // end anonymous namespace - -void clang::RegisterDivZeroChecker(GRExprEngine &Eng) { - Eng.registerCheck(new DivZeroChecker()); -} - -void *DivZeroChecker::getTag() { - static int x; - return &x; -} - -void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - BinaryOperator::Opcode Op = B->getOpcode(); - if (Op != BinaryOperator::Div && - Op != BinaryOperator::Rem && - Op != BinaryOperator::DivAssign && - Op != BinaryOperator::RemAssign) - return; - - if (!B->getRHS()->getType()->isIntegerType() || - !B->getRHS()->getType()->isScalarType()) - return; - - SVal Denom = C.getState()->getSVal(B->getRHS()); - const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); - - // Divide-by-undefined handled in the generic checking for uses of - // undefined values. - if (!DV) - return; - - // Check for divide by zero. - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotZero, *stateZero; - llvm::tie(stateNotZero, stateZero) = CM.AssumeDual(C.getState(), *DV); - - if (stateZero && !stateNotZero) { - if (ExplodedNode *N = C.GenerateSink(stateZero)) { - if (!BT) - BT = new BuiltinBug("Division by zero"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, BT->getDescription(), N); - - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - bugreporter::GetDenomExpr(N)); - - C.EmitReport(R); - } - return; - } - - // If we get here, then the denom should not be zero. We abandon the implicit - // zero denom case for now. - C.addTransition(stateNotZero); -} diff --git a/lib/Analysis/Environment.cpp b/lib/Analysis/Environment.cpp deleted file mode 100644 index f04cf7b..0000000 --- a/lib/Analysis/Environment.cpp +++ /dev/null @@ -1,164 +0,0 @@ -//== Environment.cpp - Map from Stmt* to Locations/Values -------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the Environment and EnvironmentManager classes. -// -//===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; - -SVal Environment::GetSVal(const Stmt *E, ValueManager& ValMgr) const { - - for (;;) { - - switch (E->getStmtClass()) { - - case Stmt::AddrLabelExprClass: - return ValMgr.makeLoc(cast<AddrLabelExpr>(E)); - - // ParenExprs are no-ops. - - case Stmt::ParenExprClass: - E = cast<ParenExpr>(E)->getSubExpr(); - continue; - - case Stmt::CharacterLiteralClass: { - const CharacterLiteral* C = cast<CharacterLiteral>(E); - return ValMgr.makeIntVal(C->getValue(), C->getType()); - } - - case Stmt::IntegerLiteralClass: { - // In C++, this expression may have been bound to a temporary object. - SVal const *X = ExprBindings.lookup(E); - if (X) - return *X; - else - return ValMgr.makeIntVal(cast<IntegerLiteral>(E)); - } - - // Casts where the source and target type are the same - // are no-ops. We blast through these to get the descendant - // subexpression that has a value. - - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: { - const CastExpr* C = cast<CastExpr>(E); - QualType CT = C->getType(); - - if (CT->isVoidType()) - return UnknownVal(); - - break; - } - - // Handle all other Stmt* using a lookup. - - default: - break; - }; - - break; - } - - return LookupExpr(E); -} - -Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S, - SVal V, bool Invalidate) { - assert(S); - - if (V.isUnknown()) { - if (Invalidate) - return Environment(F.Remove(Env.ExprBindings, S), Env.ACtx); - else - return Env; - } - - return Environment(F.Add(Env.ExprBindings, S, V), Env.ACtx); -} - -namespace { -class MarkLiveCallback : public SymbolVisitor { - SymbolReaper &SymReaper; -public: - MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} - bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; } -}; -} // end anonymous namespace - -// RemoveDeadBindings: -// - Remove subexpression bindings. -// - Remove dead block expression bindings. -// - Keep live block expression bindings: -// - Mark their reachable symbols live in SymbolReaper, -// see ScanReachableSymbols. -// - Mark the region in DRoots if the binding is a loc::MemRegionVal. - -Environment -EnvironmentManager::RemoveDeadBindings(Environment Env, const Stmt *S, - SymbolReaper &SymReaper, - const GRState *ST, - llvm::SmallVectorImpl<const MemRegion*> &DRoots) { - - CFG &C = *Env.getAnalysisContext().getCFG(); - - // 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(&Env.getAnalysisContext()); - - // Iterate over the block-expr bindings. - for (Environment::iterator I = Env.begin(), E = Env.end(); - I != E; ++I) { - - const Stmt *BlkExpr = I.getKey(); - - // Not a block-level expression? - if (!C.isBlkExpr(BlkExpr)) - continue; - - const SVal &X = I.getData(); - - if (SymReaper.isLive(S, BlkExpr)) { - // Copy the binding to the new map. - NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); - - // If the block expr's value is a memory region, then mark that region. - if (isa<loc::MemRegionVal>(X)) { - const MemRegion* R = cast<loc::MemRegionVal>(X).getRegion(); - DRoots.push_back(R); - // Mark the super region of the RX as live. - // e.g.: int x; char *y = (char*) &x; if (*y) ... - // 'y' => element region. 'x' is its super region. - // We only add one level super region for now. - - // FIXME: maybe multiple level of super regions should be added. - if (const SubRegion *SR = dyn_cast<SubRegion>(R)) - DRoots.push_back(SR->getSuperRegion()); - } - - // Mark all symbols in the block expr's value live. - MarkLiveCallback cb(SymReaper); - ST->scanReachableSymbols(X, cb); - continue; - } - - // Otherwise the expression is dead with a couple exceptions. - // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the - // beginning of itself, but we need its UndefinedVal to determine its - // SVal. - if (X.isUndef() && cast<UndefinedVal>(X).getData()) - NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); - } - - return NewEnv; -} diff --git a/lib/Analysis/ExplodedGraph.cpp b/lib/Analysis/ExplodedGraph.cpp deleted file mode 100644 index 3b339ff..0000000 --- a/lib/Analysis/ExplodedGraph.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- C++ -*------=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the template classes ExplodedNode and ExplodedGraph, -// which represent a path-sensitive, intra-procedural "exploded graph." -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/ExplodedGraph.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/AST/Stmt.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SmallVector.h" -#include <vector> - -using namespace clang; - -//===----------------------------------------------------------------------===// -// Node auditing. -//===----------------------------------------------------------------------===// - -// An out of line virtual method to provide a home for the class vtable. -ExplodedNode::Auditor::~Auditor() {} - -#ifndef NDEBUG -static ExplodedNode::Auditor* NodeAuditor = 0; -#endif - -void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { -#ifndef NDEBUG - NodeAuditor = A; -#endif -} - -//===----------------------------------------------------------------------===// -// ExplodedNode. -//===----------------------------------------------------------------------===// - -static inline BumpVector<ExplodedNode*>& getVector(void* P) { - return *reinterpret_cast<BumpVector<ExplodedNode*>*>(P); -} - -void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) { - assert (!V->isSink()); - Preds.addNode(V, G); - V->Succs.addNode(this, G); -#ifndef NDEBUG - if (NodeAuditor) NodeAuditor->AddEdge(V, this); -#endif -} - -void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) { - assert((reinterpret_cast<uintptr_t>(N) & Mask) == 0x0); - assert(!getFlag()); - - if (getKind() == Size1) { - if (ExplodedNode* NOld = getNode()) { - BumpVectorContext &Ctx = G.getNodeAllocator(); - BumpVector<ExplodedNode*> *V = - G.getAllocator().Allocate<BumpVector<ExplodedNode*> >(); - new (V) BumpVector<ExplodedNode*>(Ctx, 4); - - assert((reinterpret_cast<uintptr_t>(V) & Mask) == 0x0); - V->push_back(NOld, Ctx); - V->push_back(N, Ctx); - P = reinterpret_cast<uintptr_t>(V) | SizeOther; - assert(getPtr() == (void*) V); - assert(getKind() == SizeOther); - } - else { - P = reinterpret_cast<uintptr_t>(N); - assert(getKind() == Size1); - } - } - else { - assert(getKind() == SizeOther); - getVector(getPtr()).push_back(N, G.getNodeAllocator()); - } -} - -unsigned ExplodedNode::NodeGroup::size() const { - if (getFlag()) - return 0; - - if (getKind() == Size1) - return getNode() ? 1 : 0; - else - return getVector(getPtr()).size(); -} - -ExplodedNode **ExplodedNode::NodeGroup::begin() const { - if (getFlag()) - return NULL; - - if (getKind() == Size1) - return (ExplodedNode**) (getPtr() ? &P : NULL); - else - return const_cast<ExplodedNode**>(&*(getVector(getPtr()).begin())); -} - -ExplodedNode** ExplodedNode::NodeGroup::end() const { - if (getFlag()) - return NULL; - - if (getKind() == Size1) - return (ExplodedNode**) (getPtr() ? &P+1 : NULL); - else { - // Dereferencing end() is undefined behaviour. The vector is not empty, so - // we can dereference the last elem and then add 1 to the result. - return const_cast<ExplodedNode**>(getVector(getPtr()).end()); - } -} - -ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L, - const GRState* State, 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* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); - - if (!V) { - // Allocate a new node. - V = (NodeTy*) getAllocator().Allocate<NodeTy>(); - new (V) NodeTy(L, State); - - // Insert the node into the node set and return it. - Nodes.InsertNode(V, InsertPos); - - ++NumNodes; - - if (IsNew) *IsNew = true; - } - else - if (IsNew) *IsNew = false; - - return V; -} - -std::pair<ExplodedGraph*, InterExplodedGraphMap*> -ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, - llvm::DenseMap<const void*, const void*> *InverseMap) const { - - if (NBeg == NEnd) - return std::make_pair((ExplodedGraph*) 0, - (InterExplodedGraphMap*) 0); - - assert (NBeg < NEnd); - - llvm::OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); - - ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); - - return std::make_pair(static_cast<ExplodedGraph*>(G), M.take()); -} - -ExplodedGraph* -ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, - const ExplodedNode* const* EndSources, - InterExplodedGraphMap* M, - llvm::DenseMap<const void*, const void*> *InverseMap) const { - - typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty; - Pass1Ty Pass1; - - typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> Pass2Ty; - Pass2Ty& Pass2 = M->M; - - llvm::SmallVector<const ExplodedNode*, 10> WL1, WL2; - - // ===- Pass 1 (reverse DFS) -=== - for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { - assert(*I); - WL1.push_back(*I); - } - - // Process the first worklist until it is empty. Because it is a std::list - // it acts like a FIFO queue. - while (!WL1.empty()) { - const ExplodedNode *N = WL1.back(); - WL1.pop_back(); - - // Have we already visited this node? If so, continue to the next one. - if (Pass1.count(N)) - continue; - - // Otherwise, mark this node as visited. - Pass1.insert(N); - - // If this is a root enqueue it to the second worklist. - if (N->Preds.empty()) { - WL2.push_back(N); - continue; - } - - // Visit our predecessors and enqueue them. - for (ExplodedNode** I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) - WL1.push_back(*I); - } - - // We didn't hit a root? Return with a null pointer for the new graph. - if (WL2.empty()) - return 0; - - // Create an empty graph. - ExplodedGraph* G = MakeEmptyGraph(); - - // ===- Pass 2 (forward DFS to construct the new graph) -=== - while (!WL2.empty()) { - const ExplodedNode* N = WL2.back(); - WL2.pop_back(); - - // Skip this node if we have already processed it. - if (Pass2.find(N) != Pass2.end()) - continue; - - // 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); - Pass2[N] = NewN; - - // Also record the reverse mapping from the new node to the old node. - if (InverseMap) (*InverseMap)[NewN] = N; - - // If this node is a root, designate it as such in the graph. - if (N->Preds.empty()) - G->addRoot(NewN); - - // In the case that some of the intended predecessors of NewN have already - // been created, we should hook them up as predecessors. - - // Walk through the predecessors of 'N' and hook up their corresponding - // nodes in the new graph (if any) to the freshly created node. - for (ExplodedNode **I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) { - Pass2Ty::iterator PI = Pass2.find(*I); - if (PI == Pass2.end()) - continue; - - NewN->addPredecessor(PI->second, *G); - } - - // In the case that some of the intended successors of NewN have already - // been created, we should hook them up as successors. Otherwise, enqueue - // the new nodes from the original graph that should have nodes created - // in the new graph. - for (ExplodedNode **I=N->Succs.begin(), **E=N->Succs.end(); I!=E; ++I) { - Pass2Ty::iterator PI = Pass2.find(*I); - if (PI != Pass2.end()) { - PI->second->addPredecessor(NewN, *G); - continue; - } - - // Enqueue nodes to the worklist that were marked during pass 1. - if (Pass1.count(*I)) - WL2.push_back(*I); - } - - // Finally, explictly mark all nodes without any successors as sinks. - if (N->isSink()) - NewN->markAsSink(); - } - - return G; -} - -ExplodedNode* -InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const { - llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = - M.find(N); - - return I == M.end() ? 0 : I->second; -} - diff --git a/lib/Analysis/FixedAddressChecker.cpp b/lib/Analysis/FixedAddressChecker.cpp deleted file mode 100644 index 031ca44..0000000 --- a/lib/Analysis/FixedAddressChecker.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines FixedAddressChecker, a builtin checker that checks for -// assignment of a fixed address to a pointer. -// This check corresponds to CWE-587. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class FixedAddressChecker - : public CheckerVisitor<FixedAddressChecker> { - BuiltinBug *BT; -public: - FixedAddressChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} - -void *FixedAddressChecker::getTag() { - static int x; - return &x; -} - -void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - // Using a fixed address is not portable because that address will probably - // not be valid in all environments or platforms. - - if (B->getOpcode() != BinaryOperator::Assign) - return; - - QualType T = B->getType(); - if (!T->isPointerType()) - return; - - const GRState *state = C.getState(); - - SVal RV = state->getSVal(B->getRHS()); - - if (!RV.isConstant() || RV.isZeroConstant()) - return; - - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Use fixed address", - "Using a fixed address is not portable because that " - "address will probably not be valid in all " - "environments or platforms."); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); - R->addRange(B->getRHS()->getSourceRange()); - C.EmitReport(R); - } -} - -void clang::RegisterFixedAddressChecker(GRExprEngine &Eng) { - Eng.registerCheck(new FixedAddressChecker()); -} diff --git a/lib/Analysis/GRBlockCounter.cpp b/lib/Analysis/GRBlockCounter.cpp deleted file mode 100644 index 4f4103a..0000000 --- a/lib/Analysis/GRBlockCounter.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//==- GRBlockCounter.h - ADT for counting block visits -------------*- 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 GRBlockCounter, an abstract data type used to count -// the number of times a given block has been visited along a path -// analyzed by GRCoreEngine. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRBlockCounter.h" -#include "llvm/ADT/ImmutableMap.h" - -using namespace clang; - -typedef llvm::ImmutableMap<unsigned,unsigned> CountMap; - -static inline CountMap GetMap(void* D) { - return CountMap(static_cast<CountMap::TreeTy*>(D)); -} - -static inline CountMap::Factory& GetFactory(void* F) { - return *static_cast<CountMap::Factory*>(F); -} - -unsigned GRBlockCounter::getNumVisited(unsigned BlockID) const { - CountMap M = GetMap(Data); - CountMap::data_type* T = M.lookup(BlockID); - return T ? *T : 0; -} - -GRBlockCounter::Factory::Factory(llvm::BumpPtrAllocator& Alloc) { - F = new CountMap::Factory(Alloc); -} - -GRBlockCounter::Factory::~Factory() { - delete static_cast<CountMap::Factory*>(F); -} - -GRBlockCounter -GRBlockCounter::Factory::IncrementCount(GRBlockCounter BC, unsigned BlockID) { - return GRBlockCounter(GetFactory(F).Add(GetMap(BC.Data), BlockID, - BC.getNumVisited(BlockID)+1).getRoot()); -} - -GRBlockCounter -GRBlockCounter::Factory::GetEmptyCounter() { - return GRBlockCounter(GetFactory(F).GetEmptyMap().getRoot()); -} diff --git a/lib/Analysis/GRCoreEngine.cpp b/lib/Analysis/GRCoreEngine.cpp deleted file mode 100644 index 209452a..0000000 --- a/lib/Analysis/GRCoreEngine.cpp +++ /dev/null @@ -1,599 +0,0 @@ -//==- GRCoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- 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 generic engine for intraprocedural, path-sensitive, -// dataflow analysis via graph reachability engine. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRCoreEngine.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/AST/Expr.h" -#include "llvm/Support/Casting.h" -#include "llvm/ADT/DenseMap.h" -#include <vector> -#include <queue> - -using llvm::cast; -using llvm::isa; -using namespace clang; - -//===----------------------------------------------------------------------===// -// Worklist classes for exploration of reachable states. -//===----------------------------------------------------------------------===// - -namespace { -class DFS : public GRWorkList { - llvm::SmallVector<GRWorkListUnit,20> Stack; -public: - virtual bool hasWork() const { - return !Stack.empty(); - } - - virtual void Enqueue(const GRWorkListUnit& U) { - Stack.push_back(U); - } - - virtual GRWorkListUnit Dequeue() { - assert (!Stack.empty()); - const GRWorkListUnit& U = Stack.back(); - Stack.pop_back(); // This technically "invalidates" U, but we are fine. - return U; - } -}; - -class BFS : public GRWorkList { - std::queue<GRWorkListUnit> Queue; -public: - virtual bool hasWork() const { - return !Queue.empty(); - } - - virtual void Enqueue(const GRWorkListUnit& U) { - Queue.push(U); - } - - virtual GRWorkListUnit Dequeue() { - // Don't use const reference. The subsequent pop_back() might make it - // unsafe. - GRWorkListUnit U = Queue.front(); - Queue.pop(); - return U; - } -}; - -} // end anonymous namespace - -// Place the dstor for GRWorkList here because it contains virtual member -// functions, and we the code for the dstor generated in one compilation unit. -GRWorkList::~GRWorkList() {} - -GRWorkList *GRWorkList::MakeDFS() { return new DFS(); } -GRWorkList *GRWorkList::MakeBFS() { return new BFS(); } - -namespace { - class BFSBlockDFSContents : public GRWorkList { - std::queue<GRWorkListUnit> Queue; - llvm::SmallVector<GRWorkListUnit,20> Stack; - public: - virtual bool hasWork() const { - return !Queue.empty() || !Stack.empty(); - } - - virtual void Enqueue(const GRWorkListUnit& U) { - if (isa<BlockEntrance>(U.getNode()->getLocation())) - Queue.push(U); - else - Stack.push_back(U); - } - - virtual GRWorkListUnit Dequeue() { - // Process all basic blocks to completion. - if (!Stack.empty()) { - const GRWorkListUnit& U = Stack.back(); - Stack.pop_back(); // This technically "invalidates" U, but we are fine. - return U; - } - - assert(!Queue.empty()); - // Don't use const reference. The subsequent pop_back() might make it - // unsafe. - GRWorkListUnit U = Queue.front(); - Queue.pop(); - return U; - } - }; -} // end anonymous namespace - -GRWorkList* GRWorkList::MakeBFSBlockDFSContents() { - return new BFSBlockDFSContents(); -} - -//===----------------------------------------------------------------------===// -// Core analysis engine. -//===----------------------------------------------------------------------===// -void GRCoreEngine::ProcessEndPath(GREndPathNodeBuilder& Builder) { - SubEngine.ProcessEndPath(Builder); -} - -void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) { - SubEngine.ProcessStmt(E, Builder); -} - -bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const GRState* State, - GRBlockCounter BC) { - return SubEngine.ProcessBlockEntrance(Blk, State, BC); -} - -void GRCoreEngine::ProcessBranch(Stmt* Condition, Stmt* Terminator, - GRBranchNodeBuilder& Builder) { - SubEngine.ProcessBranch(Condition, Terminator, Builder); -} - -void GRCoreEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& Builder) { - SubEngine.ProcessIndirectGoto(Builder); -} - -void GRCoreEngine::ProcessSwitch(GRSwitchNodeBuilder& Builder) { - SubEngine.ProcessSwitch(Builder); -} - -/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. -bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { - - if (G->num_roots() == 0) { // Initialize the analysis by constructing - // the root if none exists. - - CFGBlock* Entry = &(L->getCFG()->getEntry()); - - assert (Entry->empty() && - "Entry block must be empty."); - - assert (Entry->succ_size() == 1 && - "Entry block must have 1 successor."); - - // Get the solitary successor. - CFGBlock* Succ = *(Entry->succ_begin()); - - // Construct an edge representing the - // starting location in the function. - BlockEdge StartLoc(Entry, Succ, L); - - // Set the current block counter to being empty. - WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); - - // Generate the root. - GenerateNode(StartLoc, getInitialState(L), 0); - } - - while (Steps && WList->hasWork()) { - --Steps; - const GRWorkListUnit& WU = WList->Dequeue(); - - // Set the current block counter. - WList->setBlockCounter(WU.getBlockCounter()); - - // 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; - - default: - assert(isa<PostStmt>(Node->getLocation())); - HandlePostStmt(cast<PostStmt>(Node->getLocation()), WU.getBlock(), - WU.getIndex(), Node); - break; - } - } - - return WList->hasWork(); -} - - -void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { - - CFGBlock* Blk = L.getDst(); - - // Check if we are entering the EXIT block. - if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { - - assert (L.getLocationContext()->getCFG()->getExit().size() == 0 - && "EXIT block cannot contain Stmts."); - - // Process the final state transition. - GREndPathNodeBuilder Builder(Blk, Pred, this); - ProcessEndPath(Builder); - - // This path is done. Don't enqueue any more nodes. - return; - } - - // FIXME: Should we allow ProcessBlockEntrance to also manipulate state? - - if (ProcessBlockEntrance(Blk, Pred->State, WList->getBlockCounter())) - GenerateNode(BlockEntrance(Blk, Pred->getLocationContext()), Pred->State, Pred); -} - -void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L, - ExplodedNode* Pred) { - - // Increment the block counter. - GRBlockCounter Counter = WList->getBlockCounter(); - Counter = BCounterFactory.IncrementCount(Counter, L.getBlock()->getBlockID()); - WList->setBlockCounter(Counter); - - // Process the entrance of the block. - if (CFGElement E = L.getFirstElement()) { - GRStmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, - SubEngine.getStateManager()); - ProcessStmt(E, Builder); - } - else - HandleBlockExit(L.getBlock(), Pred); -} - -void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) { - - if (Stmt* Term = B->getTerminator()) { - switch (Term->getStmtClass()) { - default: - assert(false && "Analysis for this terminator not implemented."); - break; - - case Stmt::BinaryOperatorClass: // '&&' and '||' - HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); - return; - - case Stmt::ConditionalOperatorClass: - HandleBranch(cast<ConditionalOperator>(Term)->getCond(), Term, B, Pred); - return; - - // FIXME: Use constant-folding in CFG construction to simplify this - // case. - - case Stmt::ChooseExprClass: - HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::DoStmtClass: - HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::ForStmtClass: - HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::ContinueStmtClass: - case Stmt::BreakStmtClass: - case Stmt::GotoStmtClass: - break; - - case Stmt::IfStmtClass: - HandleBranch(cast<IfStmt>(Term)->getCond(), Term, B, Pred); - return; - - case Stmt::IndirectGotoStmtClass: { - // Only 1 successor: the indirect goto dispatch block. - assert (B->succ_size() == 1); - - GRIndirectGotoNodeBuilder - builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(), - *(B->succ_begin()), this); - - ProcessIndirectGoto(builder); - return; - } - - case Stmt::ObjCForCollectionStmtClass: { - // In the case of ObjCForCollectionStmt, it appears twice in a CFG: - // - // (1) inside a basic block, which represents the binding of the - // 'element' variable to a value. - // (2) in a terminator, which represents the branch. - // - // For (1), subengines will bind a value (i.e., 0 or 1) indicating - // whether or not collection contains any more elements. We cannot - // just test to see if the element is nil because a container can - // contain nil elements. - HandleBranch(Term, Term, B, Pred); - return; - } - - case Stmt::SwitchStmtClass: { - GRSwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), - this); - - ProcessSwitch(builder); - return; - } - - case Stmt::WhileStmtClass: - HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); - return; - } - } - - assert (B->succ_size() == 1 && - "Blocks with no terminator should have at most 1 successor."); - - GenerateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), - Pred->State, Pred); -} - -void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, - ExplodedNode* Pred) { - assert (B->succ_size() == 2); - - GRBranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), - Pred, this); - - ProcessBranch(Cond, Term, Builder); -} - -void GRCoreEngine::HandlePostStmt(const PostStmt& L, CFGBlock* B, - unsigned StmtIdx, ExplodedNode* Pred) { - - assert (!B->empty()); - - if (StmtIdx == B->size()) - HandleBlockExit(B, Pred); - else { - GRStmtNodeBuilder Builder(B, StmtIdx, Pred, this, - SubEngine.getStateManager()); - ProcessStmt((*B)[StmtIdx], Builder); - } -} - -/// GenerateNode - Utility method to generate nodes, hook up successors, -/// and add nodes to the worklist. -void GRCoreEngine::GenerateNode(const ProgramPoint& Loc, - const GRState* State, ExplodedNode* Pred) { - - bool IsNew; - ExplodedNode* Node = G->getNode(Loc, State, &IsNew); - - if (Pred) - Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. - else { - assert (IsNew); - G->addRoot(Node); // 'Node' has no predecessor. Make it a root. - } - - // Only add 'Node' to the worklist if it was freshly generated. - if (IsNew) WList->Enqueue(Node); -} - -GRStmtNodeBuilder::GRStmtNodeBuilder(CFGBlock* b, unsigned idx, - ExplodedNode* N, GRCoreEngine* e, - GRStateManager &mgr) - : Eng(*e), B(*b), Idx(idx), Pred(N), LastNode(N), Mgr(mgr), Auditor(0), - PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), - PointKind(ProgramPoint::PostStmtKind), Tag(0) { - Deferred.insert(N); - CleanedState = getLastNode()->getState(); -} - -GRStmtNodeBuilder::~GRStmtNodeBuilder() { - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) - GenerateAutoTransition(*I); -} - -void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { - assert (!N->isSink()); - - PostStmt Loc(getStmt(), 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); - return; - } - - bool IsNew; - ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); - Succ->addPredecessor(N, *Eng.G); - - if (IsNew) - Eng.WList->Enqueue(Succ, B, Idx+1); -} - -static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, - const LocationContext *LC, const void *tag){ - switch (K) { - default: - assert(false && "Unhandled ProgramPoint kind"); - case ProgramPoint::PreStmtKind: - return PreStmt(S, LC, tag); - case ProgramPoint::PostStmtKind: - return PostStmt(S, LC, tag); - case ProgramPoint::PreLoadKind: - return PreLoad(S, LC, tag); - case ProgramPoint::PostLoadKind: - return PostLoad(S, LC, tag); - case ProgramPoint::PreStoreKind: - return PreStore(S, LC, tag); - case ProgramPoint::PostStoreKind: - return PostStore(S, LC, tag); - case ProgramPoint::PostLValueKind: - return PostLValue(S, LC, tag); - case ProgramPoint::PostPurgeDeadSymbolsKind: - return PostPurgeDeadSymbols(S, LC, tag); - } -} - -ExplodedNode* -GRStmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, - ExplodedNode* Pred, - ProgramPoint::Kind K, - const void *tag) { - - const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); - return generateNodeInternal(L, state, Pred); -} - -ExplodedNode* -GRStmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, - const GRState* 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); - LastNode = N; - return N; - } - - LastNode = NULL; - return NULL; -} - -ExplodedNode* GRBranchNodeBuilder::generateNode(const GRState* State, - bool branch) { - - // If the branch has been marked infeasible we should not generate a node. - if (!isFeasible(branch)) - return NULL; - - bool IsNew; - - ExplodedNode* Succ = - Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), - State, &IsNew); - - Succ->addPredecessor(Pred, *Eng.G); - - if (branch) - GeneratedTrue = true; - else - GeneratedFalse = true; - - if (IsNew) { - Deferred.push_back(Succ); - return Succ; - } - - return NULL; -} - -GRBranchNodeBuilder::~GRBranchNodeBuilder() { - if (!GeneratedTrue) generateNode(Pred->State, true); - if (!GeneratedFalse) generateNode(Pred->State, false); - - for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) - if (!(*I)->isSink()) Eng.WList->Enqueue(*I); -} - - -ExplodedNode* -GRIndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, - bool isSink) { - bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); - - Succ->addPredecessor(Pred, *Eng.G); - - if (IsNew) { - - if (isSink) - Succ->markAsSink(); - else - Eng.WList->Enqueue(Succ); - - return Succ; - } - - return NULL; -} - - -ExplodedNode* -GRSwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ - - bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), - Pred->getLocationContext()), St, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); - - if (IsNew) { - Eng.WList->Enqueue(Succ); - return Succ; - } - - return NULL; -} - - -ExplodedNode* -GRSwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { - - // Get the block for the default case. - assert (Src->succ_rbegin() != Src->succ_rend()); - CFGBlock* DefaultBlock = *Src->succ_rbegin(); - - bool IsNew; - - ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, - Pred->getLocationContext()), St, &IsNew); - Succ->addPredecessor(Pred, *Eng.G); - - if (IsNew) { - if (isSink) - Succ->markAsSink(); - else - Eng.WList->Enqueue(Succ); - - return Succ; - } - - return NULL; -} - -GREndPathNodeBuilder::~GREndPathNodeBuilder() { - // Auto-generate an EOP node if one has not been generated. - if (!HasGeneratedNode) generateNode(Pred->State); -} - -ExplodedNode* -GREndPathNodeBuilder::generateNode(const GRState* State, const void *tag, - ExplodedNode* P) { - HasGeneratedNode = true; - bool IsNew; - - ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, - Pred->getLocationContext(), tag), State, &IsNew); - - Node->addPredecessor(P ? P : Pred, *Eng.G); - - if (IsNew) { - Eng.G->addEndOfPath(Node); - return Node; - } - - return NULL; -} diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp deleted file mode 100644 index 8f8d859..0000000 --- a/lib/Analysis/GRExprEngine.cpp +++ /dev/null @@ -1,3325 +0,0 @@ -//=-- GRExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- 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 meta-engine for path-sensitive dataflow analysis that -// is built on GREngine, but provides the boilerplate to execute transfer -// functions and build the ExplodedGraph at the expression level. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/AST/CharUnits.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/StmtObjC.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/StringSwitch.h" - -#ifndef NDEBUG -#include "llvm/Support/GraphWriter.h" -#endif - -using namespace clang; -using llvm::dyn_cast; -using llvm::dyn_cast_or_null; -using llvm::cast; -using llvm::APSInt; - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(0, &II); -} - - -static QualType GetCalleeReturnType(const CallExpr *CE) { - const Expr *Callee = CE->getCallee(); - QualType T = Callee->getType(); - if (const PointerType *PT = T->getAs<PointerType>()) { - const FunctionType *FT = PT->getPointeeType()->getAs<FunctionType>(); - T = FT->getResultType(); - } - else { - const BlockPointerType *BT = T->getAs<BlockPointerType>(); - T = BT->getPointeeType()->getAs<FunctionType>()->getResultType(); - } - return T; -} - -static bool CalleeReturnsReference(const CallExpr *CE) { - return (bool) GetCalleeReturnType(CE)->getAs<ReferenceType>(); -} - -static bool ReceiverReturnsReference(const ObjCMessageExpr *ME) { - const ObjCMethodDecl *MD = ME->getMethodDecl(); - if (!MD) - return false; - return MD->getResultType()->getAs<ReferenceType>(); -} - -#ifndef NDEBUG -static bool ReceiverReturnsReferenceOrRecord(const ObjCMessageExpr *ME) { - const ObjCMethodDecl *MD = ME->getMethodDecl(); - if (!MD) - return false; - QualType T = MD->getResultType(); - return T->getAs<RecordType>() || T->getAs<ReferenceType>(); -} - -static bool CalleeReturnsReferenceOrRecord(const CallExpr *CE) { - QualType T = GetCalleeReturnType(CE); - return T->getAs<ReferenceType>() || T->getAs<RecordType>(); -} -#endif - -//===----------------------------------------------------------------------===// -// Batch auditor. DEPRECATED. -//===----------------------------------------------------------------------===// - -namespace { - -class MappedBatchAuditor : public GRSimpleAPICheck { - typedef llvm::ImmutableList<GRSimpleAPICheck*> Checks; - typedef llvm::DenseMap<void*,Checks> MapTy; - - MapTy M; - Checks::Factory F; - Checks AllStmts; - -public: - MappedBatchAuditor(llvm::BumpPtrAllocator& Alloc) : - F(Alloc), AllStmts(F.GetEmptyList()) {} - - virtual ~MappedBatchAuditor() { - llvm::DenseSet<GRSimpleAPICheck*> AlreadyVisited; - - for (MapTy::iterator MI = M.begin(), ME = M.end(); MI != ME; ++MI) - for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E;++I){ - - GRSimpleAPICheck* check = *I; - - if (AlreadyVisited.count(check)) - continue; - - AlreadyVisited.insert(check); - delete check; - } - } - - void AddCheck(GRSimpleAPICheck *A, Stmt::StmtClass C) { - assert (A && "Check cannot be null."); - void* key = reinterpret_cast<void*>((uintptr_t) C); - MapTy::iterator I = M.find(key); - M[key] = F.Concat(A, I == M.end() ? F.GetEmptyList() : I->second); - } - - void AddCheck(GRSimpleAPICheck *A) { - assert (A && "Check cannot be null."); - AllStmts = F.Concat(A, AllStmts); - } - - virtual bool Audit(ExplodedNode* N, GRStateManager& VMgr) { - // First handle the auditors that accept all statements. - bool isSink = false; - for (Checks::iterator I = AllStmts.begin(), E = AllStmts.end(); I!=E; ++I) - isSink |= (*I)->Audit(N, VMgr); - - // Next handle the auditors that accept only specific statements. - const Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); - void* key = reinterpret_cast<void*>((uintptr_t) S->getStmtClass()); - MapTy::iterator MI = M.find(key); - if (MI != M.end()) { - for (Checks::iterator I=MI->second.begin(), E=MI->second.end(); I!=E; ++I) - isSink |= (*I)->Audit(N, VMgr); - } - - return isSink; - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Checker worklist routines. -//===----------------------------------------------------------------------===// - -void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, - ExplodedNodeSet &Src, bool isPrevisit) { - - if (Checkers.empty()) { - Dst.insert(Src); - return; - } - - ExplodedNodeSet Tmp; - ExplodedNodeSet *PrevSet = &Src; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ - ExplodedNodeSet *CurrSet = 0; - if (I+1 == E) - CurrSet = &Dst; - else { - CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); - } - void *tag = I->first; - Checker *checker = I->second; - - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit); - PrevSet = CurrSet; - } - - // Don't autotransition. The CheckerContext objects should do this - // automatically. -} - -void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, - ExplodedNodeSet &Dst, - const GRState *state, - ExplodedNode *Pred) { - bool Evaluated = false; - ExplodedNodeSet DstTmp; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { - void *tag = I->first; - Checker *checker = I->second; - - if (checker->GR_EvalNilReceiver(DstTmp, *Builder, *this, ME, Pred, state, - tag)) { - Evaluated = true; - break; - } else - // The checker didn't evaluate the expr. Restore the Dst. - DstTmp.clear(); - } - - if (Evaluated) - Dst.insert(DstTmp); - else - Dst.insert(Pred); -} - -// CheckerEvalCall returns true if one of the checkers processed the node. -// This may return void when all call evaluation logic goes to some checker -// in the future. -bool GRExprEngine::CheckerEvalCall(const CallExpr *CE, - ExplodedNodeSet &Dst, - ExplodedNode *Pred) { - bool Evaluated = false; - ExplodedNodeSet DstTmp; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { - void *tag = I->first; - Checker *checker = I->second; - - if (checker->GR_EvalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) { - Evaluated = true; - break; - } else - // The checker didn't evaluate the expr. Restore the DstTmp set. - DstTmp.clear(); - } - - if (Evaluated) - Dst.insert(DstTmp); - else - Dst.insert(Pred); - - return Evaluated; -} - -// FIXME: This is largely copy-paste from CheckerVisit(). Need to -// unify. -void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, - ExplodedNodeSet &Dst, - ExplodedNodeSet &Src, - SVal location, SVal val, bool isPrevisit) { - - if (Checkers.empty()) { - Dst.insert(Src); - return; - } - - ExplodedNodeSet Tmp; - ExplodedNodeSet *PrevSet = &Src; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) - { - ExplodedNodeSet *CurrSet = 0; - if (I+1 == E) - CurrSet = &Dst; - else { - CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); - } - - void *tag = I->first; - Checker *checker = I->second; - - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) - checker->GR_VisitBind(*CurrSet, *Builder, *this, AssignE, StoreE, - *NI, tag, location, val, isPrevisit); - - // Update which NodeSet is the current one. - PrevSet = CurrSet; - } - - // Don't autotransition. The CheckerContext objects should do this - // automatically. -} -//===----------------------------------------------------------------------===// -// Engine construction and deletion. -//===----------------------------------------------------------------------===// - -static void RegisterInternalChecks(GRExprEngine &Eng) { - // Register internal "built-in" BugTypes with the BugReporter. These BugTypes - // are different than what probably many checks will do since they don't - // create BugReports on-the-fly but instead wait until GRExprEngine finishes - // analyzing a function. Generation of BugReport objects is done via a call - // to 'FlushReports' from BugReporter. - // The following checks do not need to have their associated BugTypes - // explicitly registered with the BugReporter. If they issue any BugReports, - // their associated BugType will get registered with the BugReporter - // automatically. Note that the check itself is owned by the GRExprEngine - // object. - RegisterAttrNonNullChecker(Eng); - RegisterCallAndMessageChecker(Eng); - RegisterDereferenceChecker(Eng); - RegisterVLASizeChecker(Eng); - RegisterDivZeroChecker(Eng); - RegisterReturnStackAddressChecker(Eng); - RegisterReturnUndefChecker(Eng); - RegisterUndefinedArraySubscriptChecker(Eng); - RegisterUndefinedAssignmentChecker(Eng); - RegisterUndefBranchChecker(Eng); - RegisterUndefResultChecker(Eng); - - // This is not a checker yet. - RegisterNoReturnFunctionChecker(Eng); - RegisterBuiltinFunctionChecker(Eng); - RegisterOSAtomicChecker(Eng); -} - -GRExprEngine::GRExprEngine(AnalysisManager &mgr, GRTransferFuncs *tf) - : AMgr(mgr), - CoreEngine(mgr.getASTContext(), *this), - G(CoreEngine.getGraph()), - Builder(NULL), - StateMgr(G.getContext(), mgr.getStoreManagerCreator(), - mgr.getConstraintManagerCreator(), G.getAllocator(), - *this), - SymMgr(StateMgr.getSymbolManager()), - ValMgr(StateMgr.getValueManager()), - SVator(ValMgr.getSValuator()), - CurrentStmt(NULL), - NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), - RaiseSel(GetNullarySelector("raise", G.getContext())), - BR(mgr, *this), TF(tf) { - // Register internal checks. - RegisterInternalChecks(*this); - - // FIXME: Eventually remove the TF object entirely. - TF->RegisterChecks(*this); - TF->RegisterPrinters(getStateManager().Printers); -} - -GRExprEngine::~GRExprEngine() { - BR.FlushReports(); - delete [] NSExceptionInstanceRaiseSelectors; - for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) - delete I->second; -} - -//===----------------------------------------------------------------------===// -// Utility methods. -//===----------------------------------------------------------------------===// - -void GRExprEngine::AddCheck(GRSimpleAPICheck* A, Stmt::StmtClass C) { - if (!BatchAuditor) - BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator())); - - ((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A, C); -} - -void GRExprEngine::AddCheck(GRSimpleAPICheck *A) { - if (!BatchAuditor) - BatchAuditor.reset(new MappedBatchAuditor(getGraph().getAllocator())); - - ((MappedBatchAuditor*) BatchAuditor.get())->AddCheck(A); -} - -const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) { - const GRState *state = StateMgr.getInitialState(InitLoc); - - // 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. - const IdentifierInfo *II = FD->getIdentifier(); - if (!II || !(II->getName() == "main" && FD->getNumParams() > 0)) - break; - - const ParmVarDecl *PD = FD->getParamDecl(0); - QualType T = PD->getType(); - if (!T->isIntegerType()) - break; - - const MemRegion *R = state->getRegion(PD, InitLoc); - if (!R) - break; - - SVal V = state->getSVal(loc::MemRegionVal(R)); - SVal Constraint_untested = EvalBinOp(state, BinaryOperator::GT, V, - ValMgr.makeZeroVal(T), - getContext().IntTy); - - DefinedOrUnknownSVal *Constraint = - dyn_cast<DefinedOrUnknownSVal>(&Constraint_untested); - - if (!Constraint) - break; - - if (const GRState *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"); - } - } - } while (0); - - return state; -} - -//===----------------------------------------------------------------------===// -// Top-level transfer function logic (Dispatcher). -//===----------------------------------------------------------------------===// - -/// EvalAssume - Called by ConstraintManager. Used to call checker-specific -/// logic for handling assumptions on symbolic values. -const GRState *GRExprEngine::ProcessAssume(const GRState *state, SVal cond, - bool assumption) { - for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { - - if (!state) - return NULL; - - state = I->second->EvalAssume(state, cond, assumption); - } - - if (!state) - return NULL; - - return TF->EvalAssume(state, cond, assumption); -} - -void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { - CurrentStmt = CE.getStmt(); - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - CurrentStmt->getLocStart(), - "Error evaluating statement"); - - Builder = &builder; - EntryNode = builder.getLastNode(); - - // Set up our simple checks. - if (BatchAuditor) - Builder->setAuditor(BatchAuditor.get()); - - // Create the cleaned state. - const ExplodedNode *BasePred = Builder->getBasePredecessor(); - SymbolReaper SymReaper(BasePred->getLiveVariables(), SymMgr, - BasePred->getLocationContext()->getCurrentStackFrame()); - CleanedState = AMgr.shouldPurgeDead() - ? StateMgr.RemoveDeadBindings(EntryNode->getState(), CurrentStmt, SymReaper) - : EntryNode->getState(); - - // Process any special transfer function for dead symbols. - ExplodedNodeSet Tmp; - - if (!SymReaper.hasDeadSymbols()) - Tmp.Add(EntryNode); - else { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); - Builder->PurgingDeadSymbols = true; - - // FIXME: This should soon be removed. - ExplodedNodeSet Tmp2; - getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, CurrentStmt, - CleanedState, SymReaper); - - if (Checkers.empty()) - Tmp.insert(Tmp2); - else { - ExplodedNodeSet Tmp3; - ExplodedNodeSet *SrcSet = &Tmp2; - for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { - ExplodedNodeSet *DstSet = 0; - if (I+1 == E) - DstSet = &Tmp; - else { - DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; - DstSet->clear(); - } - - void *tag = I->first; - Checker *checker = I->second; - for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); - NI != NE; ++NI) - checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, CurrentStmt, - *NI, SymReaper, tag); - SrcSet = DstSet; - } - } - - if (!Builder->BuildSinks && !Builder->HasGeneratedNode) - Tmp.Add(EntryNode); - } - - bool HasAutoGenerated = false; - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - ExplodedNodeSet Dst; - - // Set the cleaned state. - Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); - - // Visit the statement. - if (CE.asLValue()) - VisitLValue(cast<Expr>(CurrentStmt), *I, Dst); - else - Visit(CurrentStmt, *I, Dst); - - // Do we need to auto-generate a node? We only need to do this to generate - // a node with a "cleaned" state; GRCoreEngine will actually handle - // auto-transitions for other cases. - if (Dst.size() == 1 && *Dst.begin() == EntryNode - && !Builder->HasGeneratedNode && !HasAutoGenerated) { - HasAutoGenerated = true; - builder.generateNode(CurrentStmt, GetState(EntryNode), *I); - } - } - - // NULL out these variables to cleanup. - CleanedState = NULL; - EntryNode = NULL; - - CurrentStmt = 0; - - Builder = NULL; -} - -void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - S->getLocStart(), - "Error evaluating statement"); - - // FIXME: add metadata to the CFG so that we can disable - // 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); - return; - } - - switch (S->getStmtClass()) { - // C++ stuff we don't support yet. - case Stmt::CXXMemberCallExprClass: - case Stmt::CXXNamedCastExprClass: - case Stmt::CXXStaticCastExprClass: - case Stmt::CXXDynamicCastExprClass: - case Stmt::CXXReinterpretCastExprClass: - case Stmt::CXXConstCastExprClass: - case Stmt::CXXFunctionalCastExprClass: - case Stmt::CXXTypeidExprClass: - case Stmt::CXXBoolLiteralExprClass: - case Stmt::CXXNullPtrLiteralExprClass: - case Stmt::CXXThrowExprClass: - case Stmt::CXXDefaultArgExprClass: - case Stmt::CXXZeroInitValueExprClass: - case Stmt::CXXNewExprClass: - case Stmt::CXXDeleteExprClass: - case Stmt::CXXPseudoDestructorExprClass: - case Stmt::UnresolvedLookupExprClass: - case Stmt::UnaryTypeTraitExprClass: - case Stmt::DependentScopeDeclRefExprClass: - case Stmt::CXXConstructExprClass: - case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXExprWithTemporariesClass: - case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXUnresolvedConstructExprClass: - case Stmt::CXXDependentScopeMemberExprClass: - case Stmt::UnresolvedMemberExprClass: - case Stmt::CXXCatchStmtClass: - case Stmt::CXXTryStmtClass: { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, S, Pred, GetState(Pred)); - break; - } - - default: - // Cases we intentionally have "default" handle: - // AddrLabelExpr, IntegerLiteral, CharacterLiteral - - Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. - break; - - case Stmt::ArraySubscriptExprClass: - VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst, false); - break; - - case Stmt::AsmStmtClass: - VisitAsmStmt(cast<AsmStmt>(S), Pred, Dst); - break; - - case Stmt::BlockDeclRefExprClass: - VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(S), Pred, Dst, false); - break; - - case Stmt::BlockExprClass: - VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); - break; - - case Stmt::BinaryOperatorClass: { - BinaryOperator* B = cast<BinaryOperator>(S); - - if (B->isLogicalOp()) { - VisitLogicalExpr(B, Pred, Dst); - break; - } - else if (B->getOpcode() == BinaryOperator::Comma) { - const GRState* state = GetState(Pred); - MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); - break; - } - - if (AMgr.shouldEagerlyAssume() && - (B->isRelationalOp() || B->isEqualityOp())) { - ExplodedNodeSet Tmp; - VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp, false); - EvalEagerlyAssume(Dst, Tmp, cast<Expr>(S)); - } - else - VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst, false); - - break; - } - - case Stmt::CallExprClass: - case Stmt::CXXOperatorCallExprClass: { - CallExpr* C = cast<CallExpr>(S); - VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, false); - 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 - ChooseExpr* C = cast<ChooseExpr>(S); - VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); - break; - } - - case Stmt::CompoundAssignOperatorClass: - VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst, false); - break; - - case Stmt::CompoundLiteralExprClass: - VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst, false); - break; - - case Stmt::ConditionalOperatorClass: { // '?' operator - ConditionalOperator* C = cast<ConditionalOperator>(S); - VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); - break; - } - - case Stmt::CXXThisExprClass: - VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); - break; - - case Stmt::DeclRefExprClass: - VisitDeclRefExpr(cast<DeclRefExpr>(S), Pred, Dst, false); - break; - - case Stmt::DeclStmtClass: - VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); - break; - - case Stmt::ForStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast<ForStmt>(S)->getConditionVariable(), S, Pred, Dst); - break; - - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: { - CastExpr* C = cast<CastExpr>(S); - VisitCast(C, C->getSubExpr(), Pred, Dst, false); - break; - } - - case Stmt::IfStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast<IfStmt>(S)->getConditionVariable(), S, Pred, Dst); - break; - - case Stmt::InitListExprClass: - VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); - break; - - case Stmt::MemberExprClass: - VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst, false); - break; - - case Stmt::ObjCIvarRefExprClass: - VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst, false); - break; - - case Stmt::ObjCForCollectionStmtClass: - VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); - break; - - case Stmt::ObjCMessageExprClass: - VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), Pred, Dst, false); - 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, GetState(Pred)); - break; - } - - case Stmt::ParenExprClass: - Visit(cast<ParenExpr>(S)->getSubExpr()->IgnoreParens(), Pred, Dst); - break; - - case Stmt::ReturnStmtClass: - VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst); - break; - - case Stmt::SizeOfAlignOfExprClass: - VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), Pred, Dst); - break; - - case Stmt::StmtExprClass: { - StmtExpr* SE = cast<StmtExpr>(S); - - if (SE->getSubStmt()->body_empty()) { - // 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 GRState* state = GetState(Pred); - MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); - } - else - Dst.Add(Pred); - - break; - } - - case Stmt::StringLiteralClass: - VisitLValue(cast<StringLiteral>(S), Pred, Dst); - break; - - case Stmt::SwitchStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast<SwitchStmt>(S)->getConditionVariable(), S, Pred, Dst); - break; - - case Stmt::UnaryOperatorClass: { - UnaryOperator *U = cast<UnaryOperator>(S); - if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UnaryOperator::LNot)) { - ExplodedNodeSet Tmp; - VisitUnaryOperator(U, Pred, Tmp, false); - EvalEagerlyAssume(Dst, Tmp, U); - } - else - VisitUnaryOperator(U, Pred, Dst, false); - break; - } - - case Stmt::WhileStmtClass: - // This case isn't for branch processing, but for handling the - // initialization of a condition variable. - VisitCondInit(cast<WhileStmt>(S)->getConditionVariable(), S, Pred, Dst); - break; - } -} - -void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - Ex->getLocStart(), - "Error evaluating statement"); - - - Ex = Ex->IgnoreParens(); - - if (Ex != CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)){ - Dst.Add(Pred); - return; - } - - switch (Ex->getStmtClass()) { - // C++ stuff we don't support yet. - case Stmt::CXXExprWithTemporariesClass: - case Stmt::CXXMemberCallExprClass: - case Stmt::CXXZeroInitValueExprClass: { - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - Builder->BuildSinks = true; - MakeNode(Dst, Ex, Pred, GetState(Pred)); - break; - } - - case Stmt::ArraySubscriptExprClass: - VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(Ex), Pred, Dst, true); - return; - - case Stmt::BinaryOperatorClass: - case Stmt::CompoundAssignOperatorClass: - VisitBinaryOperator(cast<BinaryOperator>(Ex), Pred, Dst, true); - return; - - case Stmt::BlockDeclRefExprClass: - VisitBlockDeclRefExpr(cast<BlockDeclRefExpr>(Ex), Pred, Dst, true); - return; - - case Stmt::CallExprClass: - case Stmt::CXXOperatorCallExprClass: { - CallExpr *C = cast<CallExpr>(Ex); - assert(CalleeReturnsReferenceOrRecord(C)); - VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, true); - break; - } - - case Stmt::CompoundLiteralExprClass: - VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(Ex), Pred, Dst, true); - return; - - case Stmt::DeclRefExprClass: - VisitDeclRefExpr(cast<DeclRefExpr>(Ex), Pred, Dst, true); - return; - - case Stmt::ImplicitCastExprClass: - case Stmt::CStyleCastExprClass: { - CastExpr *C = cast<CastExpr>(Ex); - QualType T = Ex->getType(); - VisitCast(C, C->getSubExpr(), Pred, Dst, true); - break; - } - - case Stmt::MemberExprClass: - VisitMemberExpr(cast<MemberExpr>(Ex), Pred, Dst, true); - return; - - case Stmt::ObjCIvarRefExprClass: - VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(Ex), Pred, Dst, true); - return; - - case Stmt::ObjCMessageExprClass: { - ObjCMessageExpr *ME = cast<ObjCMessageExpr>(Ex); - assert(ReceiverReturnsReferenceOrRecord(ME)); - VisitObjCMessageExpr(ME, Pred, Dst, true); - return; - } - - case Stmt::ObjCPropertyRefExprClass: - case Stmt::ObjCImplicitSetterGetterRefExprClass: - // FIXME: Property assignments are lvalues, but not really "locations". - // e.g.: self.x = something; - // Here the "self.x" really can translate to a method call (setter) when - // the assignment is made. Moreover, the entire assignment expression - // evaluate to whatever "something" is, not calling the "getter" for - // the property (which would make sense since it can have side effects). - // We'll probably treat this as a location, but not one that we can - // take the address of. Perhaps we need a new SVal class for cases - // like thsis? - // Note that we have a similar problem for bitfields, since they don't - // have "locations" in the sense that we can take their address. - Dst.Add(Pred); - return; - - case Stmt::StringLiteralClass: { - const GRState* state = GetState(Pred); - SVal V = state->getLValue(cast<StringLiteral>(Ex)); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); - return; - } - - case Stmt::UnaryOperatorClass: - VisitUnaryOperator(cast<UnaryOperator>(Ex), Pred, Dst, true); - return; - - // In C++, binding an rvalue to a reference requires to create an object. - case Stmt::IntegerLiteralClass: - CreateCXXTemporaryObject(Ex, Pred, Dst); - return; - - default: - // Arbitrary subexpressions can return aggregate temporaries that - // can be used in a lvalue context. We need to enhance our support - // of such temporaries in both the environment and the store, so right - // now we just do a regular visit. - assert ((Ex->getType()->isAggregateType()) && - "Other kinds of expressions with non-aggregate/union types do" - " not have lvalues."); - - Visit(Ex, Pred, Dst); - } -} - -//===----------------------------------------------------------------------===// -// Block entrance. (Update counters). -//===----------------------------------------------------------------------===// - -bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const GRState*, - GRBlockCounter BC) { - - return BC.getNumVisited(B->getBlockID()) < 3; -} - -//===----------------------------------------------------------------------===// -// Generic node creation. -//===----------------------------------------------------------------------===// - -ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S, - ExplodedNode* Pred, const GRState* St, - ProgramPoint::Kind K, const void *tag) { - assert (Builder && "GRStmtNodeBuilder not present."); - SaveAndRestore<const void*> OldTag(Builder->Tag); - Builder->Tag = tag; - return Builder->MakeNode(Dst, S, Pred, St, K); -} - -//===----------------------------------------------------------------------===// -// Branch processing. -//===----------------------------------------------------------------------===// - -const GRState* GRExprEngine::MarkBranch(const GRState* state, - Stmt* Terminator, - bool branchTaken) { - - switch (Terminator->getStmtClass()) { - default: - return state; - - case Stmt::BinaryOperatorClass: { // '&&' and '||' - - BinaryOperator* B = cast<BinaryOperator>(Terminator); - BinaryOperator::Opcode Op = B->getOpcode(); - - assert (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr); - - // For &&, if we take the true branch, then the value of the whole - // expression is that of the RHS expression. - // - // For ||, if we take the false branch, then the value of the whole - // expression is that of the RHS expression. - - Expr* Ex = (Op == BinaryOperator::LAnd && branchTaken) || - (Op == BinaryOperator::LOr && !branchTaken) - ? B->getRHS() : B->getLHS(); - - return state->BindExpr(B, UndefinedVal(Ex)); - } - - case Stmt::ConditionalOperatorClass: { // ?: - - ConditionalOperator* C = cast<ConditionalOperator>(Terminator); - - // For ?, if branchTaken == true then the value is either the LHS or - // the condition itself. (GNU extension). - - Expr* Ex; - - if (branchTaken) - Ex = C->getLHS() ? C->getLHS() : C->getCond(); - else - Ex = C->getRHS(); - - return state->BindExpr(C, UndefinedVal(Ex)); - } - - case Stmt::ChooseExprClass: { // ?: - - ChooseExpr* C = cast<ChooseExpr>(Terminator); - - Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); - return state->BindExpr(C, UndefinedVal(Ex)); - } - } -} - -/// RecoverCastedSymbol - A helper function for ProcessBranch that is used -/// to try to recover some path-sensitivity for casts of symbolic -/// integers that promote their values (which are currently not tracked well). -/// This function returns the SVal bound to Condition->IgnoreCasts if all the -// cast(s) did was sign-extend the original value. -static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, - Stmt* Condition, ASTContext& Ctx) { - - Expr *Ex = dyn_cast<Expr>(Condition); - if (!Ex) - return UnknownVal(); - - uint64_t bits = 0; - bool bitsInit = false; - - while (CastExpr *CE = dyn_cast<CastExpr>(Ex)) { - QualType T = CE->getType(); - - if (!T->isIntegerType()) - return UnknownVal(); - - uint64_t newBits = Ctx.getTypeSize(T); - if (!bitsInit || newBits < bits) { - bitsInit = true; - bits = newBits; - } - - Ex = CE->getSubExpr(); - } - - // We reached a non-cast. Is it a symbolic value? - QualType T = Ex->getType(); - - if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits) - return UnknownVal(); - - return state->getSVal(Ex); -} - -void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, - GRBranchNodeBuilder& builder) { - - // Check for NULL conditions; e.g. "for(;;)" - if (!Condition) { - builder.markInfeasible(false); - return; - } - - PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - Condition->getLocStart(), - "Error evaluating branch"); - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { - void *tag = I->first; - Checker *checker = I->second; - checker->VisitBranchCondition(builder, *this, Condition, tag); - } - - // If the branch condition is undefined, return; - if (!builder.isFeasible(true) && !builder.isFeasible(false)) - return; - - const GRState* PrevState = builder.getState(); - SVal X = PrevState->getSVal(Condition); - - if (X.isUnknown()) { - // Give it a chance to recover from unknown. - if (const Expr *Ex = dyn_cast<Expr>(Condition)) { - if (Ex->getType()->isIntegerType()) { - // Try to recover some path-sensitivity. Right now casts of symbolic - // integers that promote their values are currently not tracked well. - // If 'Condition' is such an expression, try and recover the - // underlying value and use that instead. - SVal recovered = RecoverCastedSymbol(getStateManager(), - builder.getState(), Condition, - getContext()); - - if (!recovered.isUnknown()) { - X = recovered; - } - } - } - // If the condition is still unknown, give up. - if (X.isUnknown()) { - builder.generateNode(MarkBranch(PrevState, Term, true), true); - builder.generateNode(MarkBranch(PrevState, Term, false), false); - return; - } - } - - DefinedSVal V = cast<DefinedSVal>(X); - - // Process the true branch. - if (builder.isFeasible(true)) { - if (const GRState *state = PrevState->Assume(V, true)) - builder.generateNode(MarkBranch(state, Term, true), true); - else - builder.markInfeasible(true); - } - - // Process the false branch. - if (builder.isFeasible(false)) { - if (const GRState *state = PrevState->Assume(V, false)) - builder.generateNode(MarkBranch(state, Term, false), false); - else - builder.markInfeasible(false); - } -} - -/// ProcessIndirectGoto - Called by GRCoreEngine. Used to generate successor -/// nodes by processing the 'effects' of a computed goto jump. -void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) { - - const GRState *state = builder.getState(); - SVal V = state->getSVal(builder.getTarget()); - - // Three possibilities: - // - // (1) We know the computed label. - // (2) The label is NULL (or some other constant), or Undefined. - // (3) We have no clue about the label. Dispatch to all targets. - // - - typedef GRIndirectGotoNodeBuilder::iterator iterator; - - if (isa<loc::GotoLabel>(V)) { - LabelStmt* L = cast<loc::GotoLabel>(V).getLabel(); - - for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) { - if (I.getLabel() == L) { - builder.generateNode(I, state); - return; - } - } - - assert (false && "No block with label."); - return; - } - - if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) { - // Dispatch to the first target and mark it as a sink. - //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); - // FIXME: add checker visit. - // UndefBranches.insert(N); - return; - } - - // This is really a catch-all. We don't support symbolics yet. - // FIXME: Implement dispatch for symbolic pointers. - - for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) - builder.generateNode(I, state); -} - - -void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - - assert(Ex == CurrentStmt && - Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)); - - const GRState* state = GetState(Pred); - SVal X = state->getSVal(Ex); - - assert (X.isUndef()); - - Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); - assert(SE); - X = state->getSVal(SE); - - // Make sure that we invalidate the previous binding. - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); -} - -/// ProcessEndPath - Called by GRCoreEngine. Used to generate end-of-path -/// nodes when the control reaches the end of a function. -void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) { - getTF().EvalEndPath(*this, builder); - StateMgr.EndPath(builder.getState()); - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ - void *tag = I->first; - Checker *checker = I->second; - checker->EvalEndPath(builder, tag, *this); - } -} - -/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor -/// nodes by processing the 'effects' of a switch statement. -void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { - typedef GRSwitchNodeBuilder::iterator iterator; - const GRState* state = builder.getState(); - Expr* CondE = builder.getCondition(); - SVal CondV_untested = state->getSVal(CondE); - - if (CondV_untested.isUndef()) { - //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); - // FIXME: add checker - //UndefBranches.insert(N); - - return; - } - DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); - - const GRState *DefaultSt = state; - bool defaultIsFeasible = false; - - for (iterator I = builder.begin(), EI = builder.end(); I != EI; ++I) { - CaseStmt* Case = cast<CaseStmt>(I.getCase()); - - // Evaluate the LHS of the case value. - Expr::EvalResult V1; - bool b = Case->getLHS()->Evaluate(V1, getContext()); - - // Sanity checks. These go away in Release builds. - assert(b && V1.Val.isInt() && !V1.HasSideEffects - && "Case condition must evaluate to an integer constant."); - b = b; // silence unused variable warning - assert(V1.Val.getInt().getBitWidth() == - getContext().getTypeSize(CondE->getType())); - - // Get the RHS of the case, if it exists. - Expr::EvalResult V2; - - if (Expr* E = Case->getRHS()) { - b = E->Evaluate(V2, getContext()); - assert(b && V2.Val.isInt() && !V2.HasSideEffects - && "Case condition must evaluate to an integer constant."); - b = b; // silence unused variable warning - } - else - V2 = V1; - - // FIXME: Eventually we should replace the logic below with a range - // comparison, rather than concretize the values within the range. - // This should be easy once we have "ranges" for NonLVals. - - do { - nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt())); - DefinedOrUnknownSVal Res = SVator.EvalEQ(DefaultSt ? DefaultSt : state, - CondV, CaseVal); - - // Now "assume" that the case matches. - if (const GRState* stateNew = state->Assume(Res, true)) { - builder.generateCaseStmtNode(I, stateNew); - - // If CondV evaluates to a constant, then we know that this - // is the *only* case that we can take, so stop evaluating the - // others. - if (isa<nonloc::ConcreteInt>(CondV)) - return; - } - - // Now "assume" that the case doesn't match. Add this state - // to the default state (if it is feasible). - if (DefaultSt) { - if (const GRState *stateNew = DefaultSt->Assume(Res, false)) { - defaultIsFeasible = true; - DefaultSt = stateNew; - } - else { - defaultIsFeasible = false; - DefaultSt = NULL; - } - } - - // Concretize the next value in the range. - if (V1.Val.getInt() == V2.Val.getInt()) - break; - - ++V1.Val.getInt(); - assert (V1.Val.getInt() <= V2.Val.getInt()); - - } while (true); - } - - // If we reach here, than we know that the default branch is - // possible. - if (defaultIsFeasible) builder.generateDefaultCaseNode(DefaultSt); -} - -//===----------------------------------------------------------------------===// -// Transfer functions: logical operations ('&&', '||'). -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - assert(B->getOpcode() == BinaryOperator::LAnd || - B->getOpcode() == BinaryOperator::LOr); - - assert(B==CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); - - const GRState* state = GetState(Pred); - SVal X = state->getSVal(B); - assert(X.isUndef()); - - const Expr *Ex = (const Expr*) cast<UndefinedVal>(X).getData(); - assert(Ex); - - if (Ex == B->getRHS()) { - X = state->getSVal(Ex); - - // Handle undefined values. - if (X.isUndef()) { - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); - return; - } - - DefinedOrUnknownSVal XD = cast<DefinedOrUnknownSVal>(X); - - // We took the RHS. Because the value of the '&&' or '||' expression must - // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 - // or 1. Alternatively, we could take a lazy approach, and calculate this - // 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 GRState *newState = state->Assume(XD, true)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, ValMgr.makeIntVal(1U, B->getType()))); - - if (const GRState *newState = state->Assume(XD, false)) - MakeNode(Dst, B, Pred, - newState->BindExpr(B, ValMgr.makeIntVal(0U, B->getType()))); - } - else { - // We took the LHS expression. Depending on whether we are '&&' or - // '||' we know what the value of the expression is via properties of - // the short-circuiting. - X = ValMgr.makeIntVal(B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U, - B->getType()); - MakeNode(Dst, B, Pred, state->BindExpr(B, X)); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Loads and stores. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - - ExplodedNodeSet Tmp; - - CanQualType T = getContext().getCanonicalType(BE->getType()); - SVal V = ValMgr.getBlockPointer(BE->getBlockDecl(), T, - Pred->getLocationContext()); - - MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), - ProgramPoint::PostLValueKind); - - // Post-visit the BlockExpr. - CheckerVisit(BE, Dst, Tmp, false); -} - -void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); -} - -void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); -} - -void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - - const GRState *state = GetState(Pred); - - if (const VarDecl* VD = dyn_cast<VarDecl>(D)) { - - SVal V = state->getLValue(VD, Pred->getLocationContext()); - - if (asLValue) { - // For references, the 'lvalue' is the pointer address stored in the - // reference region. - if (VD->getType()->isReferenceType()) { - if (const MemRegion *R = V.getAsRegion()) - V = state->getSVal(R); - else - V = UnknownVal(); - } - - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); - } - else - EvalLoad(Dst, Ex, Pred, state, V); - - return; - } else if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) { - assert(!asLValue && "EnumConstantDecl does not have lvalue."); - - SVal V = ValMgr.makeIntVal(ED->getInitVal()); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); - return; - - } else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) { - // This code is valid regardless of the value of 'isLValue'. - SVal V = ValMgr.getFunctionPointer(FD); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); - return; - } - - assert (false && - "ValueDecl support for this ValueDecl not implemented."); -} - -/// VisitArraySubscriptExpr - Transfer function for array accesses -void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue){ - - Expr* Base = A->getBase()->IgnoreParens(); - Expr* Idx = A->getIdx()->IgnoreParens(); - ExplodedNodeSet Tmp; - - if (Base->getType()->isVectorType()) { - // For vector types get its lvalue. - // FIXME: This may not be correct. Is the rvalue of a vector its location? - // In fact, I think this is just a hack. We need to get the right - // semantics. - VisitLValue(Base, Pred, Tmp); - } - else - Visit(Base, Pred, Tmp); // Get Base's rvalue, which should be an LocVal. - - for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { - ExplodedNodeSet Tmp2; - Visit(Idx, *I1, Tmp2); // Evaluate the index. - - ExplodedNodeSet Tmp3; - CheckerVisit(A, Tmp3, Tmp2, true); - - for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { - const GRState* state = GetState(*I2); - SVal V = state->getLValue(A->getType(), state->getSVal(Idx), - state->getSVal(Base)); - - if (asLValue) - MakeNode(Dst, A, *I2, state->BindExpr(A, V), - ProgramPoint::PostLValueKind); - else - EvalLoad(Dst, A, *I2, state, V); - } - } -} - -/// VisitMemberExpr - Transfer function for member expressions. -void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - Expr* Base = M->getBase()->IgnoreParens(); - ExplodedNodeSet Tmp; - - if (M->isArrow()) - Visit(Base, Pred, Tmp); // p->f = ... or ... = p->f - else - VisitLValue(Base, Pred, Tmp); // x.f = ... or ... = x.f - - FieldDecl *Field = dyn_cast<FieldDecl>(M->getMemberDecl()); - if (!Field) // FIXME: skipping member expressions for non-fields - return; - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - const GRState* state = GetState(*I); - // FIXME: Should we insert some assumption logic in here to determine - // if "Base" is a valid piece of memory? Before we put this assumption - // later when using FieldOffset lvals (which we no longer have). - SVal L = state->getLValue(Field, state->getSVal(Base)); - - if (asLValue) - MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); - else - EvalLoad(Dst, M, *I, state, L); - } -} - -/// EvalBind - Handle the semantics of binding a value to a specific location. -/// This method is used by EvalStore and (soon) VisitDeclStmt, and others. -void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE, - Stmt* StoreE, ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val, - bool atDeclInit) { - - - // Do a previsit of the bind. - ExplodedNodeSet CheckedSet, Src; - Src.Add(Pred); - CheckerVisitBind(AssignE, StoreE, CheckedSet, Src, location, Val, true); - - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I!=E; ++I) { - - if (Pred != *I) - state = GetState(*I); - - const GRState* newState = 0; - - if (atDeclInit) { - const VarRegion *VR = - cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion()); - - newState = state->bindDecl(VR, Val); - } - else { - if (location.isUnknown()) { - // We know that the new state will be the same as the old state since - // the location of the binding is "unknown". Consequently, there - // is no reason to just create a new node. - newState = state; - } - else { - // We are binding to a value other than 'unknown'. Perform the binding - // using the StoreManager. - newState = state->bindLoc(cast<Loc>(location), Val); - } - } - - // The next thing to do is check if the GRTransferFuncs object wants to - // update the state based on the new binding. If the GRTransferFunc object - // doesn't do anything, just auto-propagate the current state. - GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, - newState != state); - - getTF().EvalBind(BuilderRef, location, Val); - } -} - -/// EvalStore - Handle the semantics of a store via an assignment. -/// @param Dst The node set to store generated state nodes -/// @param Ex The expression representing the location of the store -/// @param state The current simulation state -/// @param location The location to store the value -/// @param Val The value to be stored -void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, - Expr* StoreE, - ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val, - const void *tag) { - - assert(Builder && "GRStmtNodeBuilder must be defined."); - - // Evaluate the location (checks for bad dereferences). - ExplodedNodeSet Tmp; - EvalLocation(Tmp, StoreE, Pred, state, location, tag, false); - - if (Tmp.empty()) - return; - - assert(!location.isUndef()); - - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind, - ProgramPoint::PostStoreKind); - SaveAndRestore<const void*> OldTag(Builder->Tag, tag); - - // Proceed with the store. - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - EvalBind(Dst, AssignE, StoreE, *NI, GetState(*NI), location, Val); -} - -void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, QualType LoadTy) { - - // 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 - // referenced value. - if (const TypedRegion *TR = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - - QualType ValTy = TR->getValueType(getContext()); - if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { - static int loadReferenceTag = 0; - ExplodedNodeSet Tmp; - EvalLoadCommon(Tmp, Ex, 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 = GetState(*I); - location = state->getSVal(Ex); - EvalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); - } - return; - } - } - - EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); -} - -void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, QualType LoadTy) { - - // Evaluate the location (checks for bad dereferences). - ExplodedNodeSet Tmp; - EvalLocation(Tmp, Ex, Pred, state, location, tag, true); - - if (Tmp.empty()) - return; - - assert(!location.isUndef()); - - SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); - SaveAndRestore<const void*> OldTag(Builder->Tag); - - // Proceed with the load. - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - state = GetState(*NI); - if (location.isUnknown()) { - // This is important. We must nuke the old binding. - MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), - ProgramPoint::PostLoadKind, tag); - } - else { - SVal V = state->getSVal(cast<Loc>(location), LoadTy.isNull() ? - Ex->getType() : LoadTy); - MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, V), ProgramPoint::PostLoadKind, - tag); - } - } -} - -void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, - ExplodedNode* Pred, - const GRState* state, SVal location, - const void *tag, bool isLoad) { - // Early checks for performance reason. - if (location.isUnknown() || Checkers.empty()) { - Dst.Add(Pred); - return; - } - - ExplodedNodeSet Src, Tmp; - Src.Add(Pred); - ExplodedNodeSet *PrevSet = &Src; - - for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) - { - ExplodedNodeSet *CurrSet = 0; - if (I+1 == E) - CurrSet = &Dst; - else { - CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; - CurrSet->clear(); - } - - void *tag = I->first; - Checker *checker = I->second; - - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) { - // Use the 'state' argument only when the predecessor node is the - // same as Pred. This allows us to catch updates to the state. - checker->GR_VisitLocation(*CurrSet, *Builder, *this, S, *NI, - *NI == Pred ? state : GetState(*NI), - location, tag, isLoad); - } - - // Update which NodeSet is the current one. - PrevSet = CurrSet; - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Function calls. -//===----------------------------------------------------------------------===// - -namespace { -class CallExprWLItem { -public: - CallExpr::arg_iterator I; - ExplodedNode *N; - - CallExprWLItem(const CallExpr::arg_iterator &i, ExplodedNode *n) - : I(i), N(n) {} -}; -} // end anonymous namespace - -void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, - CallExpr::arg_iterator AI, - CallExpr::arg_iterator AE, - ExplodedNodeSet& Dst, bool asLValue) { - - // Determine the type of function we're calling (if available). - const FunctionProtoType *Proto = NULL; - QualType FnType = CE->getCallee()->IgnoreParens()->getType(); - if (const PointerType *FnTypePtr = FnType->getAs<PointerType>()) - Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>(); - - // Create a worklist to process the arguments. - llvm::SmallVector<CallExprWLItem, 20> WorkList; - WorkList.reserve(AE - AI); - WorkList.push_back(CallExprWLItem(AI, Pred)); - - ExplodedNodeSet ArgsEvaluated; - - while (!WorkList.empty()) { - CallExprWLItem Item = WorkList.back(); - WorkList.pop_back(); - - if (Item.I == AE) { - ArgsEvaluated.insert(Item.N); - continue; - } - - // Evaluate the argument. - ExplodedNodeSet Tmp; - const unsigned ParamIdx = Item.I - AI; - - bool VisitAsLvalue = false; - if (Proto && ParamIdx < Proto->getNumArgs()) - VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType(); - - if (VisitAsLvalue) - VisitLValue(*Item.I, Item.N, Tmp); - else - Visit(*Item.I, Item.N, Tmp); - - // Enqueue evaluating the next argument on the worklist. - ++(Item.I); - - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - WorkList.push_back(CallExprWLItem(Item.I, *NI)); - } - - // Now process the call itself. - ExplodedNodeSet DstTmp; - Expr* Callee = CE->getCallee()->IgnoreParens(); - - for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), - NE=ArgsEvaluated.end(); NI != NE; ++NI) { - // Evaluate the callee. - ExplodedNodeSet DstTmp2; - Visit(Callee, *NI, DstTmp2); - // Perform the previsit of the CallExpr, storing the results in DstTmp. - CheckerVisit(CE, DstTmp, DstTmp2, true); - } - - // Finally, evaluate the function call. We try each of the checkers - // to see if the can evaluate the function call. - ExplodedNodeSet DstTmp3; - - - for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); - DI != DE; ++DI) { - - const GRState* state = GetState(*DI); - SVal L = state->getSVal(Callee); - - // FIXME: Add support for symbolic function calls (calls involving - // function pointer values that are symbolic). - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - ExplodedNodeSet DstChecker; - - // If the callee is processed by a checker, skip the rest logic. - if (CheckerEvalCall(CE, DstChecker, *DI)) - DstTmp3.insert(DstChecker); - else { - for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), - DE_Checker = DstChecker.end(); - DI_Checker != DE_Checker; ++DI_Checker) { - - // Dispatch to the plug-in transfer function. - unsigned OldSize = DstTmp3.size(); - SaveOr OldHasGen(Builder->HasGeneratedNode); - Pred = *DI_Checker; - - // Dispatch to transfer function logic to handle the call itself. - // FIXME: Allow us to chain together transfer functions. - assert(Builder && "GRStmtNodeBuilder must be defined."); - getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred); - - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && DstTmp3.size() == OldSize && - !Builder->HasGeneratedNode) - MakeNode(DstTmp3, CE, Pred, state); - } - } - } - - // Finally, perform the post-condition check of the CallExpr and store - // the created nodes in 'Dst'. - - if (!(!asLValue && CalleeReturnsReference(CE))) { - CheckerVisit(CE, Dst, DstTmp3, false); - return; - } - - // Handle the case where the called function returns a reference but - // we expect an rvalue. For such cases, convert the reference to - // an rvalue. - // FIXME: This conversion doesn't actually happen unless the result - // of CallExpr is consumed by another expression. - ExplodedNodeSet DstTmp4; - CheckerVisit(CE, DstTmp4, DstTmp3, false); - QualType LoadTy = CE->getType(); - - static int *ConvertToRvalueTag = 0; - for (ExplodedNodeSet::iterator NI = DstTmp4.begin(), NE = DstTmp4.end(); - NI!=NE; ++NI) { - const GRState *state = GetState(*NI); - EvalLoad(Dst, CE, *NI, state, state->getSVal(CE), - &ConvertToRvalueTag, LoadTy); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C ivar references. -//===----------------------------------------------------------------------===// - -static std::pair<const void*,const void*> EagerlyAssumeTag - = std::pair<const void*,const void*>(&EagerlyAssumeTag,0); - -void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, - Expr *Ex) { - 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 GRState* state = Pred->getState(); - SVal V = state->getSVal(Ex); - if (nonloc::SymExprVal *SEV = dyn_cast<nonloc::SymExprVal>(&V)) { - // First assume that the condition is true. - if (const GRState *stateTrue = state->Assume(*SEV, true)) { - stateTrue = stateTrue->BindExpr(Ex, - ValMgr.makeIntVal(1U, Ex->getType())); - Dst.Add(Builder->generateNode(PostStmtCustom(Ex, - &EagerlyAssumeTag, Pred->getLocationContext()), - stateTrue, Pred)); - } - - // Next, assume that the condition is false. - if (const GRState *stateFalse = state->Assume(*SEV, false)) { - stateFalse = stateFalse->BindExpr(Ex, - ValMgr.makeIntVal(0U, Ex->getType())); - Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag, - Pred->getLocationContext()), - stateFalse, Pred)); - } - } - else - Dst.Add(Pred); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C ivar references. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - Expr* Base = cast<Expr>(Ex->getBase()); - ExplodedNodeSet Tmp; - Visit(Base, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - SVal BaseVal = state->getSVal(Base); - SVal location = state->getLValue(Ex->getDecl(), BaseVal); - - if (asLValue) - MakeNode(Dst, Ex, *I, state->BindExpr(Ex, location)); - else - EvalLoad(Dst, Ex, *I, state, location); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C fast enumeration 'for' statements. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - - // ObjCForCollectionStmts are processed in two places. This method - // handles the case where an ObjCForCollectionStmt* occurs as one of the - // statements within a basic block. This transfer function does two things: - // - // (1) binds the next container value to 'element'. This creates a new - // node in the ExplodedGraph. - // - // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating - // whether or not the container has any more elements. This value - // will be tested in ProcessBranch. We need to explicitly bind - // this value because a container can contain nil elements. - // - // FIXME: Eventually this logic should actually do dispatches to - // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). - // This will require simulating a temporary NSFastEnumerationState, either - // through an SVal or through the use of MemRegions. This value can - // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop - // terminates we reclaim the temporary (it goes out of scope) and we - // we can test if the SVal is 0 or if the MemRegion is null (depending - // on what approach we take). - // - // 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. - - Stmt* elem = S->getElement(); - SVal ElementV; - - if (DeclStmt* DS = dyn_cast<DeclStmt>(elem)) { - VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl()); - assert (ElemD->getInit() == 0); - ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); - VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); - return; - } - - ExplodedNodeSet Tmp; - VisitLValue(cast<Expr>(elem), Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem)); - } -} - -void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, - ExplodedNode* Pred, ExplodedNodeSet& Dst, - SVal ElementV) { - - // Check if the location we are writing back to is a null pointer. - Stmt* elem = S->getElement(); - ExplodedNodeSet Tmp; - EvalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); - - if (Tmp.empty()) - return; - - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - Pred = *NI; - const GRState *state = GetState(Pred); - - // Handle the case where the container still has elements. - SVal TrueV = ValMgr.makeTruthVal(1); - const GRState *hasElems = state->BindExpr(S, TrueV); - - // Handle the case where the container has no elements. - SVal FalseV = ValMgr.makeTruthVal(0); - const GRState *noElems = state->BindExpr(S, FalseV); - - if (loc::MemRegionVal* MV = dyn_cast<loc::MemRegionVal>(&ElementV)) - if (const TypedRegion* R = dyn_cast<TypedRegion>(MV->getRegion())) { - // FIXME: The proper thing to do is to really iterate over the - // container. We will do this with dispatch logic to the store. - // For now, just 'conjure' up a symbolic value. - QualType T = R->getValueType(getContext()); - assert(Loc::IsLocType(T)); - unsigned Count = Builder->getCurrentBlockCount(); - SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); - SVal V = ValMgr.makeLoc(Sym); - hasElems = hasElems->bindLoc(ElementV, V); - - // Bind the location to 'nil' on the false branch. - SVal nilV = ValMgr.makeIntVal(0, T); - noElems = noElems->bindLoc(ElementV, nilV); - } - - // Create the new nodes. - MakeNode(Dst, S, Pred, hasElems); - MakeNode(Dst, S, Pred, noElems); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function: Objective-C message expressions. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue){ - - VisitObjCMessageExprArgHelper(ME, ME->arg_begin(), ME->arg_end(), - Pred, Dst, asLValue); -} - -void GRExprEngine::VisitObjCMessageExprArgHelper(ObjCMessageExpr* ME, - ObjCMessageExpr::arg_iterator AI, - ObjCMessageExpr::arg_iterator AE, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, - bool asLValue) { - if (AI == AE) { - - // Process the receiver. - - if (Expr* Receiver = ME->getReceiver()) { - ExplodedNodeSet Tmp; - Visit(Receiver, Pred, Tmp); - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI != NE; - ++NI) - VisitObjCMessageExprDispatchHelper(ME, *NI, Dst, asLValue); - - return; - } - - VisitObjCMessageExprDispatchHelper(ME, Pred, Dst, asLValue); - return; - } - - ExplodedNodeSet Tmp; - Visit(*AI, Pred, Tmp); - - ++AI; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) - VisitObjCMessageExprArgHelper(ME, AI, AE, *NI, Dst, asLValue); -} - -void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, - bool asLValue) { - - // Handle previsits checks. - ExplodedNodeSet Src, DstTmp; - Src.Add(Pred); - - CheckerVisit(ME, DstTmp, Src, true); - - ExplodedNodeSet PostVisitSrc; - - for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); - DI!=DE; ++DI) { - - Pred = *DI; - bool RaisesException = false; - - unsigned OldSize = PostVisitSrc.size(); - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - if (const Expr *Receiver = ME->getReceiver()) { - const GRState *state = Pred->getState(); - - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = - cast<DefinedOrUnknownSVal>(state->getSVal(Receiver)); - - const GRState *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 handle must be nil, and merge the rest two into non-nil. - if (nilState && !notNilState) { - CheckerEvalNilReceiver(ME, PostVisitSrc, nilState, Pred); - continue; - } - - assert(notNilState); - - // Check if the "raise" message was sent. - if (ME->getSelector() == RaiseSel) - RaisesException = true; - - // Check if we raise an exception. For now treat these as sinks. - // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - - // Dispatch to plug-in transfer function. - EvalObjCMessageExpr(PostVisitSrc, ME, Pred, notNilState); - } - else { - IdentifierInfo* ClsName = ME->getClassName(); - Selector S = ME->getSelector(); - - // Check for special instance methods. - if (!NSExceptionII) { - ASTContext& Ctx = getContext(); - NSExceptionII = &Ctx.Idents.get("NSException"); - } - - if (ClsName == NSExceptionII) { - enum { NUM_RAISE_SELECTORS = 2 }; - - // Lazily create a cache of the selectors. - if (!NSExceptionInstanceRaiseSelectors) { - ASTContext& Ctx = getContext(); - NSExceptionInstanceRaiseSelectors = new Selector[NUM_RAISE_SELECTORS]; - llvm::SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; - unsigned idx = 0; - - // raise:format: - II.push_back(&Ctx.Idents.get("raise")); - II.push_back(&Ctx.Idents.get("format")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - - // raise:format::arguments: - II.push_back(&Ctx.Idents.get("arguments")); - NSExceptionInstanceRaiseSelectors[idx++] = - Ctx.Selectors.getSelector(II.size(), &II[0]); - } - - for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) - if (S == NSExceptionInstanceRaiseSelectors[i]) { - RaisesException = true; - break; - } - } - - // Check if we raise an exception. For now treat these as sinks. - // Eventually we will want to handle exceptions properly. - if (RaisesException) - Builder->BuildSinks = true; - - // Dispatch to plug-in transfer function. - EvalObjCMessageExpr(PostVisitSrc, ME, Pred, Builder->GetState(Pred)); - } - - // Handle the case where no nodes where generated. Auto-generate that - // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && PostVisitSrc.size() == OldSize && - !Builder->HasGeneratedNode) - MakeNode(PostVisitSrc, ME, Pred, GetState(Pred)); - } - - // Finally, perform the post-condition check of the ObjCMessageExpr and store - // the created nodes in 'Dst'. - if (!(!asLValue && ReceiverReturnsReference(ME))) { - CheckerVisit(ME, Dst, PostVisitSrc, false); - return; - } - - // Handle the case where the message expression returns a reference but - // we expect an rvalue. For such cases, convert the reference to - // an rvalue. - // FIXME: This conversion doesn't actually happen unless the result - // of ObjCMessageExpr is consumed by another expression. - ExplodedNodeSet DstRValueConvert; - CheckerVisit(ME, DstRValueConvert, PostVisitSrc, false); - QualType LoadTy = ME->getType(); - - static int *ConvertToRvalueTag = 0; - for (ExplodedNodeSet::iterator NI = DstRValueConvert.begin(), - NE = DstRValueConvert.end(); - NI!=NE; ++NI) { - const GRState *state = GetState(*NI); - EvalLoad(Dst, ME, *NI, state, state->getSVal(ME), - &ConvertToRvalueTag, LoadTy); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Miscellaneous statements. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { - ExplodedNodeSet S1; - QualType T = CastE->getType(); - QualType ExTy = Ex->getType(); - - if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) - T = ExCast->getTypeAsWritten(); - - if (ExTy->isArrayType() || ExTy->isFunctionType() || T->isReferenceType() || - asLValue) - VisitLValue(Ex, Pred, S1); - else - Visit(Ex, Pred, S1); - - ExplodedNodeSet S2; - CheckerVisit(CastE, S2, S1, true); - - // If we are evaluating the cast in an lvalue context, we implicitly want - // the cast to evaluate to a location. - if (asLValue) { - ASTContext &Ctx = getContext(); - T = Ctx.getPointerType(Ctx.getCanonicalType(T)); - ExTy = Ctx.getPointerType(Ctx.getCanonicalType(ExTy)); - } - - switch (CastE->getCastKind()) { - case CastExpr::CK_ToVoid: - assert(!asLValue); - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) - Dst.Add(*I); - return; - - case CastExpr::CK_NoOp: - case CastExpr::CK_FunctionToPointerDecay: - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { - // Copy the SVal of Ex to CastE. - ExplodedNode *N = *I; - const GRState *state = GetState(N); - SVal V = state->getSVal(Ex); - state = state->BindExpr(CastE, V); - MakeNode(Dst, CastE, N, state); - } - return; - - case CastExpr::CK_Unknown: - case CastExpr::CK_ArrayToPointerDecay: - case CastExpr::CK_BitCast: - case CastExpr::CK_IntegralCast: - case CastExpr::CK_IntegralToPointer: - case CastExpr::CK_PointerToIntegral: - case CastExpr::CK_IntegralToFloating: - case CastExpr::CK_FloatingToIntegral: - case CastExpr::CK_FloatingCast: - case CastExpr::CK_AnyPointerToObjCPointerCast: - case CastExpr::CK_AnyPointerToBlockPointerCast: - case CastExpr::CK_DerivedToBase: - // Delegate to SValuator to process. - for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { - ExplodedNode* N = *I; - const GRState* state = GetState(N); - SVal V = state->getSVal(Ex); - const SValuator::CastResult &Res = SVator.EvalCast(V, state, T, ExTy); - state = Res.getState()->BindExpr(CastE, Res.getSVal()); - MakeNode(Dst, CastE, N, state); - } - return; - - default: - llvm::errs() << "Cast kind " << CastE->getCastKind() << " not handled.\n"; - assert(0); - } -} - -void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, - bool asLValue) { - InitListExpr* ILE = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); - ExplodedNodeSet Tmp; - Visit(ILE, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { - const GRState* state = GetState(*I); - SVal ILV = state->getSVal(ILE); - const LocationContext *LC = (*I)->getLocationContext(); - state = state->bindCompoundLiteral(CL, LC, ILV); - - if (asLValue) { - MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); - } - else - MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); - } -} - -void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, - ExplodedNodeSet& Dst) { - - // The CFG has one DeclStmt per Decl. - Decl* D = *DS->decl_begin(); - - if (!D || !isa<VarDecl>(D)) - return; - - const VarDecl* VD = dyn_cast<VarDecl>(D); - Expr* InitEx = const_cast<Expr*>(VD->getInit()); - - // FIXME: static variables may have an initializer, but the second - // time a function is called those values may not be current. - ExplodedNodeSet Tmp; - - if (InitEx) { - if (VD->getType()->isReferenceType()) - VisitLValue(InitEx, Pred, Tmp); - else - Visit(InitEx, Pred, Tmp); - } - else - Tmp.Add(Pred); - - ExplodedNodeSet Tmp2; - CheckerVisit(DS, Tmp2, Tmp, true); - - for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { - ExplodedNode *N = *I; - const GRState *state = GetState(N); - - // Decls without InitExpr are not initialized explicitly. - const LocationContext *LC = N->getLocationContext(); - - if (InitEx) { - SVal InitVal = state->getSVal(InitEx); - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if (InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) { - InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); - } - - EvalBind(Dst, DS, DS, *I, state, - loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); - } - else { - state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); - MakeNode(Dst, DS, *I, state); - } - } -} - -void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S, - ExplodedNode *Pred, ExplodedNodeSet& Dst) { - - Expr* InitEx = VD->getInit(); - ExplodedNodeSet Tmp; - Visit(InitEx, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - ExplodedNode *N = *I; - const GRState *state = GetState(N); - - const LocationContext *LC = N->getLocationContext(); - SVal InitVal = state->getSVal(InitEx); - - // Recover some path-sensitivity if a scalar value evaluated to - // UnknownVal. - if (InitVal.isUnknown() || - !getConstraintManager().canReasonAbout(InitVal)) { - InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, - Builder->getCurrentBlockCount()); - } - - EvalBind(Dst, S, S, N, state, - loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); - } -} - -namespace { - // This class is used by VisitInitListExpr as an item in a worklist - // for processing the values contained in an InitListExpr. -class InitListWLItem { -public: - llvm::ImmutableList<SVal> Vals; - ExplodedNode* N; - InitListExpr::reverse_iterator Itr; - - InitListWLItem(ExplodedNode* n, llvm::ImmutableList<SVal> vals, - InitListExpr::reverse_iterator itr) - : Vals(vals), N(n), Itr(itr) {} -}; -} - - -void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - - const GRState* state = GetState(Pred); - QualType T = getContext().getCanonicalType(E->getType()); - unsigned NumInitElements = E->getNumInits(); - - if (T->isArrayType() || T->isStructureType() || - T->isUnionType() || T->isVectorType()) { - - llvm::ImmutableList<SVal> StartVals = getBasicVals().getEmptySValList(); - - // Handle base case where the initializer has no elements. - // e.g: static int* myArray[] = {}; - if (NumInitElements == 0) { - SVal V = ValMgr.makeCompoundVal(T, StartVals); - MakeNode(Dst, E, Pred, state->BindExpr(E, V)); - return; - } - - // Create a worklist to process the initializers. - llvm::SmallVector<InitListWLItem, 10> WorkList; - WorkList.reserve(NumInitElements); - WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); - InitListExpr::reverse_iterator ItrEnd = E->rend(); - assert(!(E->rbegin() == E->rend())); - - // Process the worklist until it is empty. - while (!WorkList.empty()) { - InitListWLItem X = WorkList.back(); - WorkList.pop_back(); - - ExplodedNodeSet Tmp; - Visit(*X.Itr, X.N, Tmp); - - InitListExpr::reverse_iterator NewItr = X.Itr + 1; - - for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { - // Get the last initializer value. - state = GetState(*NI); - SVal InitV = state->getSVal(cast<Expr>(*X.Itr)); - - // Construct the new list of values by prepending the new value to - // the already constructed list. - llvm::ImmutableList<SVal> NewVals = - getBasicVals().consVals(InitV, X.Vals); - - if (NewItr == ItrEnd) { - // Now we have a list holding all init values. Make CompoundValData. - SVal V = ValMgr.makeCompoundVal(T, NewVals); - - // Make final state and node. - MakeNode(Dst, E, *NI, state->BindExpr(E, V)); - } - else { - // Still some initializer values to go. Push them onto the worklist. - WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); - } - } - } - - return; - } - - if (Loc::IsLocType(T) || T->isIntegerType()) { - assert (E->getNumInits() == 1); - ExplodedNodeSet Tmp; - Expr* Init = E->getInit(0); - Visit(Init, Pred, Tmp); - for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { - state = GetState(*I); - MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init))); - } - return; - } - - assert(0 && "unprocessed InitListExpr type"); -} - -/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). -void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, - ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - QualType T = Ex->getTypeOfArgument(); - CharUnits amt; - - if (Ex->isSizeOf()) { - if (T == getContext().VoidTy) { - // sizeof(void) == 1 byte. - amt = CharUnits::One(); - } - else if (!T.getTypePtr()->isConstantSizeType()) { - // FIXME: Add support for VLAs. - return; - } - else if (T->isObjCInterfaceType()) { - // Some code tries to take the sizeof an ObjCInterfaceType, relying that - // the compiler has laid out its representation. Just report Unknown - // for these. - return; - } - else { - // All other cases. - amt = getContext().getTypeSizeInChars(T); - } - } - else // Get alignment of the type. - amt = CharUnits::fromQuantity(getContext().getTypeAlign(T) / 8); - - MakeNode(Dst, Ex, Pred, - GetState(Pred)->BindExpr(Ex, - ValMgr.makeIntVal(amt.getQuantity(), Ex->getType()))); -} - - -void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - switch (U->getOpcode()) { - - default: - break; - - case UnaryOperator::Deref: { - - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - - const GRState* state = GetState(*I); - SVal location = state->getSVal(Ex); - - if (asLValue) - MakeNode(Dst, U, *I, state->BindExpr(U, location), - ProgramPoint::PostLValueKind); - else - EvalLoad(Dst, U, *I, state, location); - } - - return; - } - - case UnaryOperator::Real: { - - 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, UnaryOperator::Real is an identity operation. - assert (U->getType() == Ex->getType()); - const GRState* state = GetState(*I); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; - } - - case UnaryOperator::Imag: { - - 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, UnaryOperator::Float returns 0. - assert (Ex->getType()->isIntegerType()); - const GRState* state = GetState(*I); - SVal X = ValMgr.makeZeroVal(Ex->getType()); - MakeNode(Dst, U, *I, state->BindExpr(U, X)); - } - - return; - } - - case UnaryOperator::OffsetOf: { - Expr::EvalResult Res; - if (U->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); - assert(IV.getBitWidth() == getContext().getTypeSize(U->getType())); - assert(U->getType()->isIntegerType()); - assert(IV.isSigned() == U->getType()->isSignedIntegerType()); - SVal X = ValMgr.makeIntVal(IV); - MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X)); - return; - } - // FIXME: Handle the case where __builtin_offsetof is not a constant. - Dst.Add(Pred); - return; - } - - case UnaryOperator::Plus: assert (!asLValue); // FALL-THROUGH. - case UnaryOperator::Extension: { - - // 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. - - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); - } - - return; - } - - case UnaryOperator::AddrOf: { - - assert(!asLValue); - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - VisitLValue(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - SVal V = state->getSVal(Ex); - state = state->BindExpr(U, V); - MakeNode(Dst, U, *I, state); - } - - return; - } - - case UnaryOperator::LNot: - case UnaryOperator::Minus: - case UnaryOperator::Not: { - - assert (!asLValue); - Expr* Ex = U->getSubExpr()->IgnoreParens(); - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - - // Get the value of the subexpression. - SVal V = state->getSVal(Ex); - - if (V.isUnknownOrUndef()) { - MakeNode(Dst, U, *I, state->BindExpr(U, V)); - continue; - } - -// QualType DstT = getContext().getCanonicalType(U->getType()); -// QualType SrcT = getContext().getCanonicalType(Ex->getType()); -// -// if (DstT != SrcT) // Perform promotions. -// V = EvalCast(V, DstT); -// -// if (V.isUnknownOrUndef()) { -// MakeNode(Dst, U, *I, BindExpr(St, U, V)); -// continue; -// } - - switch (U->getOpcode()) { - default: - assert(false && "Invalid Opcode."); - break; - - case UnaryOperator::Not: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, EvalComplement(cast<NonLoc>(V))); - break; - - case UnaryOperator::Minus: - // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, EvalMinus(cast<NonLoc>(V))); - break; - - case UnaryOperator::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 = ValMgr.makeNull(); - Result = EvalBinOp(state, BinaryOperator::EQ, cast<Loc>(V), X, - U->getType()); - } - else { - nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = EvalBinOp(state, BinaryOperator::EQ, cast<NonLoc>(V), X, - U->getType()); - } - - state = state->BindExpr(U, Result); - - break; - } - - MakeNode(Dst, U, *I, state); - } - - return; - } - } - - // Handle ++ and -- (both pre- and post-increment). - - assert (U->isIncrementDecrementOp()); - ExplodedNodeSet Tmp; - Expr* Ex = U->getSubExpr()->IgnoreParens(); - VisitLValue(Ex, Pred, Tmp); - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { - - const GRState* state = GetState(*I); - SVal V1 = state->getSVal(Ex); - - // Perform a load. - ExplodedNodeSet Tmp2; - EvalLoad(Tmp2, Ex, *I, state, V1); - - for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { - - state = GetState(*I2); - 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() ? BinaryOperator::Add - : BinaryOperator::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 = ValMgr.makeIntValWithPtrWidth(1, false); - else - RHS = ValMgr.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 = - ValMgr.getConjuredSymbolVal(NULL, Ex, - Builder->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 = - SVator.EvalEQ(state, V2, ValMgr.makeZeroVal(U->getType())); - - if (!state->Assume(Constraint, true)) { - // It isn't feasible for the original value to be null. - // Propagate this constraint. - Constraint = SVator.EvalEQ(state, SymVal, - ValMgr.makeZeroVal(U->getType())); - - - state = state->Assume(Constraint, false); - assert(state); - } - } - } - - state = state->BindExpr(U, U->isPostfix() ? V2 : Result); - - // Perform the store. - EvalStore(Dst, NULL, U, *I2, state, V1, Result); - } - } -} - - -void GRExprEngine::VisitCXXThisExpr(CXXThisExpr *TE, ExplodedNode *Pred, - ExplodedNodeSet & Dst) { - // Get the this object region from StoreManager. - const MemRegion *R = - ValMgr.getRegionManager().getCXXThisRegion(TE->getType(), - Pred->getLocationContext()); - - const GRState *state = GetState(Pred); - SVal V = state->getSVal(loc::MemRegionVal(R)); - MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); -} - -void GRExprEngine::VisitAsmStmt(AsmStmt* A, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { - VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); -} - -void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A, - AsmStmt::outputs_iterator I, - AsmStmt::outputs_iterator E, - ExplodedNode* Pred, ExplodedNodeSet& Dst) { - if (I == E) { - VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); - return; - } - - ExplodedNodeSet Tmp; - VisitLValue(*I, Pred, Tmp); - - ++I; - - for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) - VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); -} - -void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, - AsmStmt::inputs_iterator I, - AsmStmt::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. - - const GRState* state = GetState(Pred); - - for (AsmStmt::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()); - } - - MakeNode(Dst, A, Pred, state); - return; - } - - 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); -} - -void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - - ExplodedNodeSet Src; - if (Expr *RetE = RS->getRetValue()) { - Visit(RetE, Pred, Src); - } - else { - Src.Add(Pred); - } - - ExplodedNodeSet CheckedSet; - CheckerVisit(RS, CheckedSet, Src, true); - - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - - assert(Builder && "GRStmtNodeBuilder must be defined."); - - Pred = *I; - unsigned size = Dst.size(); - - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - SaveOr OldHasGen(Builder->HasGeneratedNode); - - getTF().EvalReturn(Dst, *this, *Builder, RS, Pred); - - // Handle the case where no nodes where generated. - if (!Builder->BuildSinks && Dst.size() == size && - !Builder->HasGeneratedNode) - MakeNode(Dst, RS, Pred, GetState(Pred)); - } -} - -//===----------------------------------------------------------------------===// -// Transfer functions: Binary operators. -//===----------------------------------------------------------------------===// - -void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, - ExplodedNode* Pred, - ExplodedNodeSet& Dst, bool asLValue) { - - ExplodedNodeSet Tmp1; - Expr* LHS = B->getLHS()->IgnoreParens(); - Expr* RHS = B->getRHS()->IgnoreParens(); - - // FIXME: Add proper support for ObjCImplicitSetterGetterRefExpr. - if (isa<ObjCImplicitSetterGetterRefExpr>(LHS)) { - Visit(RHS, Pred, Dst); - return; - } - - if (B->isAssignmentOp()) - VisitLValue(LHS, Pred, Tmp1); - else - Visit(LHS, Pred, Tmp1); - - ExplodedNodeSet Tmp3; - - for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { - SVal LeftV = (*I1)->getState()->getSVal(LHS); - ExplodedNodeSet Tmp2; - Visit(RHS, *I1, Tmp2); - - ExplodedNodeSet CheckedSet; - CheckerVisit(B, CheckedSet, Tmp2, true); - - // With both the LHS and RHS evaluated, process the operation itself. - - for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end(); - I2 != E2; ++I2) { - - const GRState *state = GetState(*I2); - const GRState *OldSt = state; - SVal RightV = state->getSVal(RHS); - - BinaryOperator::Opcode Op = B->getOpcode(); - - if (Op == BinaryOperator::Assign) { - // EXPERIMENTAL: "Conjured" symbols. - // FIXME: Handle structs. - QualType T = RHS->getType(); - - if ((RightV.isUnknown()||!getConstraintManager().canReasonAbout(RightV)) - && (Loc::IsLocType(T) || (T->isScalarType()&&T->isIntegerType()))) { - unsigned Count = Builder->getCurrentBlockCount(); - RightV = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), Count); - } - - SVal ExprVal = asLValue ? LeftV : RightV; - - // Simulate the effects of a "store": bind the value of the RHS - // to the L-Value represented by the LHS. - EvalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV); - continue; - } - - if (!B->isAssignmentOp()) { - // 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()) { - if (OldSt != state) { - // Generate a new node if we have already created a new state. - MakeNode(Tmp3, B, *I2, state); - } - else - Tmp3.Add(*I2); - - continue; - } - - state = state->BindExpr(B, Result); - - MakeNode(Tmp3, B, *I2, state); - continue; - } - - assert (B->isCompoundAssignmentOp()); - - switch (Op) { - default: - assert(0 && "Invalid opcode for compound assignment."); - case BinaryOperator::MulAssign: Op = BinaryOperator::Mul; break; - case BinaryOperator::DivAssign: Op = BinaryOperator::Div; break; - case BinaryOperator::RemAssign: Op = BinaryOperator::Rem; break; - case BinaryOperator::AddAssign: Op = BinaryOperator::Add; break; - case BinaryOperator::SubAssign: Op = BinaryOperator::Sub; break; - case BinaryOperator::ShlAssign: Op = BinaryOperator::Shl; break; - case BinaryOperator::ShrAssign: Op = BinaryOperator::Shr; break; - case BinaryOperator::AndAssign: Op = BinaryOperator::And; break; - case BinaryOperator::XorAssign: Op = BinaryOperator::Xor; break; - case BinaryOperator::OrAssign: Op = BinaryOperator::Or; break; - } - - // Perform a load (the LHS). This performs the checks for - // null dereferences, and so on. - ExplodedNodeSet Tmp4; - SVal location = state->getSVal(LHS); - EvalLoad(Tmp4, LHS, *I2, state, location); - - for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4; - ++I4) { - state = GetState(*I4); - SVal V = state->getSVal(LHS); - - // Get the computation type. - QualType CTy = - cast<CompoundAssignOperator>(B)->getComputationResultType(); - CTy = getContext().getCanonicalType(CTy); - - QualType CLHSTy = - cast<CompoundAssignOperator>(B)->getComputationLHSType(); - CLHSTy = getContext().getCanonicalType(CLHSTy); - - QualType LTy = getContext().getCanonicalType(LHS->getType()); - QualType RTy = getContext().getCanonicalType(RHS->getType()); - - // Promote LHS. - llvm::tie(state, V) = SVator.EvalCast(V, state, CLHSTy, LTy); - - // Compute the result of the operation. - SVal Result; - llvm::tie(state, Result) = SVator.EvalCast(EvalBinOp(state, Op, V, - RightV, CTy), - state, B->getType(), CTy); - - // EXPERIMENTAL: "Conjured" symbols. - // FIXME: Handle structs. - - SVal LHSVal; - - if ((Result.isUnknown() || - !getConstraintManager().canReasonAbout(Result)) - && (Loc::IsLocType(CTy) - || (CTy->isScalarType() && CTy->isIntegerType()))) { - - unsigned Count = Builder->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 = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count); - - // However, we need to convert the symbol to the computation type. - llvm::tie(state, Result) = SVator.EvalCast(LHSVal, state, CTy, LTy); - } - else { - // The left-hand side may bind to a different value then the - // computation type. - llvm::tie(state, LHSVal) = SVator.EvalCast(Result, state, LTy, CTy); - } - - EvalStore(Tmp3, B, LHS, *I4, state->BindExpr(B, Result), - location, LHSVal); - } - } - } - - CheckerVisit(B, Dst, Tmp3, false); -} - -void GRExprEngine::CreateCXXTemporaryObject(Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - ExplodedNodeSet Tmp; - Visit(Ex, Pred, Tmp); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - const GRState *state = GetState(*I); - - // Bind the temporary object to the value of the expression. Then bind - // the expression to the location of the object. - SVal V = state->getSVal(Ex); - - const MemRegion *R = - ValMgr.getRegionManager().getCXXObjectRegion(Ex, - Pred->getLocationContext()); - - state = state->bindLoc(loc::MemRegionVal(R), V); - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, loc::MemRegionVal(R))); - } -} - -//===----------------------------------------------------------------------===// -// Checker registration/lookup. -//===----------------------------------------------------------------------===// - -Checker *GRExprEngine::lookupChecker(void *tag) const { - CheckerMap::const_iterator I = CheckerM.find(tag); - return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; -} - -//===----------------------------------------------------------------------===// -// Visualization. -//===----------------------------------------------------------------------===// - -#ifndef NDEBUG -static GRExprEngine* GraphPrintCheckerState; -static SourceManager* GraphPrintSourceManager; - -namespace llvm { -template<> -struct DOTGraphTraits<ExplodedNode*> : - public DefaultDOTGraphTraits { - - DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} - - // FIXME: Since we do not cache error nodes in GRExprEngine now, this does not - // work. - static std::string getNodeAttributes(const ExplodedNode* N, void*) { - -#if 0 - // FIXME: Replace with a general scheme to tell if the node is - // an error node. - if (GraphPrintCheckerState->isImplicitNullDeref(N) || - GraphPrintCheckerState->isExplicitNullDeref(N) || - GraphPrintCheckerState->isUndefDeref(N) || - GraphPrintCheckerState->isUndefStore(N) || - GraphPrintCheckerState->isUndefControlFlow(N) || - GraphPrintCheckerState->isUndefResult(N) || - GraphPrintCheckerState->isBadCall(N) || - GraphPrintCheckerState->isUndefArg(N)) - return "color=\"red\",style=\"filled\""; - - if (GraphPrintCheckerState->isNoReturnCall(N)) - return "color=\"blue\",style=\"filled\""; -#endif - return ""; - } - - static std::string getNodeLabel(const ExplodedNode* N, void*){ - - std::string sbuf; - llvm::raw_string_ostream Out(sbuf); - - // Program Location. - ProgramPoint Loc = N->getLocation(); - - switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: - Out << "Block Entrance: B" - << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); - break; - - case ProgramPoint::BlockExitKind: - assert (false); - break; - - default: { - if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { - const Stmt* S = L->getStmt(); - SourceLocation SLoc = S->getLocStart(); - - Out << S->getStmtClassName() << ' ' << (void*) S << ' '; - LangOptions LO; // FIXME. - S->printPretty(Out, 0, PrintingPolicy(LO)); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) - << "\\l"; - } - - if (isa<PreStmt>(Loc)) - Out << "\\lPreStmt\\l;"; - else if (isa<PostLoad>(Loc)) - Out << "\\lPostLoad\\l;"; - else if (isa<PostStore>(Loc)) - Out << "\\lPostStore\\l"; - else if (isa<PostLValue>(Loc)) - Out << "\\lPostLValue\\l"; - -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isImplicitNullDeref(N)) - Out << "\\|Implicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isExplicitNullDeref(N)) - Out << "\\|Explicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isUndefDeref(N)) - Out << "\\|Dereference of undefialied value.\\l"; - else if (GraphPrintCheckerState->isUndefStore(N)) - Out << "\\|Store to Undefined Loc."; - else if (GraphPrintCheckerState->isUndefResult(N)) - Out << "\\|Result of operation is undefined."; - else if (GraphPrintCheckerState->isNoReturnCall(N)) - Out << "\\|Call to function marked \"noreturn\"."; - else if (GraphPrintCheckerState->isBadCall(N)) - Out << "\\|Call to NULL/Undefined."; - else if (GraphPrintCheckerState->isUndefArg(N)) - Out << "\\|Argument in call is undefined"; -#endif - - break; - } - - const BlockEdge& E = cast<BlockEdge>(Loc); - Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" - << E.getDst()->getBlockID() << ')'; - - if (Stmt* T = E.getSrc()->getTerminator()) { - - SourceLocation SLoc = T->getLocStart(); - - Out << "\\|Terminator: "; - LangOptions LO; // FIXME. - E.getSrc()->printTerminator(Out, LO); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc); - } - - if (isa<SwitchStmt>(T)) { - Stmt* Label = E.getDst()->getLabel(); - - if (Label) { - if (CaseStmt* C = dyn_cast<CaseStmt>(Label)) { - Out << "\\lcase "; - LangOptions LO; // FIXME. - C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); - - if (Stmt* RHS = C->getRHS()) { - Out << " .. "; - RHS->printPretty(Out, 0, PrintingPolicy(LO)); - } - - Out << ":"; - } - else { - assert (isa<DefaultStmt>(Label)); - Out << "\\ldefault:"; - } - } - else - Out << "\\l(implicit) default:"; - } - else if (isa<IndirectGotoStmt>(T)) { - // FIXME - } - else { - Out << "\\lCondition: "; - if (*E.getSrc()->succ_begin() == E.getDst()) - Out << "true"; - else - Out << "false"; - } - - Out << "\\l"; - } - -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isUndefControlFlow(N)) { - Out << "\\|Control-flow based on\\lUndefined value.\\l"; - } -#endif - } - } - - Out << "\\|StateID: " << (void*) N->getState() << "\\|"; - - const GRState *state = N->getState(); - state->printDOT(Out); - - Out << "\\l"; - return Out.str(); - } -}; -} // end llvm namespace -#endif - -#ifndef NDEBUG -template <typename ITERATOR> -ExplodedNode* GetGraphNode(ITERATOR I) { return *I; } - -template <> ExplodedNode* -GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator> - (llvm::DenseMap<ExplodedNode*, Expr*>::iterator I) { - return I->first; -} -#endif - -void GRExprEngine::ViewGraph(bool trim) { -#ifndef NDEBUG - if (trim) { - std::vector<ExplodedNode*> Src; - - // Flush any outstanding reports to make sure we cover all the nodes. - // This does not cause them to get displayed. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) - const_cast<BugType*>(*I)->FlushReports(BR); - - // Iterate through the reports and get their nodes. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) { - for (BugType::const_iterator I2=(*I)->begin(), E2=(*I)->end(); - I2!=E2; ++I2) { - const BugReportEquivClass& EQ = *I2; - const BugReport &R = **EQ.begin(); - ExplodedNode *N = const_cast<ExplodedNode*>(R.getEndNode()); - if (N) Src.push_back(N); - } - } - - ViewGraph(&Src[0], &Src[0]+Src.size()); - } - else { - GraphPrintCheckerState = this; - GraphPrintSourceManager = &getContext().getSourceManager(); - - llvm::ViewGraph(*G.roots_begin(), "GRExprEngine"); - - GraphPrintCheckerState = NULL; - GraphPrintSourceManager = NULL; - } -#endif -} - -void GRExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { -#ifndef NDEBUG - GraphPrintCheckerState = this; - GraphPrintSourceManager = &getContext().getSourceManager(); - - std::auto_ptr<ExplodedGraph> TrimmedG(G.Trim(Beg, End).first); - - if (!TrimmedG.get()) - llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; - else - llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedGRExprEngine"); - - GraphPrintCheckerState = NULL; - GraphPrintSourceManager = NULL; -#endif -} diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.cpp b/lib/Analysis/GRExprEngineExperimentalChecks.cpp deleted file mode 100644 index 33479b0..0000000 --- a/lib/Analysis/GRExprEngineExperimentalChecks.cpp +++ /dev/null @@ -1,40 +0,0 @@ -//=-- GRExprEngineExperimentalChecks.h ------------------------------*- 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 functions to instantiate and register experimental -// checks in GRExprEngine. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "GRExprEngineExperimentalChecks.h" -#include "clang/Analysis/LocalCheckers.h" - -using namespace clang; - -void clang::RegisterExperimentalChecks(GRExprEngine &Eng) { - // These are checks that never belong as internal checks - // within GRExprEngine. - RegisterPthreadLockChecker(Eng); - RegisterMallocChecker(Eng); -} - -void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { - // These are internal checks that should eventually migrate to - // RegisterInternalChecks() once they have been further tested. - - // Note that this must be registered after ReturnStackAddresEngsChecker. - RegisterReturnPointerRangeChecker(Eng); - - RegisterFixedAddressChecker(Eng); - RegisterPointerSubChecker(Eng); - RegisterPointerArithChecker(Eng); - RegisterCastToStructChecker(Eng); - RegisterArrayBoundChecker(Eng); -} diff --git a/lib/Analysis/GRExprEngineExperimentalChecks.h b/lib/Analysis/GRExprEngineExperimentalChecks.h deleted file mode 100644 index 9a9da32..0000000 --- a/lib/Analysis/GRExprEngineExperimentalChecks.h +++ /dev/null @@ -1,26 +0,0 @@ -//=-- GRExprEngineExperimentalChecks.h ------------------------------*- 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 functions to instantiate and register experimental -// checks in GRExprEngine. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS -#define LLVM_CLANG_GREXPRENGINE_EXPERIMENTAL_CHECKS - -namespace clang { - -class GRExprEngine; - -void RegisterPthreadLockChecker(GRExprEngine &Eng); -void RegisterMallocChecker(GRExprEngine &Eng); - -} // end clang namespace -#endif diff --git a/lib/Analysis/GRExprEngineInternalChecks.h b/lib/Analysis/GRExprEngineInternalChecks.h deleted file mode 100644 index e2354ed..0000000 --- a/lib/Analysis/GRExprEngineInternalChecks.h +++ /dev/null @@ -1,44 +0,0 @@ -//=-- GRExprEngineInternalChecks.h- Builtin GRExprEngine Checks -----*- C++ -*-= -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines functions to instantiate and register the "built-in" -// checks in GRExprEngine. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS -#define LLVM_CLANG_GREXPRENGINE_INTERNAL_CHECKS - -namespace clang { - -class GRExprEngine; - -void RegisterAttrNonNullChecker(GRExprEngine &Eng); -void RegisterDereferenceChecker(GRExprEngine &Eng); -void RegisterDivZeroChecker(GRExprEngine &Eng); -void RegisterReturnPointerRangeChecker(GRExprEngine &Eng); -void RegisterReturnStackAddressChecker(GRExprEngine &Eng); -void RegisterReturnUndefChecker(GRExprEngine &Eng); -void RegisterVLASizeChecker(GRExprEngine &Eng); -void RegisterPointerSubChecker(GRExprEngine &Eng); -void RegisterPointerArithChecker(GRExprEngine &Eng); -void RegisterFixedAddressChecker(GRExprEngine &Eng); -void RegisterCastToStructChecker(GRExprEngine &Eng); -void RegisterCallAndMessageChecker(GRExprEngine &Eng); -void RegisterArrayBoundChecker(GRExprEngine &Eng); -void RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng); -void RegisterUndefinedAssignmentChecker(GRExprEngine &Eng); -void RegisterUndefBranchChecker(GRExprEngine &Eng); -void RegisterUndefResultChecker(GRExprEngine &Eng); - -void RegisterNoReturnFunctionChecker(GRExprEngine &Eng); -void RegisterBuiltinFunctionChecker(GRExprEngine &Eng); -void RegisterOSAtomicChecker(GRExprEngine &Eng); -} // end clang namespace -#endif diff --git a/lib/Analysis/GRState.cpp b/lib/Analysis/GRState.cpp deleted file mode 100644 index 051d465..0000000 --- a/lib/Analysis/GRState.cpp +++ /dev/null @@ -1,358 +0,0 @@ -//= GRState.cpp - Path-Sensitive "State" for tracking values -----*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements GRState and GRStateManager. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -// Give the vtable for ConstraintManager somewhere to live. -// FIXME: Move this elsewhere. -ConstraintManager::~ConstraintManager() {} - -GRStateManager::~GRStateManager() { - for (std::vector<GRState::Printer*>::iterator I=Printers.begin(), - E=Printers.end(); I!=E; ++I) - delete *I; - - for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); - I!=E; ++I) - I->second.second(I->second.first); -} - -const GRState* -GRStateManager::RemoveDeadBindings(const GRState* state, Stmt* Loc, - SymbolReaper& SymReaper) { - - // This code essentially performs a "mark-and-sweep" of the VariableBindings. - // The roots are any Block-level exprs and Decls that our liveness algorithm - // tells us are live. We then see what Decls they may reference, and keep - // those around. This code more than likely can be made faster, and the - // frequency of which this method is called should be experimented with - // for optimum performance. - llvm::SmallVector<const MemRegion*, 10> RegionRoots; - GRState NewState = *state; - - NewState.Env = EnvMgr.RemoveDeadBindings(NewState.Env, Loc, SymReaper, - state, RegionRoots); - - // Clean up the store. - StoreMgr->RemoveDeadBindings(NewState, Loc, SymReaper, RegionRoots); - - return ConstraintMgr->RemoveDeadBindings(getPersistentState(NewState), - SymReaper); -} - -const GRState *GRState::unbindLoc(Loc LV) const { - Store OldStore = getStore(); - Store NewStore = getStateManager().StoreMgr->Remove(OldStore, LV); - - if (NewStore == OldStore) - return this; - - GRState NewSt = *this; - NewSt.St = NewStore; - return getStateManager().getPersistentState(NewSt); -} - -SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { - // We only want to do fetches from regions that we can actually bind - // values. For example, SymbolicRegions of type 'id<...>' cannot - // have direct bindings (but their can be bindings on their subregions). - if (!R->isBoundable()) - return UnknownVal(); - - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - QualType T = TR->getValueType(getStateManager().getContext()); - if (Loc::IsLocType(T) || T->isIntegerType()) - return getSVal(R); - } - - return UnknownVal(); -} - - -const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{ - Environment NewEnv = getStateManager().EnvMgr.BindExpr(Env, Ex, V, - Invalidate); - if (NewEnv == Env) - return this; - - GRState NewSt = *this; - NewSt.Env = NewEnv; - return getStateManager().getPersistentState(NewSt); -} - -const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { - GRState State(this, - EnvMgr.getInitialEnvironment(InitLoc->getAnalysisContext()), - StoreMgr->getInitialStore(InitLoc), - GDMFactory.GetEmptyMap()); - - return getPersistentState(State); -} - -const GRState* GRStateManager::getPersistentState(GRState& State) { - - llvm::FoldingSetNodeID ID; - State.Profile(ID); - void* InsertPos; - - if (GRState* I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) - return I; - - GRState* I = (GRState*) Alloc.Allocate<GRState>(); - new (I) GRState(State); - StateSet.InsertNode(I, InsertPos); - return I; -} - -const GRState* GRState::makeWithStore(Store store) const { - GRState NewSt = *this; - NewSt.St = store; - return getStateManager().getPersistentState(NewSt); -} - -//===----------------------------------------------------------------------===// -// State pretty-printing. -//===----------------------------------------------------------------------===// - -void GRState::print(llvm::raw_ostream& Out, const char* nl, - const char* sep) const { - // Print the store. - GRStateManager &Mgr = getStateManager(); - Mgr.getStoreManager().print(getStore(), Out, nl, sep); - - CFG &C = *getAnalysisContext().getCFG(); - - // Print Subexpression bindings. - bool isFirst = true; - - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (C.isBlkExpr(I.getKey())) - continue; - - if (isFirst) { - Out << nl << nl << "Sub-Expressions:" << nl; - isFirst = false; - } - else { Out << nl; } - - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } - - // Print block-expression bindings. - isFirst = true; - - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (!C.isBlkExpr(I.getKey())) - continue; - - if (isFirst) { - Out << nl << nl << "Block-level Expressions:" << nl; - isFirst = false; - } - else { Out << nl; } - - Out << " (" << (void*) I.getKey() << ") "; - LangOptions LO; // FIXME. - I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } - - Mgr.getConstraintManager().print(this, Out, nl, sep); - - // Print checker-specific data. - for (std::vector<Printer*>::iterator I = Mgr.Printers.begin(), - E = Mgr.Printers.end(); I != E; ++I) { - (*I)->Print(Out, this, nl, sep); - } -} - -void GRState::printDOT(llvm::raw_ostream& Out) const { - print(Out, "\\l", "\\|"); -} - -void GRState::printStdErr() const { - print(llvm::errs()); -} - -//===----------------------------------------------------------------------===// -// Generic Data Map. -//===----------------------------------------------------------------------===// - -void* const* GRState::FindGDM(void* K) const { - return GDM.lookup(K); -} - -void* -GRStateManager::FindGDMContext(void* K, - void* (*CreateContext)(llvm::BumpPtrAllocator&), - void (*DeleteContext)(void*)) { - - std::pair<void*, void (*)(void*)>& p = GDMContexts[K]; - if (!p.first) { - p.first = CreateContext(Alloc); - p.second = DeleteContext; - } - - return p.first; -} - -const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){ - GRState::GenericDataMap M1 = St->getGDM(); - GRState::GenericDataMap M2 = GDMFactory.Add(M1, Key, Data); - - if (M1 == M2) - return St; - - GRState NewSt = *St; - NewSt.GDM = M2; - return getPersistentState(NewSt); -} - -//===----------------------------------------------------------------------===// -// Utility. -//===----------------------------------------------------------------------===// - -namespace { -class ScanReachableSymbols : public SubRegionMap::Visitor { - typedef llvm::DenseSet<const MemRegion*> VisitedRegionsTy; - - VisitedRegionsTy visited; - const GRState *state; - SymbolVisitor &visitor; - llvm::OwningPtr<SubRegionMap> SRM; -public: - - ScanReachableSymbols(const GRState *st, SymbolVisitor& v) - : state(st), visitor(v) {} - - bool scan(nonloc::CompoundVal val); - bool scan(SVal val); - bool scan(const MemRegion *R); - - // From SubRegionMap::Visitor. - bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) { - return scan(SubRegion); - } -}; -} - -bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { - for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) - if (!scan(*I)) - return false; - - return true; -} - -bool ScanReachableSymbols::scan(SVal val) { - if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val)) - return scan(X->getRegion()); - - if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val)) - return scan(X->getLoc()); - - if (SymbolRef Sym = val.getAsSymbol()) - return visitor.VisitSymbol(Sym); - - if (nonloc::CompoundVal *X = dyn_cast<nonloc::CompoundVal>(&val)) - return scan(*X); - - return true; -} - -bool ScanReachableSymbols::scan(const MemRegion *R) { - if (isa<MemSpaceRegion>(R) || visited.count(R)) - return true; - - visited.insert(R); - - // If this is a symbolic region, visit the symbol for the region. - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) - if (!visitor.VisitSymbol(SR->getSymbol())) - return false; - - // If this is a subregion, also visit the parent regions. - if (const SubRegion *SR = dyn_cast<SubRegion>(R)) - if (!scan(SR->getSuperRegion())) - return false; - - // Now look at the binding to this region (if any). - if (!scan(state->getSValAsScalarOrLoc(R))) - return false; - - // Now look at the subregions. - if (!SRM.get()) - SRM.reset(state->getStateManager().getStoreManager().getSubRegionMap(state)); - - return SRM->iterSubRegions(R, *this); -} - -bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { - ScanReachableSymbols S(this, visitor); - return S.scan(val); -} - -bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, - SymbolVisitor &visitor) const { - ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { - if (!S.scan(*I)) - return false; - } - return true; -} - -bool GRState::scanReachableSymbols(const MemRegion * const *I, - const MemRegion * const *E, - SymbolVisitor &visitor) const { - ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { - if (!S.scan(*I)) - return false; - } - return true; -} - -//===----------------------------------------------------------------------===// -// Queries. -//===----------------------------------------------------------------------===// - -bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, - const llvm::APSInt& Y) { - - SVal V = state->getSVal(Ex); - - if (loc::ConcreteInt* X = dyn_cast<loc::ConcreteInt>(&V)) - return X->getValue() == Y; - - if (nonloc::ConcreteInt* X = dyn_cast<nonloc::ConcreteInt>(&V)) - return X->getValue() == Y; - - if (SymbolRef Sym = V.getAsSymbol()) - return ConstraintMgr->isEqual(state, Sym, Y); - - return false; -} - -bool GRStateManager::isEqual(const GRState* state, const Expr* Ex, uint64_t x) { - return isEqual(state, Ex, getBasicVals().getValue(x, Ex->getType())); -} diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp index 0b2620e..94ed752 100644 --- a/lib/Analysis/LiveVariables.cpp +++ b/lib/Analysis/LiveVariables.cpp @@ -19,7 +19,7 @@ #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" #include "clang/Analysis/Support/SaveAndRestore.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" +#include "clang/Analysis/AnalysisContext.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/Analysis/Makefile b/lib/Analysis/Makefile index c597254..d641112 100644 --- a/lib/Analysis/Makefile +++ b/lib/Analysis/Makefile @@ -14,7 +14,6 @@ LEVEL = ../../../.. LIBRARYNAME := clangAnalysis BUILD_ARCHIVE = 1 -CXXFLAGS = -fno-rtti CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp deleted file mode 100644 index 28f4db7..0000000 --- a/lib/Analysis/MallocChecker.cpp +++ /dev/null @@ -1,336 +0,0 @@ -//=== MallocChecker.cpp - A malloc/free checker -------------------*- 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 malloc/free checker, which checks for potential memory -// leaks, double free, and use-after-free problems. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineExperimentalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/SymbolManager.h" -#include "llvm/ADT/ImmutableMap.h" -using namespace clang; - -namespace { - -class RefState { - enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped } K; - const Stmt *S; - -public: - RefState(Kind k, const Stmt *s) : K(k), S(s) {} - - bool isAllocated() const { return K == AllocateUnchecked; } - bool isReleased() const { return K == Released; } - bool isEscaped() const { return K == Escaped; } - - bool operator==(const RefState &X) const { - return K == X.K && S == X.S; - } - - static RefState getAllocateUnchecked(const Stmt *s) { - return RefState(AllocateUnchecked, s); - } - static RefState getAllocateFailed() { - return RefState(AllocateFailed, 0); - } - static RefState getReleased(const Stmt *s) { return RefState(Released, s); } - static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(K); - ID.AddPointer(S); - } -}; - -class RegionState {}; - -class MallocChecker : public CheckerVisitor<MallocChecker> { - BuiltinBug *BT_DoubleFree; - BuiltinBug *BT_Leak; - IdentifierInfo *II_malloc, *II_free, *II_realloc; - -public: - MallocChecker() - : BT_DoubleFree(0), BT_Leak(0), II_malloc(0), II_free(0), II_realloc(0) {} - static void *getTag(); - bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); - void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper); - void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); - const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption); - -private: - void MallocMem(CheckerContext &C, const CallExpr *CE); - const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, - const Expr *SizeEx, const GRState *state); - void FreeMem(CheckerContext &C, const CallExpr *CE); - const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state); - - void ReallocMem(CheckerContext &C, const CallExpr *CE); -}; -} // end anonymous namespace - -typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy; - -namespace clang { - template <> - struct GRStateTrait<RegionState> - : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, RefState> > { - static void *GDMIndex() { return MallocChecker::getTag(); } - }; -} - -void clang::RegisterMallocChecker(GRExprEngine &Eng) { - Eng.registerCheck(new MallocChecker()); -} - -void *MallocChecker::getTag() { - static int x; - return &x; -} - -bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - ASTContext &Ctx = C.getASTContext(); - if (!II_malloc) - II_malloc = &Ctx.Idents.get("malloc"); - if (!II_free) - II_free = &Ctx.Idents.get("free"); - if (!II_realloc) - II_realloc = &Ctx.Idents.get("realloc"); - - if (FD->getIdentifier() == II_malloc) { - MallocMem(C, CE); - return true; - } - - if (FD->getIdentifier() == II_free) { - FreeMem(C, CE); - return true; - } - - if (FD->getIdentifier() == II_realloc) { - ReallocMem(C, CE); - return true; - } - - return false; -} - -void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = MallocMemAux(C, CE, CE->getArg(0), C.getState()); - C.addTransition(state); -} - -const GRState *MallocChecker::MallocMemAux(CheckerContext &C, - const CallExpr *CE, - const Expr *SizeEx, - const GRState *state) { - unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); - ValueManager &ValMgr = C.getValueManager(); - - SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); - - SVal Size = state->getSVal(SizeEx); - - state = C.getEngine().getStoreManager().setExtent(state, RetVal.getAsRegion(), - Size); - - state = state->BindExpr(CE, RetVal); - - SymbolRef Sym = RetVal.getAsLocSymbol(); - assert(Sym); - // Set the symbol's state to Allocated. - return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE)); -} - -void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = FreeMemAux(C, CE, C.getState()); - - if (state) - C.addTransition(state); -} - -const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state) { - SVal ArgVal = state->getSVal(CE->getArg(0)); - SymbolRef Sym = ArgVal.getAsLocSymbol(); - assert(Sym); - - const RefState *RS = state->get<RegionState>(Sym); - - // If the symbol has not been tracked, return. This is possible when free() is - // called on a pointer that does not get its pointee directly from malloc(). - // Full support of this requires inter-procedural analysis. - if (!RS) - return state; - - // Check double free. - if (RS->isReleased()) { - ExplodedNode *N = C.GenerateSink(); - if (N) { - if (!BT_DoubleFree) - BT_DoubleFree = new BuiltinBug("Double free", - "Try to free a memory block that has been released"); - // FIXME: should find where it's freed last time. - BugReport *R = new BugReport(*BT_DoubleFree, - BT_DoubleFree->getDescription(), N); - C.EmitReport(R); - } - return NULL; - } - - // Normal free. - return state->set<RegionState>(Sym, RefState::getReleased(CE)); -} - -void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Arg0 = CE->getArg(0); - DefinedOrUnknownSVal Arg0Val=cast<DefinedOrUnknownSVal>(state->getSVal(Arg0)); - - ValueManager &ValMgr = C.getValueManager(); - SValuator &SVator = C.getSValuator(); - - DefinedOrUnknownSVal PtrEQ = SVator.EvalEQ(state, Arg0Val, ValMgr.makeNull()); - - // If the ptr is NULL, the call is equivalent to malloc(size). - if (const GRState *stateEqual = state->Assume(PtrEQ, true)) { - // Hack: set the NULL symbolic region to released to suppress false warning. - // In the future we should add more states for allocated regions, e.g., - // CheckedNull, CheckedNonNull. - - SymbolRef Sym = Arg0Val.getAsLocSymbol(); - if (Sym) - stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE)); - - const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), stateEqual); - C.addTransition(stateMalloc); - } - - if (const GRState *stateNotEqual = state->Assume(PtrEQ, false)) { - const Expr *Arg1 = CE->getArg(1); - DefinedOrUnknownSVal Arg1Val = - cast<DefinedOrUnknownSVal>(stateNotEqual->getSVal(Arg1)); - DefinedOrUnknownSVal SizeZero = SVator.EvalEQ(stateNotEqual, Arg1Val, - ValMgr.makeIntValWithPtrWidth(0, false)); - - if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) { - const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero); - if (stateFree) - C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); - } - - if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) { - const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero); - if (stateFree) { - // FIXME: We should copy the content of the original buffer. - const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), - stateFree); - C.addTransition(stateRealloc); - } - } - } -} - -void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, - SymbolReaper &SymReaper) { - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - const GRState *state = C.getState(); - const RefState *RS = state->get<RegionState>(Sym); - if (!RS) - return; - - if (RS->isAllocated()) { - ExplodedNode *N = C.GenerateSink(); - if (N) { - if (!BT_Leak) - BT_Leak = new BuiltinBug("Memory leak", - "Allocated memory never released. Potential memory leak."); - // FIXME: where it is allocated. - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - C.EmitReport(R); - } - } - } -} - -void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, - GRExprEngine &Eng) { - SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode); - const GRState *state = B.getState(); - typedef llvm::ImmutableMap<SymbolRef, RefState> SymMap; - SymMap M = state->get<RegionState>(); - - for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { - RefState RS = I->second; - if (RS.isAllocated()) { - ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); - if (N) { - if (!BT_Leak) - BT_Leak = new BuiltinBug("Memory leak", - "Allocated memory never released. Potential memory leak."); - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - Eng.getBugReporter().EmitReport(R); - } - } - } -} - -void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { - const Expr *RetE = S->getRetValue(); - if (!RetE) - return; - - const GRState *state = C.getState(); - - SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); - - if (!Sym) - return; - - const RefState *RS = state->get<RegionState>(Sym); - if (!RS) - return; - - // FIXME: check other cases. - if (RS->isAllocated()) - state = state->set<RegionState>(Sym, RefState::getEscaped(S)); - - C.addTransition(state); -} - -const GRState *MallocChecker::EvalAssume(const GRState *state, SVal Cond, - bool Assumption) { - // If a symblic region is assumed to NULL, set its state to AllocateFailed. - // FIXME: should also check symbols assumed to non-null. - - RegionStateTy RS = state->get<RegionState>(); - - for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { - if (state->getSymVal(I.getKey())) - state = state->set<RegionState>(I.getKey(),RefState::getAllocateFailed()); - } - - return state; -} diff --git a/lib/Analysis/ManagerRegistry.cpp b/lib/Analysis/ManagerRegistry.cpp deleted file mode 100644 index 8943db2..0000000 --- a/lib/Analysis/ManagerRegistry.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//===- ManagerRegistry.cpp - Pluggble Analyzer module creators --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the pluggable analyzer module creators. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/ManagerRegistry.h" - -using namespace clang; - -StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; - -ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp deleted file mode 100644 index 87d60d3..0000000 --- a/lib/Analysis/MemRegion.cpp +++ /dev/null @@ -1,800 +0,0 @@ -//== MemRegion.cpp - Abstract memory regions for static analysis --*- 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 MemRegion and its subclasses. MemRegion defines a -// partially-typed abstraction of memory useful for path-sensitive dataflow -// analyses. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/raw_ostream.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/PathSensitive/ValueManager.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/AST/CharUnits.h" -#include "clang/AST/StmtVisitor.h" - -using namespace clang; - -//===----------------------------------------------------------------------===// -// MemRegion Construction. -//===----------------------------------------------------------------------===// - -template<typename RegionTy> struct MemRegionManagerTrait; - -template <typename RegionTy, typename A1> -RegionTy* MemRegionManager::getRegion(const A1 a1) { - - const typename MemRegionManagerTrait<RegionTy>::SuperRegionTy *superRegion = - MemRegionManagerTrait<RegionTy>::getSuperRegion(*this, a1); - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate<RegionTy>(); - new (R) RegionTy(a1, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template <typename RegionTy, typename A1> -RegionTy* MemRegionManager::getSubRegion(const A1 a1, - const MemRegion *superRegion) { - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate<RegionTy>(); - new (R) RegionTy(a1, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template <typename RegionTy, typename A1, typename A2> -RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { - - const typename MemRegionManagerTrait<RegionTy>::SuperRegionTy *superRegion = - MemRegionManagerTrait<RegionTy>::getSuperRegion(*this, a1, a2); - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate<RegionTy>(); - new (R) RegionTy(a1, a2, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template <typename RegionTy, typename A1, typename A2> -RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, - const MemRegion *superRegion) { - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate<RegionTy>(); - new (R) RegionTy(a1, a2, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template <typename RegionTy, typename A1, typename A2, typename A3> -RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, - const MemRegion *superRegion) { - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); - void* InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = (RegionTy*) A.Allocate<RegionTy>(); - new (R) RegionTy(a1, a2, a3, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -//===----------------------------------------------------------------------===// -// Object destruction. -//===----------------------------------------------------------------------===// - -MemRegion::~MemRegion() {} - -MemRegionManager::~MemRegionManager() { - // All regions and their data are BumpPtrAllocated. No need to call - // their destructors. -} - -//===----------------------------------------------------------------------===// -// Basic methods. -//===----------------------------------------------------------------------===// - -bool SubRegion::isSubRegionOf(const MemRegion* R) const { - const MemRegion* r = getSuperRegion(); - while (r != 0) { - if (r == R) - return true; - if (const SubRegion* sr = dyn_cast<SubRegion>(r)) - r = sr->getSuperRegion(); - else - break; - } - return false; -} - -MemRegionManager* SubRegion::getMemRegionManager() const { - const SubRegion* r = this; - do { - const MemRegion *superRegion = r->getSuperRegion(); - if (const SubRegion *sr = dyn_cast<SubRegion>(superRegion)) { - r = sr; - continue; - } - return superRegion->getMemRegionManager(); - } while (1); -} - -const StackFrameContext *VarRegion::getStackFrame() const { - const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace()); - return SSR ? SSR->getStackFrame() : NULL; -} - -//===----------------------------------------------------------------------===// -// FoldingSet profiling. -//===----------------------------------------------------------------------===// - -void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned)getKind()); -} - -void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned)getKind()); - ID.AddPointer(getStackFrame()); -} - -void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const StringLiteral* Str, - const MemRegion* superRegion) { - ID.AddInteger((unsigned) StringRegionKind); - ID.AddPointer(Str); - ID.AddPointer(superRegion); -} - -void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const Expr* Ex, unsigned cnt, - const MemRegion *) { - ID.AddInteger((unsigned) AllocaRegionKind); - ID.AddPointer(Ex); - ID.AddInteger(cnt); -} - -void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { - ProfileRegion(ID, Ex, Cnt, superRegion); -} - -void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { - CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); -} - -void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const CompoundLiteralExpr* CL, - const MemRegion* superRegion) { - ID.AddInteger((unsigned) CompoundLiteralRegionKind); - ID.AddPointer(CL); - ID.AddPointer(superRegion); -} - -void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, - const PointerType *PT, - const MemRegion *sRegion) { - ID.AddInteger((unsigned) CXXThisRegionKind); - ID.AddPointer(PT); - ID.AddPointer(sRegion); -} - -void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { - CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); -} - -void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D, - const MemRegion* superRegion, Kind k) { - ID.AddInteger((unsigned) k); - ID.AddPointer(D); - ID.AddPointer(superRegion); -} - -void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { - DeclRegion::ProfileRegion(ID, D, superRegion, getKind()); -} - -void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { - VarRegion::ProfileRegion(ID, getDecl(), superRegion); -} - -void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, - const MemRegion *sreg) { - ID.AddInteger((unsigned) MemRegion::SymbolicRegionKind); - ID.Add(sym); - ID.AddPointer(sreg); -} - -void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { - SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion()); -} - -void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - QualType ElementType, SVal Idx, - const MemRegion* superRegion) { - ID.AddInteger(MemRegion::ElementRegionKind); - ID.Add(ElementType); - ID.AddPointer(superRegion); - Idx.Profile(ID); -} - -void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { - ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); -} - -void FunctionTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const FunctionDecl *FD, - const MemRegion*) { - ID.AddInteger(MemRegion::FunctionTextRegionKind); - ID.AddPointer(FD); -} - -void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - FunctionTextRegion::ProfileRegion(ID, FD, superRegion); -} - -void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const BlockDecl *BD, CanQualType, - const AnalysisContext *AC, - const MemRegion*) { - ID.AddInteger(MemRegion::BlockTextRegionKind); - ID.AddPointer(BD); -} - -void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockTextRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); -} - -void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const BlockTextRegion *BC, - const LocationContext *LC, - const MemRegion *sReg) { - ID.AddInteger(MemRegion::BlockDataRegionKind); - ID.AddPointer(BC); - ID.AddPointer(LC); - ID.AddPointer(sReg); -} - -void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockDataRegion::ProfileRegion(ID, BC, LC, getSuperRegion()); -} - -void CXXObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, - Expr const *Ex, - const MemRegion *sReg) { - ID.AddPointer(Ex); - ID.AddPointer(sReg); -} - -void CXXObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ProfileRegion(ID, Ex, getSuperRegion()); -} - -//===----------------------------------------------------------------------===// -// Region pretty-printing. -//===----------------------------------------------------------------------===// - -void MemRegion::dump() const { - dumpToStream(llvm::errs()); -} - -std::string MemRegion::getString() const { - std::string s; - llvm::raw_string_ostream os(s); - dumpToStream(os); - return os.str(); -} - -void MemRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "<Unknown Region>"; -} - -void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; -} - -void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "code{" << getDecl()->getDeclName().getAsString() << '}'; -} - -void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "block_code{" << (void*) this << '}'; -} - -void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "block_data{" << BC << '}'; -} - - -void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { - // FIXME: More elaborate pretty-printing. - os << "{ " << (void*) CL << " }"; -} - -void CXXThisRegion::dumpToStream(llvm::raw_ostream &os) const { - os << "this"; -} - -void ElementRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "element{" << superRegion << ',' - << Index << ',' << getElementType().getAsString() << '}'; -} - -void FieldRegion::dumpToStream(llvm::raw_ostream& os) const { - os << superRegion << "->" << getDecl()->getNameAsString(); -} - -void ObjCIvarRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "ivar{" << superRegion << ',' << getDecl()->getNameAsString() << '}'; -} - -void StringRegion::dumpToStream(llvm::raw_ostream& os) const { - Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); -} - -void SymbolicRegion::dumpToStream(llvm::raw_ostream& os) const { - os << "SymRegion{" << sym << '}'; -} - -void VarRegion::dumpToStream(llvm::raw_ostream& os) const { - os << cast<VarDecl>(D)->getNameAsString(); -} - -void RegionRawOffset::dump() const { - dumpToStream(llvm::errs()); -} - -void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const { - os << "raw_offset{" << getRegion() << ',' << getByteOffset() << '}'; -} - -//===----------------------------------------------------------------------===// -// MemRegionManager methods. -//===----------------------------------------------------------------------===// - -template <typename REG> -const REG *MemRegionManager::LazyAllocate(REG*& region) { - if (!region) { - region = (REG*) A.Allocate<REG>(); - new (region) REG(this); - } - - return region; -} - -template <typename REG, typename ARG> -const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { - if (!region) { - region = (REG*) A.Allocate<REG>(); - new (region) REG(this, a); - } - - return region; -} - -const StackLocalsSpaceRegion* -MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { - assert(STC); - if (STC == cachedStackLocalsFrame) - return cachedStackLocalsRegion; - cachedStackLocalsFrame = STC; - return LazyAllocate(cachedStackLocalsRegion, STC); -} - -const StackArgumentsSpaceRegion * -MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { - assert(STC); - if (STC == cachedStackArgumentsFrame) - return cachedStackArgumentsRegion; - - cachedStackArgumentsFrame = STC; - return LazyAllocate(cachedStackArgumentsRegion, STC); -} - -const GlobalsSpaceRegion *MemRegionManager::getGlobalsRegion() { - return LazyAllocate(globals); -} - -const HeapSpaceRegion *MemRegionManager::getHeapRegion() { - return LazyAllocate(heap); -} - -const MemSpaceRegion *MemRegionManager::getUnknownRegion() { - return LazyAllocate(unknown); -} - -const MemSpaceRegion *MemRegionManager::getCodeRegion() { - return LazyAllocate(code); -} - -//===----------------------------------------------------------------------===// -// Constructing regions. -//===----------------------------------------------------------------------===// - -const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str) { - return getSubRegion<StringRegion>(Str, getGlobalsRegion()); -} - -const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, - const LocationContext *LC) { - const MemRegion *sReg = 0; - - if (D->hasLocalStorage()) { - // FIXME: Once we implement scope handling, we will need to properly lookup - // 'D' to the proper LocationContext. - const DeclContext *DC = D->getDeclContext(); - const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); - - if (!STC) - sReg = getUnknownRegion(); - else { - sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D) - ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC)) - : static_cast<const MemRegion*>(getStackLocalsRegion(STC)); - } - } - else { - sReg = getGlobalsRegion(); - } - - return getSubRegion<VarRegion>(D, sReg); -} - -const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, - const MemRegion *superR) { - return getSubRegion<VarRegion>(D, superR); -} - -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); - } - else { - // We allow 'LC' to be NULL for cases where want BlockDataRegions - // without context-sensitivity. - sReg = getUnknownRegion(); - } - - return getSubRegion<BlockDataRegion>(BC, LC, sReg); -} - -const CompoundLiteralRegion* -MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, - const LocationContext *LC) { - - const MemRegion *sReg = 0; - - if (CL->isFileScope()) - sReg = getGlobalsRegion(); - else { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - sReg = getStackLocalsRegion(STC); - } - - return getSubRegion<CompoundLiteralRegion>(CL, sReg); -} - -const ElementRegion* -MemRegionManager::getElementRegion(QualType elementType, SVal Idx, - const MemRegion* superRegion, - ASTContext& Ctx){ - - QualType T = Ctx.getCanonicalType(elementType); - - llvm::FoldingSetNodeID ID; - ElementRegion::ProfileRegion(ID, T, Idx, superRegion); - - void* InsertPos; - MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); - ElementRegion* R = cast_or_null<ElementRegion>(data); - - if (!R) { - R = (ElementRegion*) A.Allocate<ElementRegion>(); - new (R) ElementRegion(T, Idx, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -const FunctionTextRegion * -MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { - return getSubRegion<FunctionTextRegion>(FD, getCodeRegion()); -} - -const BlockTextRegion * -MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, - AnalysisContext *AC) { - return getSubRegion<BlockTextRegion>(BD, locTy, AC, getCodeRegion()); -} - - -/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. -const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { - return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); -} - -const FieldRegion* -MemRegionManager::getFieldRegion(const FieldDecl* d, - const MemRegion* superRegion){ - return getSubRegion<FieldRegion>(d, superRegion); -} - -const ObjCIvarRegion* -MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, - const MemRegion* superRegion) { - return getSubRegion<ObjCIvarRegion>(d, superRegion); -} - -const CXXObjectRegion* -MemRegionManager::getCXXObjectRegion(Expr const *E, - LocationContext const *LC) { - const StackFrameContext *SFC = LC->getCurrentStackFrame(); - assert(SFC); - return getSubRegion<CXXObjectRegion>(E, getStackLocalsRegion(SFC)); -} - -const CXXThisRegion* -MemRegionManager::getCXXThisRegion(QualType thisPointerTy, - const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - const PointerType *PT = thisPointerTy->getAs<PointerType>(); - assert(PT); - return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC)); -} - -const AllocaRegion* -MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt, - const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); - return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC)); -} - -const MemSpaceRegion *MemRegion::getMemorySpace() const { - const MemRegion *R = this; - const SubRegion* SR = dyn_cast<SubRegion>(this); - - while (SR) { - R = SR->getSuperRegion(); - SR = dyn_cast<SubRegion>(R); - } - - return dyn_cast<MemSpaceRegion>(R); -} - -bool MemRegion::hasStackStorage() const { - return isa<StackSpaceRegion>(getMemorySpace()); -} - -bool MemRegion::hasStackNonParametersStorage() const { - return isa<StackLocalsSpaceRegion>(getMemorySpace()); -} - -bool MemRegion::hasStackParametersStorage() const { - return isa<StackArgumentsSpaceRegion>(getMemorySpace()); -} - -bool MemRegion::hasGlobalsOrParametersStorage() const { - const MemSpaceRegion *MS = getMemorySpace(); - return isa<StackArgumentsSpaceRegion>(MS) || - isa<GlobalsSpaceRegion>(MS); -} - -// getBaseRegion strips away all elements and fields, and get the base region -// of them. -const MemRegion *MemRegion::getBaseRegion() const { - const MemRegion *R = this; - while (true) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - R = ER->getSuperRegion(); - continue; - } - if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { - R = FR->getSuperRegion(); - continue; - } - break; - } - return R; -} - -//===----------------------------------------------------------------------===// -// View handling. -//===----------------------------------------------------------------------===// - -const MemRegion *MemRegion::StripCasts() const { - const MemRegion *R = this; - while (true) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // FIXME: generalize. Essentially we want to strip away ElementRegions - // that were layered on a symbolic region because of casts. We only - // want to strip away ElementRegions, however, where the index is 0. - SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) { - if (CI->getValue().getSExtValue() == 0) { - R = ER->getSuperRegion(); - continue; - } - } - } - break; - } - return R; -} - -// FIXME: Merge with the implementation of the same method in Store.cpp -static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { - if (const RecordType *RT = Ty->getAs<RecordType>()) { - const RecordDecl *D = RT->getDecl(); - if (!D->getDefinition(Ctx)) - return false; - } - - return true; -} - -RegionRawOffset ElementRegion::getAsRawOffset() const { - CharUnits offset = CharUnits::Zero(); - const ElementRegion *ER = this; - const MemRegion *superR = NULL; - ASTContext &C = getContext(); - - // FIXME: Handle multi-dimensional arrays. - - while (ER) { - superR = ER->getSuperRegion(); - - // FIXME: generalize to symbolic offsets. - SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) { - // Update the offset. - int64_t i = CI->getValue().getSExtValue(); - - if (i != 0) { - QualType elemType = ER->getElementType(); - - // If we are pointing to an incomplete type, go no further. - if (!IsCompleteType(C, elemType)) { - superR = ER; - break; - } - - CharUnits size = C.getTypeSizeInChars(elemType); - offset += (i * size); - } - - // Go to the next ElementRegion (if any). - ER = dyn_cast<ElementRegion>(superR); - continue; - } - - return NULL; - } - - assert(superR && "super region cannot be NULL"); - return RegionRawOffset(superR, offset.getQuantity()); -} - -//===----------------------------------------------------------------------===// -// BlockDataRegion -//===----------------------------------------------------------------------===// - -void BlockDataRegion::LazyInitializeReferencedVars() { - if (ReferencedVars) - return; - - AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); - AnalysisContext::referenced_decls_iterator I, E; - llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); - - if (I == E) { - ReferencedVars = (void*) 0x1; - return; - } - - MemRegionManager &MemMgr = *getMemRegionManager(); - llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); - BumpVectorContext BC(A); - - typedef BumpVector<const MemRegion*> VarVec; - VarVec *BV = (VarVec*) A.Allocate<VarVec>(); - new (BV) VarVec(BC, E - I); - - for ( ; I != E; ++I) { - const VarDecl *VD = *I; - const VarRegion *VR = 0; - - if (!VD->getAttr<BlocksAttr>()) - VR = MemMgr.getVarRegion(VD, this); - else { - if (LC) - VR = MemMgr.getVarRegion(VD, LC); - else { - VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); - } - } - - assert(VR); - BV->push_back(VR, BC); - } - - ReferencedVars = BV; -} - -BlockDataRegion::referenced_vars_iterator -BlockDataRegion::referenced_vars_begin() const { - const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); - - BumpVector<const MemRegion*> *Vec = - static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->begin()); -} - -BlockDataRegion::referenced_vars_iterator -BlockDataRegion::referenced_vars_end() const { - const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); - - BumpVector<const MemRegion*> *Vec = - static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); - - return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? - NULL : Vec->end()); -} diff --git a/lib/Analysis/NSAutoreleasePoolChecker.cpp b/lib/Analysis/NSAutoreleasePoolChecker.cpp deleted file mode 100644 index 2ff0487..0000000 --- a/lib/Analysis/NSAutoreleasePoolChecker.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a NSAutoreleasePoolChecker, a small checker that warns -// about subpar uses of NSAutoreleasePool. Note that while the check itself -// (in it's current form) could be written as a flow-insensitive check, in -// can be potentially enhanced in the future with flow-sensitive information. -// It is also a good example of the CheckerVisitor interface. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "BasicObjCFoundationChecks.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Decl.h" - -using namespace clang; - -namespace { -class NSAutoreleasePoolChecker - : public CheckerVisitor<NSAutoreleasePoolChecker> { - - Selector releaseS; - -public: - NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} - - static void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); -}; - -} // end anonymous namespace - - -void clang::RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng) { - ASTContext &Ctx = Eng.getContext(); - if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { - Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", - Ctx))); - } -} - -void -NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - - const Expr *receiver = ME->getReceiver(); - if (!receiver) - return; - - // FIXME: Enhance with value-tracking information instead of consulting - // the type of the expression. - const ObjCObjectPointerType* PT = - receiver->getType()->getAs<ObjCObjectPointerType>(); - - if (!PT) - return; - const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); - if (!OD) - return; - if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) - return; - - // Sending 'release' message? - if (ME->getSelector() != releaseS) - return; - - SourceRange R = ME->getSourceRange(); - - C.getBugReporter().EmitBasicReport("Use -drain instead of -release", - "API Upgrade (Apple)", - "Use -drain instead of -release when using NSAutoreleasePool " - "and garbage collection", ME->getLocStart(), &R, 1); -} diff --git a/lib/Analysis/NSErrorChecker.cpp b/lib/Analysis/NSErrorChecker.cpp deleted file mode 100644 index e3cf57f..0000000 --- a/lib/Analysis/NSErrorChecker.cpp +++ /dev/null @@ -1,237 +0,0 @@ -//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- 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 CheckNSError, a flow-insenstive check -// that determines if an Objective-C class interface correctly returns -// a non-void return type. -// -// File under feature request PR 2600. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/LocalCheckers.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" -#include "BasicObjCFoundationChecks.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Decl.h" -#include "llvm/ADT/SmallVector.h" - -using namespace clang; - -namespace { -class NSErrorChecker : public BugType { - const Decl &CodeDecl; - const bool isNSErrorWarning; - IdentifierInfo * const II; - GRExprEngine &Eng; - - void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams); - - void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams); - - bool CheckNSErrorArgument(QualType ArgTy); - bool CheckCFErrorArgument(QualType ArgTy); - - void CheckParamDeref(const VarDecl *V, const LocationContext *LC, - const GRState *state, BugReporter& BR); - - void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); - -public: - NSErrorChecker(const Decl &D, bool isNSError, GRExprEngine& eng) - : BugType(isNSError ? "NSError** null dereference" - : "CFErrorRef* null dereference", - "Coding conventions (Apple)"), - CodeDecl(D), - isNSErrorWarning(isNSError), - II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), - Eng(eng) {} - - void FlushReports(BugReporter& BR); -}; - -} // end anonymous namespace - -void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, - const Decl &D) { - BR.Register(new NSErrorChecker(D, true, Eng)); - BR.Register(new NSErrorChecker(D, false, Eng)); -} - -void NSErrorChecker::FlushReports(BugReporter& BR) { - // Get the analysis engine and the exploded analysis graph. - ExplodedGraph& G = Eng.getGraph(); - - // Get the ASTContext, which is useful for querying type information. - ASTContext &Ctx = BR.getContext(); - - QualType ResultTy; - llvm::SmallVector<VarDecl*, 5> ErrorParams; - - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl)) - CheckSignature(*MD, ResultTy, ErrorParams); - else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl)) - CheckSignature(*FD, ResultTy, ErrorParams); - else - return; - - if (ErrorParams.empty()) - return; - - if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); - - for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); - RI!=RE; ++RI) { - // Scan the parameters for an implicit null dereference. - for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(), - E=ErrorParams.end(); I!=E; ++I) - CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); - } -} - -void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (isa<ObjCMethodDecl>(CodeDecl)) - os << "Method"; - else - os << "Function"; - - os << " accepting "; - os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); - os << " should have a non-void return value to indicate whether or not an " - "error occurred"; - - BR.EmitBasicReport(isNSErrorWarning - ? "Bad return type when passing NSError**" - : "Bad return type when passing CFError*", - getCategory(), os.str(), - CodeDecl.getLocation()); -} - -void -NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { - - ResultTy = M.getResultType(); - - for (ObjCMethodDecl::param_iterator I=M.param_begin(), - E=M.param_end(); I!=E; ++I) { - - QualType T = (*I)->getType(); - - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); - } -} - -void -NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { - - ResultTy = F.getResultType(); - - for (FunctionDecl::param_const_iterator I = F.param_begin(), - E = F.param_end(); I != E; ++I) { - - QualType T = (*I)->getType(); - - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); - } -} - - -bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { - - const PointerType* PPT = ArgTy->getAs<PointerType>(); - if (!PPT) - return false; - - const ObjCObjectPointerType* PT = - PPT->getPointeeType()->getAs<ObjCObjectPointerType>(); - - if (!PT) - return false; - - const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); - - // FIXME: Can ID ever be NULL? - if (ID) - return II == ID->getIdentifier(); - - return false; -} - -bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { - - const PointerType* PPT = ArgTy->getAs<PointerType>(); - if (!PPT) return false; - - const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>(); - if (!TT) return false; - - return TT->getDecl()->getIdentifier() == II; -} - -void NSErrorChecker::CheckParamDeref(const VarDecl *Param, - const LocationContext *LC, - const GRState *rootState, - BugReporter& BR) { - - SVal ParamL = rootState->getLValue(Param, LC); - const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>(); - assert (ParamR && "Parameters always have VarRegions."); - SVal ParamSVal = rootState->getSVal(ParamR); - - // FIXME: For now assume that ParamSVal is symbolic. We need to generalize - // this later. - SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); - if (!ParamSym) - return; - - // Iterate over the implicit-null dereferences. - ExplodedNode *const* I, *const* E; - llvm::tie(I, E) = GetImplicitNullDereferences(Eng); - for ( ; I != E; ++I) { - const GRState *state = (*I)->getState(); - SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt()); - if (location.getAsSymbol() != ParamSym) - continue; - - // Emit an error. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Potential null dereference. According to coding standards "; - - if (isNSErrorWarning) - os << "in 'Creating and Returning NSError Objects' the parameter '"; - else - os << "documented in CoreFoundation/CFError.h the parameter '"; - - os << Param->getNameAsString() << "' may be null."; - - BugReport *report = new BugReport(*this, os.str(), *I); - // FIXME: Notable symbols are now part of the report. We should - // add support for notable symbols in BugReport. - // BR.addNotableSymbol(SV->getSymbol()); - BR.EmitReport(report); - } -} diff --git a/lib/Analysis/NoReturnFunctionChecker.cpp b/lib/Analysis/NoReturnFunctionChecker.cpp deleted file mode 100644 index 5cfd9ac..0000000 --- a/lib/Analysis/NoReturnFunctionChecker.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines NoReturnFunctionChecker, which evaluates functions that do not -// return to the caller. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; - -namespace { - -class NoReturnFunctionChecker : public Checker { -public: - static void *getTag() { static int tag = 0; return &tag; } - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); -}; - -} - -void clang::RegisterNoReturnFunctionChecker(GRExprEngine &Eng) { - Eng.registerCheck(new NoReturnFunctionChecker()); -} - -bool NoReturnFunctionChecker::EvalCallExpr(CheckerContext &C, - const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - bool BuildSinks = false; - - if (FD->getAttr<NoReturnAttr>() || FD->getAttr<AnalyzerNoReturnAttr>()) - BuildSinks = true; - else if (const IdentifierInfo *II = FD->getIdentifier()) { - // HACK: Some functions are not marked noreturn, and don't return. - // Here are a few hardwired ones. If this takes too long, we can - // potentially cache these results. - BuildSinks - = llvm::StringSwitch<bool>(llvm::StringRef(II->getName())) - .Case("exit", true) - .Case("panic", true) - .Case("error", true) - .Case("Assert", true) - // FIXME: This is just a wrapper around throwing an exception. - // Eventually inter-procedural analysis should handle this easily. - .Case("ziperr", true) - .Case("assfail", true) - .Case("db_error", true) - .Case("__assert", true) - .Case("__assert_rtn", true) - .Case("__assert_fail", true) - .Case("dtrace_assfail", true) - .Case("yy_fatal_error", true) - .Case("_XCAssertionFailureHandler", true) - .Case("_DTAssertionFailureHandler", true) - .Case("_TSAssertionFailureHandler", true) - .Default(false); - } - - if (!BuildSinks) - return false; - - C.GenerateSink(CE); - return true; -} diff --git a/lib/Analysis/OSAtomicChecker.cpp b/lib/Analysis/OSAtomicChecker.cpp deleted file mode 100644 index 9d34e9e..0000000 --- a/lib/Analysis/OSAtomicChecker.cpp +++ /dev/null @@ -1,198 +0,0 @@ -//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This checker evaluates OSAtomic functions. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" -#include "clang/Basic/Builtins.h" -#include "llvm/ADT/StringSwitch.h" - -using namespace clang; - -namespace { - -class OSAtomicChecker : public Checker { -public: - static void *getTag() { static int tag = 0; return &tag; } - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); - -private: - bool EvalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); -}; - -} - -void clang::RegisterOSAtomicChecker(GRExprEngine &Eng) { - Eng.registerCheck(new OSAtomicChecker()); -} - -bool OSAtomicChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl* FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - const IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return false; - - llvm::StringRef FName(II->getName()); - - // Check for compare and swap. - if (FName.startswith("OSAtomicCompareAndSwap") || - FName.startswith("objc_atomicCompareAndSwap")) - return EvalOSAtomicCompareAndSwap(C, CE); - - // FIXME: Other atomics. - return false; -} - -bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, - const CallExpr *CE) { - // Not enough arguments to match OSAtomicCompareAndSwap? - if (CE->getNumArgs() != 3) - return false; - - ASTContext &Ctx = C.getASTContext(); - const Expr *oldValueExpr = CE->getArg(0); - QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); - - const Expr *newValueExpr = CE->getArg(1); - QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); - - // Do the types of 'oldValue' and 'newValue' match? - if (oldValueType != newValueType) - return false; - - const Expr *theValueExpr = CE->getArg(2); - const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>(); - - // theValueType not a pointer? - if (!theValueType) - return false; - - QualType theValueTypePointee = - Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); - - // The pointee must match newValueType and oldValueType. - if (theValueTypePointee != newValueType) - return false; - - static unsigned magic_load = 0; - static unsigned magic_store = 0; - - const void *OSAtomicLoadTag = &magic_load; - const void *OSAtomicStoreTag = &magic_store; - - // Load 'theValue'. - GRExprEngine &Engine = C.getEngine(); - const GRState *state = C.getState(); - ExplodedNodeSet Tmp; - SVal location = state->getSVal(theValueExpr); - // Here we should use the value type of the region as the load type. - QualType LoadTy; - if (const TypedRegion *TR = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - LoadTy = TR->getValueType(Ctx); - } - Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(), - state, location, OSAtomicLoadTag, LoadTy); - - if (Tmp.empty()) { - // If no nodes were generated, other checkers must generated sinks. But - // since the builder state was restored, we set it manually to prevent - // auto transition. - // FIXME: there should be a better approach. - C.getNodeBuilder().BuildSinks = true; - return true; - } - - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); - I != E; ++I) { - - ExplodedNode *N = *I; - const GRState *stateLoad = N->getState(); - SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); - SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); - - // FIXME: Issue an error. - if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { - return false; - } - - DefinedOrUnknownSVal theValueVal = - cast<DefinedOrUnknownSVal>(theValueVal_untested); - DefinedOrUnknownSVal oldValueVal = - cast<DefinedOrUnknownSVal>(oldValueVal_untested); - - SValuator &SVator = Engine.getSValuator(); - - // Perform the comparison. - DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal); - - const GRState *stateEqual = stateLoad->Assume(Cmp, true); - - // Were they equal? - if (stateEqual) { - // Perform the store. - ExplodedNodeSet TmpStore; - SVal val = stateEqual->getSVal(newValueExpr); - - // Handle implicit value casts. - if (const TypedRegion *R = - dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx), - newValueExpr->getType()); - } - - Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N, - stateEqual, location, val, OSAtomicStoreTag); - - if (TmpStore.empty()) { - // If no nodes were generated, other checkers must generated sinks. But - // since the builder state was restored, we set it manually to prevent - // auto transition. - // FIXME: there should be a better approach. - C.getNodeBuilder().BuildSinks = true; - return true; - } - - // Now bind the result of the comparison. - for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), - E2 = TmpStore.end(); I2 != E2; ++I2) { - ExplodedNode *predNew = *I2; - const GRState *stateNew = predNew->getState(); - // Check for 'void' return type if we have a bogus function prototype. - SVal Res = UnknownVal(); - QualType T = CE->getType(); - if (!T->isVoidType()) - Res = Engine.getValueManager().makeTruthVal(true, T); - C.GenerateNode(stateNew->BindExpr(CE, Res), predNew); - } - } - - // Were they not equal? - if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) { - // Check for 'void' return type if we have a bogus function prototype. - SVal Res = UnknownVal(); - QualType T = CE->getType(); - if (!T->isVoidType()) - Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); - C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N); - } - } - - return true; -} diff --git a/lib/Analysis/PathDiagnostic.cpp b/lib/Analysis/PathDiagnostic.cpp deleted file mode 100644 index 734570a..0000000 --- a/lib/Analysis/PathDiagnostic.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the PathDiagnostic-related interfaces. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathDiagnostic.h" -#include "clang/AST/Expr.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/StmtCXX.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Casting.h" - -using namespace clang; -using llvm::dyn_cast; -using llvm::isa; - -bool PathDiagnosticMacroPiece::containsEvent() const { - for (const_iterator I = begin(), E = end(); I!=E; ++I) { - if (isa<PathDiagnosticEventPiece>(*I)) - return true; - - if (PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(*I)) - if (MP->containsEvent()) - return true; - } - - return false; -} - -static llvm::StringRef StripTrailingDots(llvm::StringRef s) { - for (llvm::StringRef::size_type i = s.size(); i != 0; --i) - if (s[i - 1] != '.') - return s.substr(0, i); - return ""; -} - -PathDiagnosticPiece::PathDiagnosticPiece(llvm::StringRef s, - Kind k, DisplayHint hint) - : str(StripTrailingDots(s)), kind(k), Hint(hint) {} - -PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) - : kind(k), Hint(hint) {} - -PathDiagnosticPiece::~PathDiagnosticPiece() {} -PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} -PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} - -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { - for (iterator I = begin(), E = end(); I != E; ++I) delete *I; -} - -PathDiagnostic::PathDiagnostic() : Size(0) {} - -PathDiagnostic::~PathDiagnostic() { - for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; -} - -void PathDiagnostic::resetPath(bool deletePieces) { - Size = 0; - - if (deletePieces) - for (iterator I=begin(), E=end(); I!=E; ++I) - delete &*I; - - path.clear(); -} - - -PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, - llvm::StringRef category) - : Size(0), - BugType(StripTrailingDots(bugtype)), - Desc(StripTrailingDots(desc)), - Category(StripTrailingDots(category)) {} - -void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, - const DiagnosticInfo &Info) { - - // Create a PathDiagnostic with a single piece. - - PathDiagnostic* D = new PathDiagnostic(); - - const char *LevelStr; - switch (DiagLevel) { - default: - case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); - case Diagnostic::Note: LevelStr = "note: "; break; - case Diagnostic::Warning: LevelStr = "warning: "; break; - case Diagnostic::Error: LevelStr = "error: "; break; - case Diagnostic::Fatal: LevelStr = "fatal error: "; break; - } - - llvm::SmallString<100> StrC; - StrC += LevelStr; - Info.FormatDiagnostic(StrC); - - PathDiagnosticPiece *P = - new PathDiagnosticEventPiece(Info.getLocation(), StrC.str()); - - for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) - P->addRange(Info.getRange(i)); - for (unsigned i = 0, e = Info.getNumCodeModificationHints(); i != e; ++i) - P->addCodeModificationHint(Info.getCodeModificationHint(i)); - D->push_front(P); - - HandlePathDiagnostic(D); -} - -//===----------------------------------------------------------------------===// -// PathDiagnosticLocation methods. -//===----------------------------------------------------------------------===// - -FullSourceLoc PathDiagnosticLocation::asLocation() const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - case RangeK: - break; - case StmtK: - return FullSourceLoc(S->getLocStart(), const_cast<SourceManager&>(*SM)); - case DeclK: - return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); - } - - return FullSourceLoc(R.getBegin(), const_cast<SourceManager&>(*SM)); -} - -PathDiagnosticRange PathDiagnosticLocation::asRange() const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - return PathDiagnosticRange(R, true); - case RangeK: - break; - case StmtK: { - const Stmt *S = asStmt(); - switch (S->getStmtClass()) { - default: - break; - case Stmt::DeclStmtClass: { - const DeclStmt *DS = cast<DeclStmt>(S); - if (DS->isSingleDecl()) { - // Should always be the case, but we'll be defensive. - return SourceRange(DS->getLocStart(), - DS->getSingleDecl()->getLocation()); - } - break; - } - // FIXME: Provide better range information for different - // terminators. - case Stmt::IfStmtClass: - case Stmt::WhileStmtClass: - case Stmt::DoStmtClass: - case Stmt::ForStmtClass: - case Stmt::ChooseExprClass: - case Stmt::IndirectGotoStmtClass: - case Stmt::SwitchStmtClass: - case Stmt::ConditionalOperatorClass: - case Stmt::ObjCForCollectionStmtClass: { - SourceLocation L = S->getLocStart(); - return SourceRange(L, L); - } - } - - return S->getSourceRange(); - } - case DeclK: - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) - return MD->getSourceRange(); - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - // FIXME: We would like to always get the function body, even - // when it needs to be de-serialized, but getting the - // ASTContext here requires significant changes. - if (Stmt *Body = FD->getBody()) { - if (CompoundStmt *CS = dyn_cast<CompoundStmt>(Body)) - return CS->getSourceRange(); - else - return cast<CXXTryStmt>(Body)->getSourceRange(); - } - } - else { - SourceLocation L = D->getLocation(); - return PathDiagnosticRange(SourceRange(L, L), true); - } - } - - return R; -} - -void PathDiagnosticLocation::flatten() { - if (K == StmtK) { - R = asRange(); - K = RangeK; - S = 0; - D = 0; - } - else if (K == DeclK) { - SourceLocation L = D->getLocation(); - R = SourceRange(L, L); - K = SingleLocK; - S = 0; - D = 0; - } -} - -//===----------------------------------------------------------------------===// -// FoldingSet profiling methods. -//===----------------------------------------------------------------------===// - -void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) K); - switch (K) { - case RangeK: - ID.AddInteger(R.getBegin().getRawEncoding()); - ID.AddInteger(R.getEnd().getRawEncoding()); - break; - case SingleLocK: - ID.AddInteger(R.getBegin().getRawEncoding()); - break; - case StmtK: - ID.Add(S); - break; - case DeclK: - ID.Add(D); - break; - } - return; -} - -void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) getKind()); - ID.AddString(str); - // FIXME: Add profiling support for code hints. - ID.AddInteger((unsigned) getDisplayHint()); - for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { - ID.AddInteger(I->getBegin().getRawEncoding()); - ID.AddInteger(I->getEnd().getRawEncoding()); - } -} - -void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - ID.Add(Pos); -} - -void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); -} - -void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(**I); -} - -void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Size); - ID.AddString(BugType); - ID.AddString(Desc); - ID.AddString(Category); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); - - for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) - ID.AddString(*I); -} diff --git a/lib/Analysis/PointerArithChecker.cpp b/lib/Analysis/PointerArithChecker.cpp deleted file mode 100644 index 370233c..0000000 --- a/lib/Analysis/PointerArithChecker.cpp +++ /dev/null @@ -1,71 +0,0 @@ -//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines PointerArithChecker, a builtin checker that checks for -// pointer arithmetic on locations other than array elements. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class PointerArithChecker - : public CheckerVisitor<PointerArithChecker> { - BuiltinBug *BT; -public: - PointerArithChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} - -void *PointerArithChecker::getTag() { - static int x; - return &x; -} - -void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - if (B->getOpcode() != BinaryOperator::Sub && - B->getOpcode() != BinaryOperator::Add) - return; - - const GRState *state = C.getState(); - SVal LV = state->getSVal(B->getLHS()); - SVal RV = state->getSVal(B->getRHS()); - - const MemRegion *LR = LV.getAsRegion(); - - if (!LR || !RV.isConstant()) - return; - - // If pointer arithmetic is done on variables of non-array type, this often - // means behavior rely on memory organization, which is dangerous. - if (isa<VarRegion>(LR) || isa<CodeTextRegion>(LR) || - isa<CompoundLiteralRegion>(LR)) { - - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Dangerous pointer arithmetic", - "Pointer arithmetic done on non-array variables " - "means reliance on memory layout, which is " - "dangerous."); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); - R->addRange(B->getSourceRange()); - C.EmitReport(R); - } - } -} - -void clang::RegisterPointerArithChecker(GRExprEngine &Eng) { - Eng.registerCheck(new PointerArithChecker()); -} diff --git a/lib/Analysis/PointerSubChecker.cpp b/lib/Analysis/PointerSubChecker.cpp deleted file mode 100644 index c597a25..0000000 --- a/lib/Analysis/PointerSubChecker.cpp +++ /dev/null @@ -1,77 +0,0 @@ -//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This files defines PointerSubChecker, a builtin checker that checks for -// pointer subtractions on two pointers pointing to different memory chunks. -// This check corresponds to CWE-469. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class PointerSubChecker - : public CheckerVisitor<PointerSubChecker> { - BuiltinBug *BT; -public: - PointerSubChecker() : BT(0) {} - static void *getTag(); - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} - -void *PointerSubChecker::getTag() { - static int x; - return &x; -} - -void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - // When doing pointer subtraction, if the two pointers do not point to the - // same memory chunk, emit a warning. - if (B->getOpcode() != BinaryOperator::Sub) - return; - - const GRState *state = C.getState(); - SVal LV = state->getSVal(B->getLHS()); - SVal RV = state->getSVal(B->getRHS()); - - const MemRegion *LR = LV.getAsRegion(); - const MemRegion *RR = RV.getAsRegion(); - - if (!(LR && RR)) - return; - - const MemRegion *BaseLR = LR->getBaseRegion(); - const MemRegion *BaseRR = RR->getBaseRegion(); - - if (BaseLR == BaseRR) - return; - - // Allow arithmetic on different symbolic regions. - if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) - return; - - if (ExplodedNode *N = C.GenerateNode()) { - if (!BT) - BT = new BuiltinBug("Pointer subtraction", - "Subtraction of two pointers that do not point to " - "the same memory chunk may cause incorrect result."); - RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); - R->addRange(B->getSourceRange()); - C.EmitReport(R); - } -} - -void clang::RegisterPointerSubChecker(GRExprEngine &Eng) { - Eng.registerCheck(new PointerSubChecker()); -} diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp new file mode 100644 index 0000000..55abd10 --- /dev/null +++ b/lib/Analysis/PrintfFormatString.cpp @@ -0,0 +1,436 @@ +//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Handling of format string in printf and friends. The structure of format +// strings for fprintf() are described in C99 7.19.6.1. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/PrintfFormatString.h" +#include "clang/AST/ASTContext.h" + +using clang::analyze_printf::FormatSpecifier; +using clang::analyze_printf::OptionalAmount; +using clang::analyze_printf::ArgTypeResult; +using clang::analyze_printf::FormatStringHandler; +using namespace clang; + +namespace { +class FormatSpecifierResult { + FormatSpecifier FS; + const char *Start; + bool Stop; +public: + FormatSpecifierResult(bool stop = false) + : Start(0), Stop(stop) {} + FormatSpecifierResult(const char *start, + const FormatSpecifier &fs) + : FS(fs), Start(start), Stop(false) {} + + + const char *getStart() const { return Start; } + bool shouldStop() const { return Stop; } + bool hasValue() const { return Start != 0; } + const FormatSpecifier &getValue() const { + assert(hasValue()); + return FS; + } + const FormatSpecifier &getValue() { return FS; } +}; +} // end anonymous namespace + +template <typename T> +class UpdateOnReturn { + T &ValueToUpdate; + const T &ValueToCopy; +public: + UpdateOnReturn(T &valueToUpdate, const T &valueToCopy) + : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {} + + ~UpdateOnReturn() { + ValueToUpdate = ValueToCopy; + } +}; + +//===----------------------------------------------------------------------===// +// Methods for parsing format strings. +//===----------------------------------------------------------------------===// + +static OptionalAmount ParseAmount(const char *&Beg, const char *E) { + const char *I = Beg; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + bool foundDigits = false; + unsigned accumulator = 0; + + for ( ; I != E; ++I) { + char c = *I; + if (c >= '0' && c <= '9') { + foundDigits = true; + accumulator += (accumulator * 10) + (c - '0'); + continue; + } + + if (foundDigits) + return OptionalAmount(accumulator, Beg); + + if (c == '*') { + ++I; + return OptionalAmount(OptionalAmount::Arg, Beg); + } + + break; + } + + return OptionalAmount(); +} + +static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, + const char *&Beg, + const char *E) { + + using namespace clang::analyze_printf; + + const char *I = Beg; + const char *Start = 0; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + // Look for a '%' character that indicates the start of a format specifier. + for ( ; I != E ; ++I) { + char c = *I; + if (c == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + if (c == '%') { + Start = I++; // Record the start of the format specifier. + break; + } + } + + // No format specifier found? + if (!Start) + return false; + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + FormatSpecifier FS; + + // Look for flags (if any). + bool hasMore = true; + for ( ; I != E; ++I) { + switch (*I) { + default: hasMore = false; break; + case '-': FS.setIsLeftJustified(); break; + case '+': FS.setHasPlusPrefix(); break; + case ' ': FS.setHasSpacePrefix(); break; + case '#': FS.setHasAlternativeForm(); break; + case '0': FS.setHasLeadingZeros(); break; + } + if (!hasMore) + break; + } + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + // Look for the field width (if any). + FS.setFieldWidth(ParseAmount(I, E)); + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + // Look for the precision (if any). + if (*I == '.') { + ++I; + if (I == E) { + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + FS.setPrecision(ParseAmount(I, E)); + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + } + + // Look for the length modifier. + LengthModifier lm = None; + switch (*I) { + default: + break; + case 'h': + ++I; + lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort; + break; + case 'l': + ++I; + lm = (I != E && *I == 'l') ? ++I, AsLongLong : AsLong; + break; + case 'j': lm = AsIntMax; ++I; break; + case 'z': lm = AsSizeT; ++I; break; + case 't': lm = AsPtrDiff; ++I; break; + case 'L': lm = AsLongDouble; ++I; break; + case 'q': lm = AsLongLong; ++I; break; + } + FS.setLengthModifier(lm); + + if (I == E) { + // No more characters left? + H.HandleIncompleteFormatSpecifier(Start, E - Start); + return true; + } + + if (*I == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + + // Finally, look for the conversion specifier. + const char *conversionPosition = I++; + ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; + switch (*conversionPosition) { + default: + break; + // C99: 7.19.6.1 (section 8). + case 'd': k = ConversionSpecifier::dArg; break; + case 'i': k = ConversionSpecifier::iArg; break; + case 'o': k = ConversionSpecifier::oArg; break; + case 'u': k = ConversionSpecifier::uArg; break; + case 'x': k = ConversionSpecifier::xArg; break; + case 'X': k = ConversionSpecifier::XArg; break; + case 'f': k = ConversionSpecifier::fArg; break; + case 'F': k = ConversionSpecifier::FArg; break; + case 'e': k = ConversionSpecifier::eArg; break; + case 'E': k = ConversionSpecifier::EArg; break; + case 'g': k = ConversionSpecifier::gArg; break; + case 'G': k = ConversionSpecifier::GArg; break; + case 'a': k = ConversionSpecifier::aArg; break; + case 'A': k = ConversionSpecifier::AArg; break; + case 'c': k = ConversionSpecifier::IntAsCharArg; break; + case 's': k = ConversionSpecifier::CStrArg; break; + case 'p': k = ConversionSpecifier::VoidPtrArg; break; + case 'n': k = ConversionSpecifier::OutIntPtrArg; break; + case '%': k = ConversionSpecifier::PercentArg; break; + // Objective-C. + case '@': k = ConversionSpecifier::ObjCObjArg; break; + // Glibc specific. + case 'm': k = ConversionSpecifier::PrintErrno; break; + } + FS.setConversionSpecifier(ConversionSpecifier(conversionPosition, k)); + + if (k == ConversionSpecifier::InvalidSpecifier) { + H.HandleInvalidConversionSpecifier(FS, Beg, I - Beg); + return false; // Keep processing format specifiers. + } + return FormatSpecifierResult(Start, FS); +} + +bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H, + const char *I, const char *E) { + // Keep looking for a format specifier until we have exhausted the string. + while (I != E) { + const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E); + // Did a fail-stop error of any kind occur when parsing the specifier? + // If so, don't do any more processing. + if (FSR.shouldStop()) + return true;; + // Did we exhaust the string or encounter an error that + // we can recover from? + if (!FSR.hasValue()) + continue; + // We have a format specifier. Pass it to the callback. + if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(), + I - FSR.getStart())) + return true; + } + assert(I == E && "Format string not exhausted"); + return false; +} + +FormatStringHandler::~FormatStringHandler() {} + +//===----------------------------------------------------------------------===// +// Methods on ArgTypeResult. +//===----------------------------------------------------------------------===// + +bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { + assert(isValid()); + + if (K == UnknownTy) + return true; + + if (K == SpecificTy) { + argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + + if (T == argTy) + return true; + + if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + return T == C.UnsignedCharTy; + case BuiltinType::Char_U: + case BuiltinType::UChar: + return T == C.SignedCharTy; + case BuiltinType::Short: + return T == C.UnsignedShortTy; + case BuiltinType::UShort: + return T == C.ShortTy; + case BuiltinType::Int: + return T == C.UnsignedIntTy; + case BuiltinType::UInt: + return T == C.IntTy; + case BuiltinType::Long: + return T == C.UnsignedLongTy; + case BuiltinType::ULong: + return T == C.LongTy; + case BuiltinType::LongLong: + return T == C.UnsignedLongLongTy; + case BuiltinType::ULongLong: + return T == C.LongLongTy; + } + + return false; + } + + if (K == CStrTy) { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + + QualType pointeeTy = PT->getPointeeType(); + + if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Void: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + return true; + default: + break; + } + + return false; + } + + if (K == WCStrTy) { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + + QualType pointeeTy = PT->getPointeeType(); + return pointeeTy == C.WCharTy; + } + + return false; +} + +QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { + assert(isValid()); + if (K == SpecificTy) + return T; + if (K == CStrTy) + return C.getPointerType(C.CharTy); + if (K == WCStrTy) + return C.getPointerType(C.WCharTy); + if (K == ObjCPointerTy) + return C.ObjCBuiltinIdTy; + + return QualType(); +} + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +ArgTypeResult OptionalAmount::getArgType(ASTContext &Ctx) const { + return Ctx.IntTy; +} + +//===----------------------------------------------------------------------===// +// Methods on FormatSpecifier. +//===----------------------------------------------------------------------===// + +ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { + if (!CS.consumesDataArgument()) + return ArgTypeResult::Invalid(); + + if (CS.isIntArg()) + switch (LM) { + case AsLongDouble: + return ArgTypeResult::Invalid(); + case None: return Ctx.IntTy; + case AsChar: return Ctx.SignedCharTy; + case AsShort: return Ctx.ShortTy; + case AsLong: return Ctx.LongTy; + case AsLongLong: return Ctx.LongLongTy; + case AsIntMax: + // FIXME: Return unknown for now. + return ArgTypeResult(); + case AsSizeT: return Ctx.getSizeType(); + case AsPtrDiff: return Ctx.getPointerDiffType(); + } + + if (CS.isUIntArg()) + switch (LM) { + case AsLongDouble: + return ArgTypeResult::Invalid(); + case None: return Ctx.UnsignedIntTy; + case AsChar: return Ctx.UnsignedCharTy; + case AsShort: return Ctx.UnsignedShortTy; + case AsLong: return Ctx.UnsignedLongTy; + case AsLongLong: return Ctx.UnsignedLongLongTy; + case AsIntMax: + // FIXME: Return unknown for now. + return ArgTypeResult(); + case AsSizeT: + // FIXME: How to get the corresponding unsigned + // version of size_t? + return ArgTypeResult(); + case AsPtrDiff: + // FIXME: How to get the corresponding unsigned + // version of ptrdiff_t? + return ArgTypeResult(); + } + + if (CS.isDoubleArg()) { + if (LM == AsLongDouble) + return Ctx.LongDoubleTy; + return Ctx.DoubleTy; + } + + if (CS.getKind() == ConversionSpecifier::CStrArg) + return ArgTypeResult(LM == AsWideChar ? ArgTypeResult::WCStrTy + : ArgTypeResult::CStrTy); + + // FIXME: Handle other cases. + return ArgTypeResult(); +} + diff --git a/lib/Analysis/PthreadLockChecker.cpp b/lib/Analysis/PthreadLockChecker.cpp deleted file mode 100644 index e95095c..0000000 --- a/lib/Analysis/PthreadLockChecker.cpp +++ /dev/null @@ -1,141 +0,0 @@ -//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually -// this shouldn't be registered with GRExprEngineInternalChecks. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "GRExprEngineExperimentalChecks.h" -#include "llvm/ADT/ImmutableSet.h" - -using namespace clang; - -namespace { -class PthreadLockChecker - : public CheckerVisitor<PthreadLockChecker> { - BugType *BT; -public: - PthreadLockChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); - - void AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock); - - void ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock); - -}; -} // end anonymous namespace - -// GDM Entry for tracking lock state. -namespace { class LockSet {}; } -namespace clang { -template <> struct GRStateTrait<LockSet> : - public GRStatePartialTrait<llvm::ImmutableSet<const MemRegion*> > { - static void* GDMIndex() { return PthreadLockChecker::getTag(); } -}; -} // end clang namespace - -void clang::RegisterPthreadLockChecker(GRExprEngine &Eng) { - Eng.registerCheck(new PthreadLockChecker()); -} - - -void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, - const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - const FunctionTextRegion *R = - dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); - - if (!R) - return; - - llvm::StringRef FName = R->getDecl()->getName(); - - if (FName == "pthread_mutex_lock") { - if (CE->getNumArgs() != 1) - return; - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); - } - else if (FName == "pthread_mutex_trylock") { - if (CE->getNumArgs() != 1) - return; - AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); - } - else if (FName == "pthread_mutex_unlock") { - if (CE->getNumArgs() != 1) - return; - ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); - } -} - -void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, - SVal lock, bool isTryLock) { - - const MemRegion *lockR = lock.getAsRegion(); - if (!lockR) - return; - - const GRState *state = C.getState(); - - SVal X = state->getSVal(CE); - if (X.isUnknownOrUndef()) - return; - - DefinedSVal retVal = cast<DefinedSVal>(X); - const GRState *lockSucc = state; - - if (isTryLock) { - // Bifurcate the state, and allow a mode where the lock acquisition fails. - const GRState *lockFail; - llvm::tie(lockFail, lockSucc) = state->Assume(retVal); - assert(lockFail && lockSucc); - C.addTransition(C.GenerateNode(CE, lockFail)); - } - else { - // Assume that the return value was 0. - lockSucc = state->Assume(retVal, false); - assert(lockSucc); - } - - // Record that the lock was acquired. - lockSucc = lockSucc->add<LockSet>(lockR); - - C.addTransition(lockSucc != state ? C.GenerateNode(CE, lockSucc) : - C.getPredecessor()); -} - -void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, - SVal lock) { - - const MemRegion *lockR = lock.getAsRegion(); - if (!lockR) - return; - - const GRState *state = C.getState(); - - // Record that the lock was released. - // FIXME: Handle unlocking locks that were never acquired. This may - // require IPA for wrappers. - const GRState *unlockState = state->remove<LockSet>(lockR); - - if (state == unlockState) - return; - - C.addTransition(C.GenerateNode(CE, unlockState)); -} diff --git a/lib/Analysis/RangeConstraintManager.cpp b/lib/Analysis/RangeConstraintManager.cpp deleted file mode 100644 index 2cf3dfb..0000000 --- a/lib/Analysis/RangeConstraintManager.cpp +++ /dev/null @@ -1,359 +0,0 @@ -//== RangeConstraintManager.cpp - Manage range constraints.------*- 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 RangeConstraintManager, a class that tracks simple -// equality and inequality constraints on symbolic values of GRState. -// -//===----------------------------------------------------------------------===// - -#include "SimpleConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" -#include "clang/Analysis/ManagerRegistry.h" -#include "llvm/Support/Debug.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/ImmutableSet.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -namespace { class ConstraintRange {}; } -static int ConstraintRangeIndex = 0; - -/// A Range represents the closed range [from, to]. The caller must -/// guarantee that from <= to. Note that Range is immutable, so as not -/// to subvert RangeSet's immutability. -namespace { -class Range : public std::pair<const llvm::APSInt*, - const llvm::APSInt*> { -public: - Range(const llvm::APSInt &from, const llvm::APSInt &to) - : std::pair<const llvm::APSInt*, const llvm::APSInt*>(&from, &to) { - assert(from <= to); - } - bool Includes(const llvm::APSInt &v) const { - return *first <= v && v <= *second; - } - const llvm::APSInt &From() const { - return *first; - } - const llvm::APSInt &To() const { - return *second; - } - const llvm::APSInt *getConcreteValue() const { - return &From() == &To() ? &From() : NULL; - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddPointer(&From()); - ID.AddPointer(&To()); - } -}; - - -class RangeTrait : public llvm::ImutContainerInfo<Range> { -public: - // When comparing if one Range is less than another, we should compare - // the actual APSInt values instead of their pointers. This keeps the order - // consistent (instead of comparing by pointer values) and can potentially - // be used to speed up some of the operations in RangeSet. - static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { - return *lhs.first < *rhs.first || (!(*rhs.first < *lhs.first) && - *lhs.second < *rhs.second); - } -}; - -/// RangeSet contains a set of ranges. If the set is empty, then -/// there the value of a symbol is overly constrained and there are no -/// possible values for that symbol. -class RangeSet { - typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet; - PrimRangeSet ranges; // no need to make const, since it is an - // ImmutableSet - this allows default operator= - // to work. -public: - typedef PrimRangeSet::Factory Factory; - typedef PrimRangeSet::iterator iterator; - - RangeSet(PrimRangeSet RS) : ranges(RS) {} - RangeSet(Factory& F) : ranges(F.GetEmptySet()) {} - - iterator begin() const { return ranges.begin(); } - iterator end() const { return ranges.end(); } - - bool isEmpty() const { return ranges.isEmpty(); } - - /// Construct a new RangeSet representing '{ [from, to] }'. - RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) - : ranges(F.Add(F.GetEmptySet(), Range(from, to))) {} - - /// Profile - Generates a hash profile of this RangeSet for use - /// by FoldingSet. - void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } - - /// getConcreteValue - If a symbol is contrained to equal a specific integer - /// constant then this method returns that value. Otherwise, it returns - /// NULL. - const llvm::APSInt* getConcreteValue() const { - return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : 0; - } - - /// AddEQ - Create a new RangeSet with the additional constraint that the - /// value be equal to V. - RangeSet AddEQ(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - // Search for a range that includes 'V'. If so, return a new RangeSet - // representing { [V, V] }. - for (PrimRangeSet::iterator i = begin(), e = end(); i!=e; ++i) - if (i->Includes(V)) - return RangeSet(F, V, V); - - return RangeSet(F); - } - - /// AddNE - Create a new RangeSet with the additional constraint that the - /// value be not be equal to V. - RangeSet AddNE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = ranges; - - // FIXME: We can perhaps enhance ImmutableSet to do this search for us - // in log(N) time using the sorted property of the internal AVL tree. - for (iterator i = begin(), e = end(); i != e; ++i) { - if (i->Includes(V)) { - // Remove the old range. - newRanges = F.Remove(newRanges, *i); - // Split the old range into possibly one or two ranges. - if (V != i->From()) - newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V))); - if (V != i->To()) - newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To())); - // All of the ranges are non-overlapping, so we can stop. - break; - } - } - - return newRanges; - } - - /// AddNE - Create a new RangeSet with the additional constraint that the - /// value be less than V. - RangeSet AddLT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (iterator i = begin(), e = end() ; i != e ; ++i) { - if (i->Includes(V) && i->From() < V) - newRanges = F.Add(newRanges, Range(i->From(), BV.Sub1(V))); - else if (i->To() < V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - RangeSet AddLE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (iterator i = begin(), e = end(); i != e; ++i) { - // Strictly we should test for includes *V + 1, but no harm is - // done by this formulation - if (i->Includes(V)) - newRanges = F.Add(newRanges, Range(i->From(), V)); - else if (i->To() <= V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - RangeSet AddGT(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) { - if (i->Includes(V) && i->To() > V) - newRanges = F.Add(newRanges, Range(BV.Add1(V), i->To())); - else if (i->From() > V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - RangeSet AddGE(BasicValueFactory &BV, Factory &F, const llvm::APSInt &V) { - PrimRangeSet newRanges = F.GetEmptySet(); - - for (PrimRangeSet::iterator i = begin(), e = end(); i != e; ++i) { - // Strictly we should test for includes *V - 1, but no harm is - // done by this formulation - if (i->Includes(V)) - newRanges = F.Add(newRanges, Range(V, i->To())); - else if (i->From() >= V) - newRanges = F.Add(newRanges, *i); - } - - return newRanges; - } - - void print(llvm::raw_ostream &os) const { - bool isFirst = true; - os << "{ "; - for (iterator i = begin(), e = end(); i != e; ++i) { - if (isFirst) - isFirst = false; - else - os << ", "; - - os << '[' << i->From().toString(10) << ", " << i->To().toString(10) - << ']'; - } - os << " }"; - } - - bool operator==(const RangeSet &other) const { - return ranges == other.ranges; - } -}; -} // end anonymous namespace - -typedef llvm::ImmutableMap<SymbolRef,RangeSet> ConstraintRangeTy; - -namespace clang { -template<> -struct GRStateTrait<ConstraintRange> - : public GRStatePartialTrait<ConstraintRangeTy> { - static inline void* GDMIndex() { return &ConstraintRangeIndex; } -}; -} - -namespace { -class RangeConstraintManager : public SimpleConstraintManager{ - RangeSet GetRange(const GRState *state, SymbolRef sym); -public: - RangeConstraintManager(GRSubEngine &subengine) - : SimpleConstraintManager(subengine) {} - - const GRState* AssumeSymNE(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLT(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGT(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymGE(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const GRState* AssumeSymLE(const GRState* St, SymbolRef sym, - const llvm::APSInt& V); - - const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const; - - // FIXME: Refactor into SimpleConstraintManager? - bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const { - const llvm::APSInt *i = getSymVal(St, sym); - return i ? *i == V : false; - } - - const GRState* RemoveDeadBindings(const GRState* St, SymbolReaper& SymReaper); - - void print(const GRState* St, llvm::raw_ostream& Out, - const char* nl, const char *sep); - -private: - RangeSet::Factory F; -}; - -} // end anonymous namespace - -ConstraintManager* clang::CreateRangeConstraintManager(GRStateManager&, - GRSubEngine &subeng) { - return new RangeConstraintManager(subeng); -} - -const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St, - SymbolRef sym) const { - const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym); - return T ? T->getConcreteValue() : NULL; -} - -/// Scan all symbols referenced by the constraints. If the symbol is not alive -/// as marked in LSymbols, mark it as dead in DSymbols. -const GRState* -RangeConstraintManager::RemoveDeadBindings(const GRState* state, - SymbolReaper& SymReaper) { - - ConstraintRangeTy CR = state->get<ConstraintRange>(); - ConstraintRangeTy::Factory& CRFactory = state->get_context<ConstraintRange>(); - - for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) - CR = CRFactory.Remove(CR, sym); - } - - return state->set<ConstraintRange>(CR); -} - -//===------------------------------------------------------------------------=== -// AssumeSymX methods: public interface for RangeConstraintManager. -//===------------------------------------------------------------------------===/ - -RangeSet -RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) { - if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym)) - return *V; - - // Lazily generate a new RangeSet representing all possible values for the - // given symbol type. - QualType T = state->getSymbolManager().getType(sym); - BasicValueFactory& BV = state->getBasicVals(); - return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T)); -} - -//===------------------------------------------------------------------------=== -// AssumeSymX methods: public interface for RangeConstraintManager. -//===------------------------------------------------------------------------===/ - -#define AssumeX(OP)\ -const GRState*\ -RangeConstraintManager::AssumeSym ## OP(const GRState* state, SymbolRef sym,\ - const llvm::APSInt& V){\ - const RangeSet& R = GetRange(state, sym).Add##OP(state->getBasicVals(), F, V);\ - return !R.isEmpty() ? state->set<ConstraintRange>(sym, R) : NULL;\ -} - -AssumeX(EQ) -AssumeX(NE) -AssumeX(LT) -AssumeX(GT) -AssumeX(LE) -AssumeX(GE) - -//===------------------------------------------------------------------------=== -// Pretty-printing. -//===------------------------------------------------------------------------===/ - -void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out, - const char* nl, const char *sep) { - - ConstraintRangeTy Ranges = St->get<ConstraintRange>(); - - if (Ranges.isEmpty()) - return; - - 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); - } -} diff --git a/lib/Analysis/RegionStore.cpp b/lib/Analysis/RegionStore.cpp deleted file mode 100644 index a735ed9..0000000 --- a/lib/Analysis/RegionStore.cpp +++ /dev/null @@ -1,2015 +0,0 @@ -//== RegionStore.cpp - Field-sensitive store model --------------*- 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 basic region store model. In this model, we do have field -// sensitivity. But we assume nothing about the heap shape. So recursive data -// structures are largely ignored. Basically we do 1-limiting analysis. -// Parameter pointers are assumed with no aliasing. Pointee objects of -// parameters are created lazily. -// -//===----------------------------------------------------------------------===// -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/GRStateTrait.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/Support/Optional.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/AST/CharUnits.h" - -#include "llvm/ADT/ImmutableMap.h" -#include "llvm/ADT/ImmutableList.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -#define USE_EXPLICIT_COMPOUND 0 - -//===----------------------------------------------------------------------===// -// Representation of value bindings. -//===----------------------------------------------------------------------===// - -namespace { -class BindingVal { -public: - enum BindingKind { Direct, Default }; -private: - SVal Value; - BindingKind Kind; - -public: - BindingVal(SVal V, BindingKind K) : Value(V), Kind(K) {} - - bool isDefault() const { return Kind == Default; } - - const SVal *getValue() const { return &Value; } - - const SVal *getDirectValue() const { return isDefault() ? 0 : &Value; } - - const SVal *getDefaultValue() const { return isDefault() ? &Value : 0; } - - void Profile(llvm::FoldingSetNodeID& ID) const { - Value.Profile(ID); - ID.AddInteger(Kind); - } - - inline bool operator==(const BindingVal& R) const { - return Value == R.Value && Kind == R.Kind; - } - - inline bool operator!=(const BindingVal& R) const { - return !(*this == R); - } -}; -} - -namespace llvm { -static inline -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingVal V) { - if (V.isDefault()) - os << "(default) "; - else - os << "(direct) "; - os << *V.getValue(); - return os; -} -} // end llvm namespace - -//===----------------------------------------------------------------------===// -// Representation of binding keys. -//===----------------------------------------------------------------------===// - -namespace { - class BindingKey : public std::pair<const MemRegion*, uint64_t> { -public: - explicit BindingKey(const MemRegion *r, uint64_t offset) - : std::pair<const MemRegion*,uint64_t>(r, offset) { assert(r); } - - const MemRegion *getRegion() const { return first; } - uint64_t getOffset() const { return second; } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddPointer(getRegion()); - ID.AddInteger(getOffset()); - } - - static BindingKey Make(const MemRegion *R); -}; -} // end anonymous namespace - -namespace llvm { - static inline - llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) { - os << '(' << K.getRegion() << ',' << K.getOffset() << ')'; - return os; - } -} // end llvm namespace - -//===----------------------------------------------------------------------===// -// Actual Store type. -//===----------------------------------------------------------------------===// - -typedef llvm::ImmutableMap<BindingKey, BindingVal> RegionBindings; - -//===----------------------------------------------------------------------===// -// Fine-grained control of RegionStoreManager. -//===----------------------------------------------------------------------===// - -namespace { -struct minimal_features_tag {}; -struct maximal_features_tag {}; - -class RegionStoreFeatures { - bool SupportsFields; - bool SupportsRemaining; - -public: - RegionStoreFeatures(minimal_features_tag) : - SupportsFields(false), SupportsRemaining(false) {} - - RegionStoreFeatures(maximal_features_tag) : - SupportsFields(true), SupportsRemaining(false) {} - - void enableFields(bool t) { SupportsFields = t; } - - bool supportsFields() const { return SupportsFields; } - bool supportsRemaining() const { return SupportsRemaining; } -}; -} - -//===----------------------------------------------------------------------===// -// Region "Extents" -//===----------------------------------------------------------------------===// -// -// MemRegions represent chunks of memory with a size (their "extent"). This -// GDM entry tracks the extents for regions. Extents are in bytes. -// -namespace { class RegionExtents {}; } -static int RegionExtentsIndex = 0; -namespace clang { - template<> struct GRStateTrait<RegionExtents> - : public GRStatePartialTrait<llvm::ImmutableMap<const MemRegion*, SVal> > { - static void* GDMIndex() { return &RegionExtentsIndex; } - }; -} - -//===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) { - if (ty->isAnyPointerType()) - return true; - - return ty->isIntegerType() && ty->isScalarType() && - Ctx.getTypeSize(ty) == Ctx.getTypeSize(Ctx.VoidPtrTy); -} - -//===----------------------------------------------------------------------===// -// Main RegionStore logic. -//===----------------------------------------------------------------------===// - -namespace { - -class RegionStoreSubRegionMap : public SubRegionMap { - typedef llvm::ImmutableSet<const MemRegion*> SetTy; - typedef llvm::DenseMap<const MemRegion*, SetTy> Map; - SetTy::Factory F; - Map M; -public: - bool add(const MemRegion* Parent, const MemRegion* SubRegion) { - Map::iterator I = M.find(Parent); - - if (I == M.end()) { - M.insert(std::make_pair(Parent, F.Add(F.GetEmptySet(), SubRegion))); - return true; - } - - I->second = F.Add(I->second, SubRegion); - return false; - } - - void process(llvm::SmallVectorImpl<const SubRegion*> &WL, const SubRegion *R); - - ~RegionStoreSubRegionMap() {} - - bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { - Map::const_iterator I = M.find(Parent); - - if (I == M.end()) - return true; - - llvm::ImmutableSet<const MemRegion*> S = I->second; - for (llvm::ImmutableSet<const MemRegion*>::iterator SI=S.begin(),SE=S.end(); - SI != SE; ++SI) { - if (!V.Visit(Parent, *SI)) - return false; - } - - return true; - } - - typedef SetTy::iterator iterator; - - std::pair<iterator, iterator> begin_end(const MemRegion *R) { - Map::iterator I = M.find(R); - SetTy S = I == M.end() ? F.GetEmptySet() : I->second; - return std::make_pair(S.begin(), S.end()); - } -}; - -class RegionStoreManager : public StoreManager { - const RegionStoreFeatures Features; - RegionBindings::Factory RBFactory; - - typedef llvm::DenseMap<const GRState *, RegionStoreSubRegionMap*> SMCache; - SMCache SC; - -public: - RegionStoreManager(GRStateManager& mgr, const RegionStoreFeatures &f) - : StoreManager(mgr), - Features(f), - RBFactory(mgr.getAllocator()) {} - - virtual ~RegionStoreManager() { - for (SMCache::iterator I = SC.begin(), E = SC.end(); I != E; ++I) - delete (*I).second; - } - - SubRegionMap *getSubRegionMap(const GRState *state); - - RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); - - Optional<SVal> getBinding(RegionBindings B, const MemRegion *R); - Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R); - /// getDefaultBinding - Returns an SVal* representing an optional default - /// binding associated with a region and its subregions. - Optional<SVal> getDefaultBinding(RegionBindings B, const MemRegion *R); - - /// setImplicitDefaultValue - Set the default binding for the provided - /// MemRegion to the value implicitly defined for compound literals when - /// the value is not specified. - const GRState *setImplicitDefaultValue(const GRState *state, - const MemRegion *R, - QualType T); - - /// getLValueString - Returns an SVal representing the lvalue of a - /// StringLiteral. Within RegionStore a StringLiteral has an - /// associated StringRegion, and the lvalue of a StringLiteral is - /// the lvalue of that region. - SVal getLValueString(const StringLiteral* S); - - /// getLValueCompoundLiteral - Returns an SVal representing the - /// lvalue of a compound literal. Within RegionStore a compound - /// literal has an associated region, and the lvalue of the - /// compound literal is the lvalue of that region. - SVal getLValueCompoundLiteral(const CompoundLiteralExpr*); - - /// getLValueVar - Returns an SVal that represents the lvalue of a - /// variable. Within RegionStore a variable has an associated - /// VarRegion, and the lvalue of the variable is the lvalue of that region. - SVal getLValueVar(const VarDecl *VD, const LocationContext *LC); - - SVal getLValueIvar(const ObjCIvarDecl* D, SVal Base); - - SVal getLValueField(const FieldDecl* D, SVal Base); - - SVal getLValueFieldOrIvar(const Decl* D, SVal Base); - - SVal getLValueElement(QualType elementType, SVal Offset, SVal Base); - - - /// ArrayToPointer - Emulates the "decay" of an array to a pointer - /// type. 'Array' represents the lvalue of the array being decayed - /// to a pointer, and the returned SVal represents the decayed - /// version of that lvalue (i.e., a pointer to the first element of - /// the array). This is called by GRExprEngine when evaluating - /// casts from arrays to pointers. - SVal ArrayToPointer(Loc Array); - - SVal EvalBinOp(const GRState *state, BinaryOperator::Opcode Op,Loc L, - NonLoc R, QualType resultTy); - - Store getInitialStore(const LocationContext *InitLoc) { - return RBFactory.GetEmptyMap().getRoot(); - } - - //===-------------------------------------------------------------------===// - // Binding values to regions. - //===-------------------------------------------------------------------===// - - const GRState *InvalidateRegion(const GRState *state, const MemRegion *R, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS) { - return RegionStoreManager::InvalidateRegions(state, &R, &R+1, E, Count, IS); - } - - const GRState *InvalidateRegions(const GRState *state, - const MemRegion * const *Begin, - const MemRegion * const *End, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS); - -private: - void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, - RegionStoreSubRegionMap &M); - - RegionBindings Add(RegionBindings B, BindingKey K, BindingVal V); - RegionBindings Add(RegionBindings B, const MemRegion *R, BindingVal V); - - const BindingVal *Lookup(RegionBindings B, BindingKey K); - const BindingVal *Lookup(RegionBindings B, const MemRegion *R); - - RegionBindings Remove(RegionBindings B, BindingKey K); - RegionBindings Remove(RegionBindings B, const MemRegion *R); - Store Remove(Store store, BindingKey K); - -public: - const GRState *Bind(const GRState *state, Loc LV, SVal V); - - const GRState *BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr* CL, - const LocationContext *LC, - SVal V); - - const GRState *BindDecl(const GRState *ST, const VarRegion *VR, - SVal InitVal); - - const GRState *BindDeclWithNoInit(const GRState *state, - const VarRegion *) { - return state; - } - - /// BindStruct - Bind a compound value to a structure. - const GRState *BindStruct(const GRState *, const TypedRegion* R, SVal V); - - const GRState *BindArray(const GRState *state, const TypedRegion* R, SVal V); - - /// KillStruct - Set the entire struct to unknown. - Store KillStruct(Store store, const TypedRegion* R); - - Store Remove(Store store, Loc LV); - - - //===------------------------------------------------------------------===// - // Loading values from regions. - //===------------------------------------------------------------------===// - - /// The high level logic for this method is this: - /// Retrieve (L) - /// if L has binding - /// return L's binding - /// else if L is in killset - /// return unknown - /// else - /// if L is on stack or heap - /// return undefined - /// else - /// return symbolic - SValuator::CastResult Retrieve(const GRState *state, Loc L, - QualType T = QualType()); - - SVal RetrieveElement(const GRState *state, const ElementRegion *R); - - SVal RetrieveField(const GRState *state, const FieldRegion *R); - - SVal RetrieveObjCIvar(const GRState *state, const ObjCIvarRegion *R); - - SVal RetrieveVar(const GRState *state, const VarRegion *R); - - SVal RetrieveLazySymbol(const GRState *state, const TypedRegion *R); - - SVal RetrieveFieldOrElementCommon(const GRState *state, const TypedRegion *R, - QualType Ty, const MemRegion *superR); - - /// Retrieve 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(const GRState *St, const TypedRegion* R); - - SVal RetrieveArray(const GRState *St, const TypedRegion* R); - - /// Get the state and region whose binding this region R corresponds to. - std::pair<const GRState*, const MemRegion*> - GetLazyBinding(RegionBindings B, const MemRegion *R); - - const GRState* CopyLazyBindings(nonloc::LazyCompoundVal V, - const GRState *state, - const TypedRegion *R); - - const ElementRegion *GetElementZeroRegion(const SymbolicRegion *SR, - QualType T); - - //===------------------------------------------------------------------===// - // State pruning. - //===------------------------------------------------------------------===// - - /// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values. - /// It returns a new Store with these values removed. - void RemoveDeadBindings(GRState &state, Stmt* Loc, SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); - - const GRState *EnterStackFrame(const GRState *state, - const StackFrameContext *frame); - - //===------------------------------------------------------------------===// - // Region "extents". - //===------------------------------------------------------------------===// - - const GRState *setExtent(const GRState *state,const MemRegion* R,SVal Extent); - DefinedOrUnknownSVal getSizeInElements(const GRState *state, - const MemRegion* R, QualType EleTy); - - //===------------------------------------------------------------------===// - // Utility methods. - //===------------------------------------------------------------------===// - - static inline RegionBindings GetRegionBindings(Store store) { - return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store)); - } - - void print(Store store, llvm::raw_ostream& Out, const char* nl, - const char *sep); - - void iterBindings(Store store, BindingsHandler& f) { - // FIXME: Implement. - } - - // FIXME: Remove. - BasicValueFactory& getBasicVals() { - return StateMgr.getBasicVals(); - } - - // FIXME: Remove. - ASTContext& getContext() { return StateMgr.getContext(); } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// RegionStore creation. -//===----------------------------------------------------------------------===// - -StoreManager *clang::CreateRegionStoreManager(GRStateManager& StMgr) { - RegionStoreFeatures F = maximal_features_tag(); - return new RegionStoreManager(StMgr, F); -} - -StoreManager *clang::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { - RegionStoreFeatures F = minimal_features_tag(); - F.enableFields(true); - return new RegionStoreManager(StMgr, F); -} - -void -RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL, - const SubRegion *R) { - const MemRegion *superR = R->getSuperRegion(); - if (add(superR, R)) - if (const SubRegion *sr = dyn_cast<SubRegion>(superR)) - WL.push_back(sr); -} - -RegionStoreSubRegionMap* -RegionStoreManager::getRegionStoreSubRegionMap(Store store) { - RegionBindings B = GetRegionBindings(store); - RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); - - llvm::SmallVector<const SubRegion*, 10> WL; - - for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) - if (const SubRegion *R = dyn_cast<SubRegion>(I.getKey().getRegion())) - M->process(WL, R); - - // We also need to record in the subregion map "intermediate" regions that - // don't have direct bindings but are super regions of those that do. - while (!WL.empty()) { - const SubRegion *R = WL.back(); - WL.pop_back(); - M->process(WL, R); - } - - return M; -} - -SubRegionMap *RegionStoreManager::getSubRegionMap(const GRState *state) { - return getRegionStoreSubRegionMap(state->getStore()); -} - -//===----------------------------------------------------------------------===// -// Binding invalidation. -//===----------------------------------------------------------------------===// - -void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, - const MemRegion *R, - RegionStoreSubRegionMap &M) { - RegionStoreSubRegionMap::iterator I, E; - - for (llvm::tie(I, E) = M.begin_end(R); I != E; ++I) - RemoveSubRegionBindings(B, *I, M); - - B = Remove(B, R); -} - -const GRState *RegionStoreManager::InvalidateRegions(const GRState *state, - const MemRegion * const *I, - const MemRegion * const *E, - const Expr *Ex, - unsigned Count, - InvalidatedSymbols *IS) { - ASTContext& Ctx = StateMgr.getContext(); - - // Get the mapping of regions -> subregions. - llvm::OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(state->getStore())); - - RegionBindings B = GetRegionBindings(state->getStore()); - - llvm::DenseMap<const MemRegion *, unsigned> Visited; - llvm::SmallVector<const MemRegion *, 10> WorkList; - - for ( ; I != E; ++I) { - // Strip away casts. - WorkList.push_back((*I)->StripCasts()); - } - - while (!WorkList.empty()) { - const MemRegion *R = WorkList.back(); - WorkList.pop_back(); - - // Have we visited this region before? - unsigned &visited = Visited[R]; - if (visited) - continue; - visited = 1; - - // Add subregions to work list. - RegionStoreSubRegionMap::iterator I, E; - for (llvm::tie(I, E) = SubRegions->begin_end(R); I!=E; ++I) - WorkList.push_back(*I); - - // Get the old binding. Is it a region? If so, add it to the worklist. - if (Optional<SVal> V = getDirectBinding(B, R)) { - if (const MemRegion *RV = V->getAsRegion()) - WorkList.push_back(RV); - - // A symbol? Mark it touched by the invalidation. - if (IS) { - if (SymbolRef Sym = V->getAsSymbol()) - IS->insert(Sym); - } - } - - // Symbolic region? Mark that symbol touched by the invalidation. - if (IS) { - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) - IS->insert(SR->getSymbol()); - } - - // BlockDataRegion? If so, invalidate captured variables that are passed - // by reference. - if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { - for (BlockDataRegion::referenced_vars_iterator - I = BR->referenced_vars_begin(), E = BR->referenced_vars_end() ; - I != E; ++I) { - const VarRegion *VR = *I; - if (VR->getDecl()->getAttr<BlocksAttr>()) - WorkList.push_back(VR); - } - continue; - } - - // Handle the region itself. - if (isa<AllocaRegion>(R) || isa<SymbolicRegion>(R)) { - // Invalidate the region by setting its default value to - // conjured symbol. The type of the symbol is irrelavant. - DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(R, Ex, Ctx.IntTy, - Count); - B = Add(B, R, BindingVal(V, BindingVal::Default)); - continue; - } - - if (!R->isBoundable()) - continue; - - const TypedRegion *TR = cast<TypedRegion>(R); - QualType T = TR->getValueType(Ctx); - - if (const RecordType *RT = T->getAsStructureType()) { - const RecordDecl *RD = RT->getDecl()->getDefinition(Ctx); - - // No record definition. There is nothing we can do. - if (!RD) - continue; - - // Invalidate the region by setting its default value to - // conjured symbol. The type of the symbol is irrelavant. - DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(R, Ex, Ctx.IntTy, - Count); - B = Add(B, R, BindingVal(V, BindingVal::Default)); - continue; - } - - if (const ArrayType *AT = Ctx.getAsArrayType(T)) { - // Set the default value of the array to conjured symbol. - DefinedOrUnknownSVal V = - ValMgr.getConjuredSymbolVal(R, Ex, AT->getElementType(), Count); - B = Add(B, R, BindingVal(V, BindingVal::Default)); - continue; - } - - if ((isa<FieldRegion>(R)||isa<ElementRegion>(R)||isa<ObjCIvarRegion>(R)) - && Visited[cast<SubRegion>(R)->getSuperRegion()]) { - // For fields and elements whose super region has also been invalidated, - // only remove the old binding. The super region will get set with a - // default value from which we can lazily derive a new symbolic value. - B = Remove(B, R); - continue; - } - - // Invalidate the binding. - DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(R, Ex, T, Count); - assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); - B = Add(B, R, BindingVal(V, BindingVal::Direct)); - } - - // Create a new state with the updated bindings. - return state->makeWithStore(B.getRoot()); -} - -//===----------------------------------------------------------------------===// -// getLValueXXX methods. -//===----------------------------------------------------------------------===// - -/// getLValueString - Returns an SVal representing the lvalue of a -/// StringLiteral. Within RegionStore a StringLiteral has an -/// associated StringRegion, and the lvalue of a StringLiteral is the -/// lvalue of that region. -SVal RegionStoreManager::getLValueString(const StringLiteral* S) { - return loc::MemRegionVal(MRMgr.getStringRegion(S)); -} - -/// getLValueVar - Returns an SVal that represents the lvalue of a -/// variable. Within RegionStore a variable has an associated -/// VarRegion, and the lvalue of the variable is the lvalue of that region. -SVal RegionStoreManager::getLValueVar(const VarDecl *VD, - const LocationContext *LC) { - return loc::MemRegionVal(MRMgr.getVarRegion(VD, LC)); -} - -SVal RegionStoreManager::getLValueIvar(const ObjCIvarDecl* D, SVal Base) { - return getLValueFieldOrIvar(D, Base); -} - -SVal RegionStoreManager::getLValueField(const FieldDecl* D, SVal Base) { - return getLValueFieldOrIvar(D, Base); -} - -SVal RegionStoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) { - if (Base.isUnknownOrUndef()) - return Base; - - Loc BaseL = cast<Loc>(Base); - const MemRegion* BaseR = 0; - - switch (BaseL.getSubKind()) { - case loc::MemRegionKind: - BaseR = cast<loc::MemRegionVal>(BaseL).getRegion(); - break; - - case loc::GotoLabelKind: - // These are anormal cases. Flag an undefined value. - return UndefinedVal(); - - case loc::ConcreteIntKind: - // While these seem funny, this can happen through casts. - // FIXME: What we should return is the field offset. For example, - // add the field offset to the integer value. That way funny things - // like this work properly: &(((struct foo *) 0xa)->f) - return Base; - - default: - assert(0 && "Unhandled Base."); - return Base; - } - - // NOTE: We must have this check first because ObjCIvarDecl is a subclass - // of FieldDecl. - if (const ObjCIvarDecl *ID = dyn_cast<ObjCIvarDecl>(D)) - return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR)); - - return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR)); -} - -SVal RegionStoreManager::getLValueElement(QualType elementType, SVal Offset, - SVal Base) { - - // If the base is an unknown or undefined value, just return it back. - // FIXME: For absolute pointer addresses, we just return that value back as - // well, although in reality we should return the offset added to that - // value. - if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base)) - return Base; - - // Only handle integer offsets... for now. - if (!isa<nonloc::ConcreteInt>(Offset)) - return UnknownVal(); - - const MemRegion* BaseRegion = cast<loc::MemRegionVal>(Base).getRegion(); - - // Pointer of any type can be cast and used as array base. - const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion); - - // Convert the offset to the appropriate size and signedness. - Offset = ValMgr.convertToArrayIndex(Offset); - - if (!ElemR) { - // - // If the base region is not an ElementRegion, create one. - // This can happen in the following example: - // - // char *p = __builtin_alloc(10); - // p[1] = 8; - // - // Observe that 'p' binds to an AllocaRegion. - // - return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, - BaseRegion, getContext())); - } - - SVal BaseIdx = ElemR->getIndex(); - - if (!isa<nonloc::ConcreteInt>(BaseIdx)) - return UnknownVal(); - - const llvm::APSInt& BaseIdxI = cast<nonloc::ConcreteInt>(BaseIdx).getValue(); - const llvm::APSInt& OffI = cast<nonloc::ConcreteInt>(Offset).getValue(); - assert(BaseIdxI.isSigned()); - - // Compute the new index. - SVal NewIdx = nonloc::ConcreteInt(getBasicVals().getValue(BaseIdxI + OffI)); - - // Construct the new ElementRegion. - const MemRegion *ArrayR = ElemR->getSuperRegion(); - return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, - getContext())); -} - -//===----------------------------------------------------------------------===// -// Extents for regions. -//===----------------------------------------------------------------------===// - -DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, - const MemRegion *R, - QualType EleTy) { - - switch (R->getKind()) { - case MemRegion::CXXThisRegionKind: - assert(0 && "Cannot get size of 'this' region"); - case MemRegion::GenericMemSpaceRegionKind: - case MemRegion::StackLocalsSpaceRegionKind: - case MemRegion::StackArgumentsSpaceRegionKind: - case MemRegion::HeapSpaceRegionKind: - case MemRegion::GlobalsSpaceRegionKind: - case MemRegion::UnknownSpaceRegionKind: - assert(0 && "Cannot index into a MemSpace"); - return UnknownVal(); - - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: - // Technically this can happen if people do funny things with casts. - return UnknownVal(); - - // Not yet handled. - case MemRegion::AllocaRegionKind: - case MemRegion::CompoundLiteralRegionKind: - case MemRegion::ElementRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::CXXObjectRegionKind: - return UnknownVal(); - - case MemRegion::SymbolicRegionKind: { - const SVal *Size = state->get<RegionExtents>(R); - if (!Size) - return UnknownVal(); - const nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(Size); - if (!CI) - return UnknownVal(); - - CharUnits RegionSize = - CharUnits::fromQuantity(CI->getValue().getSExtValue()); - CharUnits EleSize = getContext().getTypeSizeInChars(EleTy); - assert(RegionSize % EleSize == 0); - - return ValMgr.makeIntVal(RegionSize / EleSize, false); - } - - case MemRegion::StringRegionKind: { - const StringLiteral* Str = cast<StringRegion>(R)->getStringLiteral(); - // We intentionally made the size value signed because it participates in - // operations with signed indices. - return ValMgr.makeIntVal(Str->getByteLength()+1, false); - } - - case MemRegion::VarRegionKind: { - const VarRegion* VR = cast<VarRegion>(R); - // Get the type of the variable. - QualType T = VR->getDesugaredValueType(getContext()); - - // FIXME: Handle variable-length arrays. - if (isa<VariableArrayType>(T)) - return UnknownVal(); - - if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(T)) { - // return the size as signed integer. - return ValMgr.makeIntVal(CAT->getSize(), false); - } - - // Clients can use ordinary variables as if they were arrays. These - // essentially are arrays of size 1. - return ValMgr.makeIntVal(1, false); - } - } - - assert(0 && "Unreachable"); - return UnknownVal(); -} - -const GRState *RegionStoreManager::setExtent(const GRState *state, - const MemRegion *region, - SVal extent) { - return state->set<RegionExtents>(region, extent); -} - -//===----------------------------------------------------------------------===// -// Location and region casting. -//===----------------------------------------------------------------------===// - -/// ArrayToPointer - Emulates the "decay" of an array to a pointer -/// type. 'Array' represents the lvalue of the array being decayed -/// to a pointer, and the returned SVal represents the decayed -/// version of that lvalue (i.e., a pointer to the first element of -/// the array). This is called by GRExprEngine when evaluating casts -/// from arrays to pointers. -SVal RegionStoreManager::ArrayToPointer(Loc Array) { - if (!isa<loc::MemRegionVal>(Array)) - return UnknownVal(); - - const MemRegion* R = cast<loc::MemRegionVal>(&Array)->getRegion(); - const TypedRegion* ArrayR = dyn_cast<TypedRegion>(R); - - if (!ArrayR) - return UnknownVal(); - - // Strip off typedefs from the ArrayRegion's ValueType. - QualType T = ArrayR->getValueType(getContext()).getDesugaredType(); - ArrayType *AT = cast<ArrayType>(T); - T = AT->getElementType(); - - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, - getContext())); -} - -//===----------------------------------------------------------------------===// -// Pointer arithmetic. -//===----------------------------------------------------------------------===// - -SVal RegionStoreManager::EvalBinOp(const GRState *state, - BinaryOperator::Opcode Op, Loc L, NonLoc R, - QualType resultTy) { - // Assume the base location is MemRegionVal. - if (!isa<loc::MemRegionVal>(L)) - return UnknownVal(); - - const MemRegion* MR = cast<loc::MemRegionVal>(L).getRegion(); - const ElementRegion *ER = 0; - - switch (MR->getKind()) { - case MemRegion::SymbolicRegionKind: { - const SymbolicRegion *SR = cast<SymbolicRegion>(MR); - SymbolRef Sym = SR->getSymbol(); - QualType T = Sym->getType(getContext()); - QualType EleTy; - - if (const PointerType *PT = T->getAs<PointerType>()) - EleTy = PT->getPointeeType(); - else - EleTy = T->getAs<ObjCObjectPointerType>()->getPointeeType(); - - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, getContext()); - break; - } - case MemRegion::AllocaRegionKind: { - const AllocaRegion *AR = cast<AllocaRegion>(MR); - QualType T = getContext().CharTy; // Create an ElementRegion of bytes. - QualType EleTy = T->getAs<PointerType>()->getPointeeType(); - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, getContext()); - break; - } - - case MemRegion::ElementRegionKind: { - ER = cast<ElementRegion>(MR); - break; - } - - // Not yet handled. - case MemRegion::VarRegionKind: - case MemRegion::StringRegionKind: { - - } - // Fall-through. - case MemRegion::CompoundLiteralRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::CXXObjectRegionKind: - return UnknownVal(); - - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: - // Technically this can happen if people do funny things with casts. - return UnknownVal(); - - case MemRegion::CXXThisRegionKind: - assert(0 && - "Cannot perform pointer arithmetic on implicit argument 'this'"); - case MemRegion::GenericMemSpaceRegionKind: - case MemRegion::StackLocalsSpaceRegionKind: - case MemRegion::StackArgumentsSpaceRegionKind: - case MemRegion::HeapSpaceRegionKind: - case MemRegion::GlobalsSpaceRegionKind: - case MemRegion::UnknownSpaceRegionKind: - assert(0 && "Cannot perform pointer arithmetic on a MemSpace"); - return UnknownVal(); - } - - SVal Idx = ER->getIndex(); - nonloc::ConcreteInt* Base = dyn_cast<nonloc::ConcreteInt>(&Idx); - - // For now, only support: - // (a) concrete integer indices that can easily be resolved - // (b) 0 + symbolic index - if (Base) { - if (nonloc::ConcreteInt *Offset = dyn_cast<nonloc::ConcreteInt>(&R)) { - // FIXME: Should use SValuator here. - SVal NewIdx = - Base->evalBinOp(ValMgr, Op, - cast<nonloc::ConcreteInt>(ValMgr.convertToArrayIndex(*Offset))); - const MemRegion* NewER = - MRMgr.getElementRegion(ER->getElementType(), NewIdx, - ER->getSuperRegion(), getContext()); - return ValMgr.makeLoc(NewER); - } - if (0 == Base->getValue()) { - const MemRegion* NewER = - MRMgr.getElementRegion(ER->getElementType(), R, - ER->getSuperRegion(), getContext()); - return ValMgr.makeLoc(NewER); - } - } - - return UnknownVal(); -} - -//===----------------------------------------------------------------------===// -// Loading values from regions. -//===----------------------------------------------------------------------===// - -Optional<SVal> RegionStoreManager::getDirectBinding(RegionBindings B, - const MemRegion *R) { - if (const BindingVal *BV = Lookup(B, R)) - return Optional<SVal>::create(BV->getDirectValue()); - - return Optional<SVal>(); -} - -Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, - const MemRegion *R) { - - if (R->isBoundable()) - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) - if (TR->getValueType(getContext())->isUnionType()) - return UnknownVal(); - - if (const BindingVal *V = Lookup(B, R)) - return Optional<SVal>::create(V->getDefaultValue()); - - return Optional<SVal>(); -} - -Optional<SVal> RegionStoreManager::getBinding(RegionBindings B, - const MemRegion *R) { - if (const BindingVal *BV = Lookup(B, R)) - return Optional<SVal>::create(BV->getValue()); - - return Optional<SVal>(); -} - -static bool IsReinterpreted(QualType RTy, QualType UsedTy, ASTContext &Ctx) { - RTy = Ctx.getCanonicalType(RTy); - UsedTy = Ctx.getCanonicalType(UsedTy); - - if (RTy == UsedTy) - return false; - - - // Recursively check the types. We basically want to see if a pointer value - // is ever reinterpreted as a non-pointer, e.g. void** and intptr_t* - // represents a reinterpretation. - if (Loc::IsLocType(RTy) && Loc::IsLocType(UsedTy)) { - const PointerType *PRTy = RTy->getAs<PointerType>(); - const PointerType *PUsedTy = UsedTy->getAs<PointerType>(); - - return PUsedTy && PRTy && - IsReinterpreted(PRTy->getPointeeType(), - PUsedTy->getPointeeType(), Ctx); - } - - return true; -} - -const ElementRegion * -RegionStoreManager::GetElementZeroRegion(const SymbolicRegion *SR, QualType T) { - ASTContext &Ctx = getContext(); - SVal idx = ValMgr.makeZeroArrayIndex(); - assert(!T.isNull()); - return MRMgr.getElementRegion(T, idx, SR, Ctx); -} - - - -SValuator::CastResult -RegionStoreManager::Retrieve(const GRState *state, Loc L, QualType T) { - - assert(!isa<UnknownVal>(L) && "location unknown"); - assert(!isa<UndefinedVal>(L) && "location undefined"); - - // FIXME: Is this even possible? Shouldn't this be treated as a null - // dereference at a higher level? - if (isa<loc::ConcreteInt>(L)) - return SValuator::CastResult(state, UndefinedVal()); - - const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion(); - - // FIXME: return symbolic value for these cases. - // Example: - // void f(int* p) { int x = *p; } - // char* p = alloca(); - // read(p); - // c = *p; - if (isa<AllocaRegion>(MR)) - return SValuator::CastResult(state, UnknownVal()); - - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) - MR = GetElementZeroRegion(SR, T); - - if (isa<CodeTextRegion>(MR)) - return SValuator::CastResult(state, UnknownVal()); - - // 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 TypedRegion *R = cast<TypedRegion>(MR); - QualType RTy = R->getValueType(getContext()); - - // FIXME: We should eventually handle funny addressing. e.g.: - // - // int x = ...; - // int *p = &x; - // char *q = (char*) p; - // char c = *q; // returns the first byte of 'x'. - // - // Such funny addressing will occur due to layering of regions. - -#if 0 - ASTContext &Ctx = getContext(); - if (!T.isNull() && IsReinterpreted(RTy, T, Ctx)) { - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - R = MRMgr.getElementRegion(T, ZeroIdx, R, Ctx); - RTy = T; - assert(Ctx.getCanonicalType(RTy) == - Ctx.getCanonicalType(R->getValueType(Ctx))); - } -#endif - - if (RTy->isStructureType()) - return SValuator::CastResult(state, RetrieveStruct(state, R)); - - // FIXME: Handle unions. - if (RTy->isUnionType()) - return SValuator::CastResult(state, UnknownVal()); - - if (RTy->isArrayType()) - return SValuator::CastResult(state, RetrieveArray(state, R)); - - // FIXME: handle Vector types. - if (RTy->isVectorType()) - return SValuator::CastResult(state, UnknownVal()); - - if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return SValuator::CastResult(state, - CastRetrievedVal(RetrieveField(state, FR), FR, - T, false)); - - if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { - // FIXME: Here we actually perform an implicit conversion from the loaded - // value to the element type. Eventually we want to compose these values - // 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 SValuator::CastResult(state, - CastRetrievedVal(RetrieveElement(state, ER), - ER, T, false)); - } - - if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { - // FIXME: Here we actually perform an implicit conversion from the loaded - // value to the ivar type. What we should model is stores to ivars - // that blow past the extent of the ivar. If the address of the ivar is - // 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 SValuator::CastResult(state, - CastRetrievedVal(RetrieveObjCIvar(state, IVR), - IVR, T, false)); - } - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - // FIXME: Here we actually perform an implicit conversion from the loaded - // value to the variable type. What we should model is stores to variables - // that blow past the extent of the variable. If the address of the - // 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 SValuator::CastResult(state, - CastRetrievedVal(RetrieveVar(state, VR), VR, T, - false)); - } - - RegionBindings B = GetRegionBindings(state->getStore()); - const BindingVal *V = Lookup(B, R); - - // Check if the region has a binding. - if (V) - if (SVal const *SV = V->getValue()) - return SValuator::CastResult(state, *SV); - - // The location does not have a bound value. This means that it has - // the value it had upon its creation and/or entry to the analyzed - // function/method. These are either symbolic values or 'undefined'. - if (R->hasStackNonParametersStorage()) { - // All stack variables are considered to have undefined values - // upon creation. All heap allocated blocks are considered to - // have undefined values as well unless they are explicitly bound - // to specific values. - return SValuator::CastResult(state, UndefinedVal()); - } - - // All other values are symbolic. - return SValuator::CastResult(state, ValMgr.getRegionValueSymbolVal(R, RTy)); -} - -std::pair<const GRState*, const MemRegion*> -RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R) { - if (Optional<SVal> OV = getDirectBinding(B, R)) - if (const nonloc::LazyCompoundVal *V = - dyn_cast<nonloc::LazyCompoundVal>(OV.getPointer())) - return std::make_pair(V->getState(), V->getRegion()); - - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const std::pair<const GRState *, const MemRegion *> &X = - GetLazyBinding(B, ER->getSuperRegion()); - - if (X.first) - return std::make_pair(X.first, - MRMgr.getElementRegionWithSuper(ER, X.second)); - } - else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { - const std::pair<const GRState *, const MemRegion *> &X = - GetLazyBinding(B, FR->getSuperRegion()); - - if (X.first) - return std::make_pair(X.first, - MRMgr.getFieldRegionWithSuper(FR, X.second)); - } - - return std::make_pair((const GRState*) 0, (const MemRegion *) 0); -} - -SVal RegionStoreManager::RetrieveElement(const GRState* state, - const ElementRegion* R) { - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - if (Optional<SVal> V = getDirectBinding(B, R)) - return *V; - - const MemRegion* superR = R->getSuperRegion(); - - // Check if the region is an element region of a string literal. - if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) { - // FIXME: Handle loads from strings where the literal is treated as - // an integer, e.g., *((unsigned int*)"hello") - ASTContext &Ctx = getContext(); - QualType T = Ctx.getAsArrayType(StrR->getValueType(Ctx))->getElementType(); - if (T != Ctx.getCanonicalType(R->getElementType())) - return UnknownVal(); - - const StringLiteral *Str = StrR->getStringLiteral(); - SVal Idx = R->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { - int64_t i = CI->getValue().getSExtValue(); - int64_t byteLength = Str->getByteLength(); - if (i > byteLength) { - // Buffer overflow checking in GRExprEngine should handle this case, - // but we shouldn't rely on it to not overflow here if that checking - // is disabled. - return UnknownVal(); - } - char c = (i == byteLength) ? '\0' : Str->getStrData()[i]; - return ValMgr.makeIntVal(c, T); - } - } - - // Check if the immediate super region has a direct binding. - if (Optional<SVal> V = getDirectBinding(B, superR)) { - if (SymbolRef parentSym = V->getAsSymbol()) - return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); - - if (V->isUnknownOrUndef()) - return *V; - - // Handle LazyCompoundVals for the immediate super region. Other cases - // are handled in 'RetrieveFieldOrElementCommon'. - if (const nonloc::LazyCompoundVal *LCV = - dyn_cast<nonloc::LazyCompoundVal>(V)) { - - R = MRMgr.getElementRegionWithSuper(R, LCV->getRegion()); - return RetrieveElement(LCV->getState(), R); - } - - // Other cases: give up. - return UnknownVal(); - } - - return RetrieveFieldOrElementCommon(state, R, R->getElementType(), superR); -} - -SVal RegionStoreManager::RetrieveField(const GRState* state, - const FieldRegion* R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - if (Optional<SVal> V = getDirectBinding(B, R)) - return *V; - - QualType Ty = R->getValueType(getContext()); - return RetrieveFieldOrElementCommon(state, R, Ty, R->getSuperRegion()); -} - -SVal RegionStoreManager::RetrieveFieldOrElementCommon(const GRState *state, - const TypedRegion *R, - QualType Ty, - const MemRegion *superR) { - - // At this point we have already checked in either RetrieveElement or - // RetrieveField if 'R' has a direct binding. - - RegionBindings B = GetRegionBindings(state->getStore()); - - while (superR) { - if (const Optional<SVal> &D = getDefaultBinding(B, superR)) { - if (SymbolRef parentSym = D->getAsSymbol()) - return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); - - if (D->isZeroConstant()) - return ValMgr.makeZeroVal(Ty); - - if (D->isUnknown()) - return *D; - - assert(0 && "Unknown default value"); - } - - // 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 (isa<FieldRegion>(superR) || isa<ElementRegion>(superR)) { - superR = cast<SubRegion>(superR)->getSuperRegion(); - continue; - } - - break; - } - - // Lazy binding? - const GRState *lazyBindingState = NULL; - const MemRegion *lazyBindingRegion = NULL; - llvm::tie(lazyBindingState, lazyBindingRegion) = GetLazyBinding(B, R); - - if (lazyBindingState) { - assert(lazyBindingRegion && "Lazy-binding region not set"); - - if (isa<ElementRegion>(R)) - return RetrieveElement(lazyBindingState, - cast<ElementRegion>(lazyBindingRegion)); - - return RetrieveField(lazyBindingState, - cast<FieldRegion>(lazyBindingRegion)); - } - - if (R->hasStackNonParametersStorage()) { - 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 TypedRegion *typedSuperR = dyn_cast<TypedRegion>(superR)) { - if (typedSuperR->getValueType(getContext())->isVectorType()) - return UnknownVal(); - } - } - - return UndefinedVal(); - } - - // All other values are symbolic. - return ValMgr.getRegionValueSymbolVal(R, Ty); -} - -SVal RegionStoreManager::RetrieveObjCIvar(const GRState* state, - const ObjCIvarRegion* R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - - if (Optional<SVal> V = getDirectBinding(B, R)) - return *V; - - const MemRegion *superR = R->getSuperRegion(); - - // Check if the super region has a default binding. - if (Optional<SVal> V = getDefaultBinding(B, superR)) { - if (SymbolRef parentSym = V->getAsSymbol()) - return ValMgr.getDerivedRegionValueSymbolVal(parentSym, R); - - // Other cases: give up. - return UnknownVal(); - } - - return RetrieveLazySymbol(state, R); -} - -SVal RegionStoreManager::RetrieveVar(const GRState *state, - const VarRegion *R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(state->getStore()); - - if (Optional<SVal> V = getDirectBinding(B, R)) - return *V; - - // Lazily derive a value for the VarRegion. - const VarDecl *VD = R->getDecl(); - - if (R->hasGlobalsOrParametersStorage() || - isa<UnknownSpaceRegion>(R->getMemorySpace())) - return ValMgr.getRegionValueSymbolVal(R, VD->getType()); - - return UndefinedVal(); -} - -SVal RegionStoreManager::RetrieveLazySymbol(const GRState *state, - const TypedRegion *R) { - - QualType valTy = R->getValueType(getContext()); - - // All other values are symbolic. - return ValMgr.getRegionValueSymbolVal(R, valTy); -} - -SVal RegionStoreManager::RetrieveStruct(const GRState *state, - const TypedRegion* R) { - QualType T = R->getValueType(getContext()); - assert(T->isStructureType()); - - const RecordType* RT = T->getAsStructureType(); - RecordDecl* RD = RT->getDecl(); - assert(RD->isDefinition()); - (void)RD; -#if USE_EXPLICIT_COMPOUND - llvm::ImmutableList<SVal> StructVal = getBasicVals().getEmptySValList(); - - // FIXME: We shouldn't use a std::vector. If RecordDecl doesn't have a - // reverse iterator, we should implement one. - std::vector<FieldDecl *> Fields(RD->field_begin(), RD->field_end()); - - for (std::vector<FieldDecl *>::reverse_iterator Field = Fields.rbegin(), - FieldEnd = Fields.rend(); - Field != FieldEnd; ++Field) { - FieldRegion* FR = MRMgr.getFieldRegion(*Field, R); - QualType FTy = (*Field)->getType(); - SVal FieldValue = Retrieve(state, loc::MemRegionVal(FR), FTy).getSVal(); - StructVal = getBasicVals().consVals(FieldValue, StructVal); - } - - return ValMgr.makeCompoundVal(T, StructVal); -#else - return ValMgr.makeLazyCompoundVal(state, R); -#endif -} - -SVal RegionStoreManager::RetrieveArray(const GRState *state, - const TypedRegion * R) { -#if USE_EXPLICIT_COMPOUND - QualType T = R->getValueType(getContext()); - ConstantArrayType* CAT = cast<ConstantArrayType>(T.getTypePtr()); - - llvm::ImmutableList<SVal> ArrayVal = getBasicVals().getEmptySValList(); - uint64_t size = CAT->getSize().getZExtValue(); - for (uint64_t i = 0; i < size; ++i) { - SVal Idx = ValMgr.makeArrayIndex(i); - ElementRegion* ER = MRMgr.getElementRegion(CAT->getElementType(), Idx, R, - getContext()); - QualType ETy = ER->getElementType(); - SVal ElementVal = Retrieve(state, loc::MemRegionVal(ER), ETy).getSVal(); - ArrayVal = getBasicVals().consVals(ElementVal, ArrayVal); - } - - return ValMgr.makeCompoundVal(T, ArrayVal); -#else - assert(isa<ConstantArrayType>(R->getValueType(getContext()))); - return ValMgr.makeLazyCompoundVal(state, R); -#endif -} - -//===----------------------------------------------------------------------===// -// Binding values to regions. -//===----------------------------------------------------------------------===// - -Store RegionStoreManager::Remove(Store store, Loc L) { - if (isa<loc::MemRegionVal>(L)) - if (const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion()) - return Remove(store, BindingKey::Make(R)); - - return store; -} - -const GRState *RegionStoreManager::Bind(const GRState *state, Loc L, SVal V) { - if (isa<loc::ConcreteInt>(L)) - return state; - - // If we get here, the location should be a region. - const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); - - // Check if the region is a struct region. - if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) - if (TR->getValueType(getContext())->isStructureType()) - return BindStruct(state, TR, V); - - // Special case: the current region represents a cast and it and the super - // region both have pointer types or intptr_t types. If so, perform the - // bind to the super region. - // This is needed to support OSAtomicCompareAndSwap and friends or other - // loads that treat integers as pointers and vis versa. - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - if (ER->getIndex().isZeroConstant()) { - if (const TypedRegion *superR = - dyn_cast<TypedRegion>(ER->getSuperRegion())) { - ASTContext &Ctx = getContext(); - QualType superTy = superR->getValueType(Ctx); - QualType erTy = ER->getValueType(Ctx); - - if (IsAnyPointerOrIntptr(superTy, Ctx) && - IsAnyPointerOrIntptr(erTy, Ctx)) { - SValuator::CastResult cr = - ValMgr.getSValuator().EvalCast(V, state, superTy, erTy); - return Bind(cr.getState(), loc::MemRegionVal(superR), cr.getSVal()); - } - // For now, just invalidate the fields of the struct/union/class. - // FIXME: Precisely handle the fields of the record. - if (superTy->isRecordType()) - return InvalidateRegion(state, superR, NULL, 0, NULL); - } - } - } - else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { - // Binding directly to a symbolic region should be treated as binding - // to element 0. - QualType T = SR->getSymbol()->getType(getContext()); - - // FIXME: Is this the right way to handle symbols that are references? - if (const PointerType *PT = T->getAs<PointerType>()) - T = PT->getPointeeType(); - else - T = T->getAs<ReferenceType>()->getPointeeType(); - - R = GetElementZeroRegion(SR, T); - } - - // Perform the binding. - RegionBindings B = GetRegionBindings(state->getStore()); - return state->makeWithStore(Add(B, R, - BindingVal(V, BindingVal::Direct)).getRoot()); -} - -const GRState *RegionStoreManager::BindDecl(const GRState *ST, - const VarRegion *VR, - SVal InitVal) { - - QualType T = VR->getDecl()->getType(); - - if (T->isArrayType()) - return BindArray(ST, VR, InitVal); - if (T->isStructureType()) - return BindStruct(ST, VR, InitVal); - - return Bind(ST, ValMgr.makeLoc(VR), InitVal); -} - -// FIXME: this method should be merged into Bind(). -const GRState * -RegionStoreManager::BindCompoundLiteral(const GRState *state, - const CompoundLiteralExpr *CL, - const LocationContext *LC, - SVal V) { - return Bind(state, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), - V); -} - -const GRState *RegionStoreManager::setImplicitDefaultValue(const GRState *state, - const MemRegion *R, - QualType T) { - Store store = state->getStore(); - RegionBindings B = GetRegionBindings(store); - SVal V; - - if (Loc::IsLocType(T)) - V = ValMgr.makeNull(); - else if (T->isIntegerType()) - V = ValMgr.makeZeroVal(T); - else if (T->isStructureType() || T->isArrayType()) { - // Set the default value to a zero constant when it is a structure - // or array. The type doesn't really matter. - V = ValMgr.makeZeroVal(ValMgr.getContext().IntTy); - } - else { - return state; - } - - return state->makeWithStore(Add(B, R, - BindingVal(V, BindingVal::Default)).getRoot()); -} - -const GRState *RegionStoreManager::BindArray(const GRState *state, - const TypedRegion* R, - SVal Init) { - - QualType T = R->getValueType(getContext()); - ConstantArrayType* CAT = cast<ConstantArrayType>(T.getTypePtr()); - QualType ElementTy = CAT->getElementType(); - - uint64_t size = CAT->getSize().getZExtValue(); - - // Check if the init expr is a StringLiteral. - if (isa<loc::MemRegionVal>(Init)) { - const MemRegion* InitR = cast<loc::MemRegionVal>(Init).getRegion(); - const StringLiteral* S = cast<StringRegion>(InitR)->getStringLiteral(); - const char* str = S->getStrData(); - unsigned len = S->getByteLength(); - unsigned j = 0; - - // Copy bytes from the string literal into the target array. Trailing bytes - // in the array that are not covered by the string literal are initialized - // to zero. - for (uint64_t i = 0; i < size; ++i, ++j) { - if (j >= len) - break; - - SVal Idx = ValMgr.makeArrayIndex(i); - const ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, - getContext()); - - SVal V = ValMgr.makeIntVal(str[j], sizeof(char)*8, true); - state = Bind(state, loc::MemRegionVal(ER), V); - } - - return state; - } - - // Handle lazy compound values. - if (nonloc::LazyCompoundVal *LCV = dyn_cast<nonloc::LazyCompoundVal>(&Init)) - return CopyLazyBindings(*LCV, state, R); - - // Remaining case: explicit compound values. - - if (Init.isUnknown()) - return setImplicitDefaultValue(state, R, ElementTy); - - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(Init); - nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - uint64_t i = 0; - - for (; i < size; ++i, ++VI) { - // The init list might be shorter than the array length. - if (VI == VE) - break; - - SVal Idx = ValMgr.makeArrayIndex(i); - const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext()); - - if (CAT->getElementType()->isStructureType()) - state = BindStruct(state, ER, *VI); - else - // FIXME: Do we need special handling of nested arrays? - state = Bind(state, ValMgr.makeLoc(ER), *VI); - } - - // If the init list is shorter than the array length, set the - // array default value. - if (i < size) - state = setImplicitDefaultValue(state, R, ElementTy); - - return state; -} - -const GRState * -RegionStoreManager::BindStruct(const GRState *state, const TypedRegion* R, - SVal V) { - - if (!Features.supportsFields()) - return state; - - QualType T = R->getValueType(getContext()); - assert(T->isStructureType()); - - const RecordType* RT = T->getAs<RecordType>(); - RecordDecl* RD = RT->getDecl(); - - if (!RD->isDefinition()) - return state; - - // Handle lazy compound values. - if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V)) - return CopyLazyBindings(*LCV, state, R); - - // We may get non-CompoundVal accidentally due to imprecise cast logic. - // Ignore them and kill the field values. - if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) - return state->makeWithStore(KillStruct(state->getStore(), R)); - - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); - nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - - RecordDecl::field_iterator FI, FE; - - for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) { - - if (VI == VE) - break; - - QualType FTy = (*FI)->getType(); - const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); - - if (FTy->isArrayType()) - state = BindArray(state, FR, *VI); - else if (FTy->isStructureType()) - state = BindStruct(state, FR, *VI); - else - state = Bind(state, ValMgr.makeLoc(FR), *VI); - } - - // There may be fewer values in the initialize list than the fields of struct. - if (FI != FE) { - Store store = state->getStore(); - RegionBindings B = GetRegionBindings(store); - B = Add(B, R, BindingVal(ValMgr.makeIntVal(0, false), BindingVal::Default)); - state = state->makeWithStore(B.getRoot()); - } - - return state; -} - -Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R) { - RegionBindings B = GetRegionBindings(store); - llvm::OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(store)); - RemoveSubRegionBindings(B, R, *SubRegions); - - // Set the default value of the struct region to "unknown". - B = Add(B, R, BindingVal(UnknownVal(), BindingVal::Default)); - - return B.getRoot(); -} - -const GRState* -RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, - const GRState *state, - const TypedRegion *R) { - - // Nuke the old bindings stemming from R. - RegionBindings B = GetRegionBindings(state->getStore()); - - llvm::OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(state->getStore())); - - // B and DVM are updated after the call to RemoveSubRegionBindings. - RemoveSubRegionBindings(B, R, *SubRegions.get()); - - // Now copy the bindings. This amounts to just binding 'V' to 'R'. This - // results in a zero-copy algorithm. - return state->makeWithStore(Add(B, R, - BindingVal(V, BindingVal::Direct)).getRoot()); -} - -//===----------------------------------------------------------------------===// -// "Raw" retrievals and bindings. -//===----------------------------------------------------------------------===// - -BindingKey BindingKey::Make(const MemRegion *R) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const RegionRawOffset &O = ER->getAsRawOffset(); - - if (O.getRegion()) - return BindingKey(O.getRegion(), O.getByteOffset()); - - // FIXME: There are some ElementRegions for which we cannot compute - // raw offsets yet, including regions with symbolic offsets. - } - - return BindingKey(R, 0); -} - -RegionBindings RegionStoreManager::Add(RegionBindings B, BindingKey K, - BindingVal V) { - return RBFactory.Add(B, K, V); -} - -RegionBindings RegionStoreManager::Add(RegionBindings B, const MemRegion *R, - BindingVal V) { - return Add(B, BindingKey::Make(R), V); -} - -const BindingVal *RegionStoreManager::Lookup(RegionBindings B, BindingKey K) { - return B.lookup(K); -} - -const BindingVal *RegionStoreManager::Lookup(RegionBindings B, - const MemRegion *R) { - return Lookup(B, BindingKey::Make(R)); -} - -RegionBindings RegionStoreManager::Remove(RegionBindings B, BindingKey K) { - return RBFactory.Remove(B, K); -} - -RegionBindings RegionStoreManager::Remove(RegionBindings B, const MemRegion *R){ - return Remove(B, BindingKey::Make(R)); -} - -Store RegionStoreManager::Remove(Store store, BindingKey K) { - RegionBindings B = GetRegionBindings(store); - return Remove(B, K).getRoot(); -} - -//===----------------------------------------------------------------------===// -// State pruning. -//===----------------------------------------------------------------------===// - -void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, - SymbolReaper& SymReaper, - llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) -{ - typedef std::pair<const GRState*, const MemRegion *> RBDNode; - - Store store = state.getStore(); - RegionBindings B = GetRegionBindings(store); - - // The backmap from regions to subregions. - llvm::OwningPtr<RegionStoreSubRegionMap> - SubRegions(getRegionStoreSubRegionMap(store)); - - // Do a pass over the regions in the store. For VarRegions we check if - // the variable is still live and if so add it to the list of live roots. - // For other regions we populate our region backmap. - llvm::SmallVector<const MemRegion*, 10> IntermediateRoots; - - // Scan the direct bindings for "intermediate" roots. - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const MemRegion *R = I.getKey().getRegion(); - IntermediateRoots.push_back(R); - } - - // Process the "intermediate" roots to find if they are referenced by - // real roots. - llvm::SmallVector<RBDNode, 10> WorkList; - llvm::SmallVector<RBDNode, 10> Postponed; - - llvm::DenseSet<const MemRegion*> IntermediateVisited; - - while (!IntermediateRoots.empty()) { - const MemRegion* R = IntermediateRoots.back(); - IntermediateRoots.pop_back(); - - if (IntermediateVisited.count(R)) - continue; - IntermediateVisited.insert(R); - - if (const VarRegion* VR = dyn_cast<VarRegion>(R)) { - if (SymReaper.isLive(Loc, VR)) - WorkList.push_back(std::make_pair(&state, VR)); - continue; - } - - if (const SymbolicRegion* SR = dyn_cast<SymbolicRegion>(R)) { - llvm::SmallVectorImpl<RBDNode> &Q = - SymReaper.isLive(SR->getSymbol()) ? WorkList : Postponed; - - Q.push_back(std::make_pair(&state, SR)); - - continue; - } - - // Add the super region for R to the worklist if it is a subregion. - if (const SubRegion* superR = - dyn_cast<SubRegion>(cast<SubRegion>(R)->getSuperRegion())) - IntermediateRoots.push_back(superR); - } - - // Enqueue the RegionRoots onto WorkList. - for (llvm::SmallVectorImpl<const MemRegion*>::iterator I=RegionRoots.begin(), - E=RegionRoots.end(); I!=E; ++I) { - WorkList.push_back(std::make_pair(&state, *I)); - } - RegionRoots.clear(); - - llvm::DenseSet<RBDNode> Visited; - -tryAgain: - while (!WorkList.empty()) { - RBDNode N = WorkList.back(); - WorkList.pop_back(); - - // Have we visited this node before? - if (Visited.count(N)) - continue; - Visited.insert(N); - - const MemRegion *R = N.second; - const GRState *state_N = N.first; - - // Enqueue subregions. - RegionStoreSubRegionMap *M; - - if (&state == state_N) - M = SubRegions.get(); - else { - RegionStoreSubRegionMap *& SM = SC[state_N]; - if (!SM) - SM = getRegionStoreSubRegionMap(state_N->getStore()); - M = SM; - } - - RegionStoreSubRegionMap::iterator I, E; - for (llvm::tie(I, E) = M->begin_end(R); I != E; ++I) - WorkList.push_back(std::make_pair(state_N, *I)); - - // Enqueue the super region. - if (const SubRegion *SR = dyn_cast<SubRegion>(R)) { - const MemRegion *superR = SR->getSuperRegion(); - if (!isa<MemSpaceRegion>(superR)) { - // If 'R' is a field or an element, we want to keep the bindings - // for the other fields and elements around. The reason is that - // pointer arithmetic can get us to the other fields or elements. - assert(isa<FieldRegion>(R) || isa<ElementRegion>(R) - || isa<ObjCIvarRegion>(R)); - WorkList.push_back(std::make_pair(state_N, superR)); - } - } - - // Mark the symbol for any live SymbolicRegion as "live". This means we - // should continue to track that symbol. - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) - SymReaper.markLive(SymR->getSymbol()); - - // For BlockDataRegions, enqueue the VarRegions for variables marked - // with __block (passed-by-reference). - // via BlockDeclRefExprs. - if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) { - for (BlockDataRegion::referenced_vars_iterator - RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); - RI != RE; ++RI) { - if ((*RI)->getDecl()->getAttr<BlocksAttr>()) - WorkList.push_back(std::make_pair(state_N, *RI)); - } - // No possible data bindings on a BlockDataRegion. Continue to the - // next region in the worklist. - continue; - } - - Store store_N = state_N->getStore(); - RegionBindings B_N = GetRegionBindings(store_N); - - // Get the data binding for R (if any). - Optional<SVal> V = getBinding(B_N, R); - - if (V) { - // Check for lazy bindings. - if (const nonloc::LazyCompoundVal *LCV = - dyn_cast<nonloc::LazyCompoundVal>(V.getPointer())) { - - const LazyCompoundValData *D = LCV->getCVData(); - WorkList.push_back(std::make_pair(D->getState(), D->getRegion())); - } - else { - // Update the set of live symbols. - for (SVal::symbol_iterator SI=V->symbol_begin(), SE=V->symbol_end(); - SI!=SE;++SI) - SymReaper.markLive(*SI); - - // If V is a region, then add it to the worklist. - if (const MemRegion *RX = V->getAsRegion()) - WorkList.push_back(std::make_pair(state_N, RX)); - } - } - } - - // See if any postponed SymbolicRegions are actually live now, after - // having done a scan. - for (llvm::SmallVectorImpl<RBDNode>::iterator I = Postponed.begin(), - E = Postponed.end() ; I != E ; ++I) { - if (const SymbolicRegion *SR = cast_or_null<SymbolicRegion>(I->second)) { - if (SymReaper.isLive(SR->getSymbol())) { - WorkList.push_back(*I); - I->second = NULL; - } - } - } - - if (!WorkList.empty()) - goto tryAgain; - - // We have now scanned the store, marking reachable regions and symbols - // as live. We now remove all the regions that are dead from the store - // as well as update DSymbols with the set symbols that are now dead. - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const MemRegion* R = I.getKey().getRegion(); - // If this region live? Is so, none of its symbols are dead. - if (Visited.count(std::make_pair(&state, R))) - continue; - - // Remove this dead region from the store. - store = Remove(store, I.getKey()); - - // Mark all non-live symbols that this region references as dead. - if (const SymbolicRegion* SymR = dyn_cast<SymbolicRegion>(R)) - SymReaper.maybeDead(SymR->getSymbol()); - - SVal X = *I.getData().getValue(); - SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); - for (; SI != SE; ++SI) - SymReaper.maybeDead(*SI); - } - - // Write the store back. - state.setStore(store); -} - -GRState const *RegionStoreManager::EnterStackFrame(GRState const *state, - StackFrameContext const *frame) { - FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl()); - CallExpr const *CE = cast<CallExpr>(frame->getCallSite()); - - FunctionDecl::param_const_iterator PI = FD->param_begin(); - - CallExpr::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); - state = Bind(state, ValMgr.makeLoc(MRMgr.getVarRegion(*PI, frame)), ArgVal); - } - - return state; -} - -//===----------------------------------------------------------------------===// -// Utility methods. -//===----------------------------------------------------------------------===// - -void RegionStoreManager::print(Store store, llvm::raw_ostream& OS, - const char* nl, const char *sep) { - RegionBindings B = GetRegionBindings(store); - OS << "Store (direct and default bindings):" << nl; - - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) - OS << ' ' << I.getKey() << " : " << I.getData() << nl; -} diff --git a/lib/Analysis/ReturnPointerRangeChecker.cpp b/lib/Analysis/ReturnPointerRangeChecker.cpp deleted file mode 100644 index b0350cb..0000000 --- a/lib/Analysis/ReturnPointerRangeChecker.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ReturnPointerRangeChecker, which is a path-sensitive check -// which looks for an out-of-bound pointer being returned to callers. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" - -using namespace clang; - -namespace { -class ReturnPointerRangeChecker : - public CheckerVisitor<ReturnPointerRangeChecker> { - BuiltinBug *BT; -public: - ReturnPointerRangeChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); -}; -} - -void clang::RegisterReturnPointerRangeChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ReturnPointerRangeChecker()); -} - -void *ReturnPointerRangeChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { - const GRState *state = C.getState(); - - const Expr *RetE = RS->getRetValue(); - if (!RetE) - return; - - SVal V = state->getSVal(RetE); - const MemRegion *R = V.getAsRegion(); - if (!R) - return; - - R = R->StripCasts(); - if (!R) - return; - - const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); - if (!ER) - return; - - DefinedOrUnknownSVal &Idx = cast<DefinedOrUnknownSVal>(ER->getIndex()); - - // FIXME: All of this out-of-bounds checking should eventually be refactored - // into a common place. - - DefinedOrUnknownSVal NumElements - = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), - ER->getValueType(C.getASTContext())); - - const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); - const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); - if (StOutBound && !StInBound) { - ExplodedNode *N = C.GenerateSink(StOutBound); - - if (!N) - return; - - // FIXME: This bug correspond to CWE-466. Eventually we should have bug - // types explicitly reference such exploit categories (when applicable). - if (!BT) - BT = new BuiltinBug("Return of pointer value outside of expected range", - "Returned pointer value points outside the original object " - "(potential buffer overflow)"); - - // FIXME: It would be nice to eventually make this diagnostic more clear, - // e.g., by referencing the original declaration or by saying *why* this - // reference is outside the range. - - // Generate a report for this bug. - RangedBugReport *report = - new RangedBugReport(*BT, BT->getDescription(), N); - - report->addRange(RetE->getSourceRange()); - C.EmitReport(report); - } -} diff --git a/lib/Analysis/ReturnStackAddressChecker.cpp b/lib/Analysis/ReturnStackAddressChecker.cpp deleted file mode 100644 index 4d7e8ad..0000000 --- a/lib/Analysis/ReturnStackAddressChecker.cpp +++ /dev/null @@ -1,114 +0,0 @@ -//== ReturnStackAddressChecker.cpp ------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ReturnStackAddressChecker, which is a path-sensitive -// check which looks for the addresses of stack variables being returned to -// callers. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/ADT/SmallString.h" - -using namespace clang; - -namespace { -class ReturnStackAddressChecker : - public CheckerVisitor<ReturnStackAddressChecker> { - BuiltinBug *BT; -public: - ReturnStackAddressChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); -}; -} - -void clang::RegisterReturnStackAddressChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ReturnStackAddressChecker()); -} - -void *ReturnStackAddressChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnStackAddressChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { - - const Expr *RetE = RS->getRetValue(); - if (!RetE) - return; - - SVal V = C.getState()->getSVal(RetE); - const MemRegion *R = V.getAsRegion(); - - if (!R || !R->hasStackStorage()) - return; - - ExplodedNode *N = C.GenerateSink(); - - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Return of address to stack-allocated memory"); - - // Generate a report for this bug. - llvm::SmallString<100> buf; - llvm::raw_svector_ostream os(buf); - SourceRange range; - - // Get the base region, stripping away fields and elements. - R = R->getBaseRegion(); - - // Check if the region is a compound literal. - if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { - const CompoundLiteralExpr* CL = CR->getLiteralExpr(); - os << "Address of stack memory associated with a compound literal " - "declared on line " - << C.getSourceManager().getInstantiationLineNumber(CL->getLocStart()) - << " returned to caller"; - range = CL->getSourceRange(); - } - else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) { - const Expr* ARE = AR->getExpr(); - SourceLocation L = ARE->getLocStart(); - range = ARE->getSourceRange(); - os << "Address of stack memory allocated by call to alloca() on line " - << C.getSourceManager().getInstantiationLineNumber(L) - << " returned to caller"; - } - else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { - const BlockDecl *BD = BR->getCodeRegion()->getDecl(); - SourceLocation L = BD->getLocStart(); - range = BD->getSourceRange(); - os << "Address of stack-allocated block declared on line " - << C.getSourceManager().getInstantiationLineNumber(L) - << " returned to caller"; - } - else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << "Address of stack memory associated with local variable '" - << VR->getString() << "' returned"; - range = VR->getDecl()->getSourceRange(); - } - else { - assert(false && "Invalid region in ReturnStackAddressChecker."); - return; - } - - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(RetE->getSourceRange()); - if (range.isValid()) - report->addRange(range); - - C.EmitReport(report); -} diff --git a/lib/Analysis/ReturnUndefChecker.cpp b/lib/Analysis/ReturnUndefChecker.cpp deleted file mode 100644 index 7cd7126..0000000 --- a/lib/Analysis/ReturnUndefChecker.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines ReturnUndefChecker, which is a path-sensitive -// check which looks for undefined or garbage values being returned to the -// caller. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "llvm/ADT/SmallString.h" - -using namespace clang; - -namespace { -class ReturnUndefChecker : - public CheckerVisitor<ReturnUndefChecker> { - BuiltinBug *BT; -public: - ReturnUndefChecker() : BT(0) {} - static void *getTag(); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); -}; -} - -void clang::RegisterReturnUndefChecker(GRExprEngine &Eng) { - Eng.registerCheck(new ReturnUndefChecker()); -} - -void *ReturnUndefChecker::getTag() { - static int x = 0; return &x; -} - -void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *RS) { - - const Expr *RetE = RS->getRetValue(); - if (!RetE) - return; - - if (!C.getState()->getSVal(RetE).isUndef()) - return; - - ExplodedNode *N = C.GenerateSink(); - - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Garbage return value", - "Undefined or garbage value returned to caller"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT, BT->getDescription(), N); - - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); - - C.EmitReport(report); -} diff --git a/lib/Analysis/SVals.cpp b/lib/Analysis/SVals.cpp deleted file mode 100644 index fbdb73b..0000000 --- a/lib/Analysis/SVals.cpp +++ /dev/null @@ -1,327 +0,0 @@ -//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- 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 SVal, Loc, and NonLoc, classes that represent -// abstract r-values for use with path-sensitive value tracking. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Basic/IdentifierTable.h" - -using namespace clang; -using llvm::dyn_cast; -using llvm::cast; -using llvm::APSInt; - -//===----------------------------------------------------------------------===// -// Symbol iteration within an SVal. -//===----------------------------------------------------------------------===// - - -//===----------------------------------------------------------------------===// -// Utility methods. -//===----------------------------------------------------------------------===// - -bool SVal::hasConjuredSymbol() const { - if (const nonloc::SymbolVal* SV = dyn_cast<nonloc::SymbolVal>(this)) { - SymbolRef sym = SV->getSymbol(); - if (isa<SymbolConjured>(sym)) - return true; - } - - if (const loc::MemRegionVal *RV = dyn_cast<loc::MemRegionVal>(this)) { - const MemRegion *R = RV->getRegion(); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { - SymbolRef sym = SR->getSymbol(); - if (isa<SymbolConjured>(sym)) - return true; - } - } - - return false; -} - -const FunctionDecl *SVal::getAsFunctionDecl() const { - if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(this)) { - const MemRegion* R = X->getRegion(); - if (const FunctionTextRegion *CTR = R->getAs<FunctionTextRegion>()) - return CTR->getDecl(); - } - - return NULL; -} - -/// 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? -SymbolRef SVal::getAsLocSymbol() const { - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) { - const MemRegion *R = X->StripCasts(); - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) - return SymR->getSymbol(); - } - return NULL; -} - -/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. -/// Otherwise return 0. -// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? -SymbolRef SVal::getAsSymbol() const { - 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(); - - return getAsSymbol(); -} - -const MemRegion *SVal::getAsRegion() const { - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) - return X->getRegion(); - - if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) { - return X->getLoc().getAsRegion(); - } - - return 0; -} - -const MemRegion *loc::MemRegionVal::StripCasts() const { - const MemRegion *R = getRegion(); - 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; - } - - assert(false && "unhandled expansion case"); -} - -const GRState *nonloc::LazyCompoundVal::getState() const { - return static_cast<const LazyCompoundValData*>(Data)->getState(); -} - -const TypedRegion *nonloc::LazyCompoundVal::getRegion() const { - return static_cast<const LazyCompoundValData*>(Data)->getRegion(); -} - -//===----------------------------------------------------------------------===// -// Other Iterators. -//===----------------------------------------------------------------------===// - -nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { - return getValue()->begin(); -} - -nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { - return getValue()->end(); -} - -//===----------------------------------------------------------------------===// -// Useful predicates. -//===----------------------------------------------------------------------===// - -bool SVal::isConstant() const { - return isa<nonloc::ConcreteInt>(this) || isa<loc::ConcreteInt>(this); -} - -bool SVal::isZeroConstant() const { - if (isa<loc::ConcreteInt>(*this)) - return cast<loc::ConcreteInt>(*this).getValue() == 0; - else if (isa<nonloc::ConcreteInt>(*this)) - return cast<nonloc::ConcreteInt>(*this).getValue() == 0; - else - return false; -} - - -//===----------------------------------------------------------------------===// -// Transfer function dispatch for Non-Locs. -//===----------------------------------------------------------------------===// - -SVal nonloc::ConcreteInt::evalBinOp(ValueManager &ValMgr, - BinaryOperator::Opcode Op, - const nonloc::ConcreteInt& R) const { - const llvm::APSInt* X = - ValMgr.getBasicValueFactory().EvaluateAPSInt(Op, getValue(), R.getValue()); - - if (X) - return nonloc::ConcreteInt(*X); - else - return UndefinedVal(); -} - -nonloc::ConcreteInt -nonloc::ConcreteInt::evalComplement(ValueManager &ValMgr) const { - return ValMgr.makeIntVal(~getValue()); -} - -nonloc::ConcreteInt nonloc::ConcreteInt::evalMinus(ValueManager &ValMgr) const { - return ValMgr.makeIntVal(-getValue()); -} - -//===----------------------------------------------------------------------===// -// Transfer function dispatch for Locs. -//===----------------------------------------------------------------------===// - -SVal loc::ConcreteInt::EvalBinOp(BasicValueFactory& BasicVals, - BinaryOperator::Opcode Op, - const loc::ConcreteInt& R) const { - - assert (Op == BinaryOperator::Add || Op == BinaryOperator::Sub || - (Op >= BinaryOperator::LT && Op <= BinaryOperator::NE)); - - const llvm::APSInt* X = BasicVals.EvaluateAPSInt(Op, getValue(), R.getValue()); - - if (X) - return loc::ConcreteInt(*X); - else - return UndefinedVal(); -} - -//===----------------------------------------------------------------------===// -// Pretty-Printing. -//===----------------------------------------------------------------------===// - -void SVal::dump() const { dumpToStream(llvm::errs()); } - -void SVal::dumpToStream(llvm::raw_ostream& os) const { - switch (getBaseKind()) { - case UnknownKind: - os << "Invalid"; - break; - case NonLocKind: - cast<NonLoc>(this)->dumpToStream(os); - break; - case LocKind: - cast<Loc>(this)->dumpToStream(os); - break; - case UndefinedKind: - os << "Undefined"; - break; - default: - assert (false && "Invalid SVal."); - } -} - -void NonLoc::dumpToStream(llvm::raw_ostream& os) const { - switch (getSubKind()) { - case nonloc::ConcreteIntKind: - os << cast<nonloc::ConcreteInt>(this)->getValue().getZExtValue(); - if (cast<nonloc::ConcreteInt>(this)->getValue().isUnsigned()) - os << 'U'; - 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; - break; - } - case nonloc::LocAsIntegerKind: { - const nonloc::LocAsInteger& C = *cast<nonloc::LocAsInteger>(this); - os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; - break; - } - case nonloc::CompoundValKind: { - const nonloc::CompoundVal& C = *cast<nonloc::CompoundVal>(this); - os << "compoundVal{"; - bool first = true; - for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { - if (first) { - os << ' '; first = false; - } - else - os << ", "; - - (*I).dumpToStream(os); - } - os << "}"; - break; - } - case nonloc::LazyCompoundValKind: { - const nonloc::LazyCompoundVal &C = *cast<nonloc::LazyCompoundVal>(this); - os << "lazyCompoundVal{" << (void*) C.getState() << ',' << C.getRegion() - << '}'; - break; - } - default: - assert (false && "Pretty-printed not implemented for this NonLoc."); - break; - } -} - -void Loc::dumpToStream(llvm::raw_ostream& os) const { - switch (getSubKind()) { - case loc::ConcreteIntKind: - os << cast<loc::ConcreteInt>(this)->getValue().getZExtValue() << " (Loc)"; - break; - case loc::GotoLabelKind: - os << "&&" << cast<loc::GotoLabel>(this)->getLabel()->getID()->getName(); - break; - case loc::MemRegionKind: - os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString(); - break; - default: - assert(false && "Pretty-printing not implemented for this Loc."); - break; - } -} diff --git a/lib/Analysis/SValuator.cpp b/lib/Analysis/SValuator.cpp deleted file mode 100644 index 8392fcf..0000000 --- a/lib/Analysis/SValuator.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// SValuator.cpp - Basic class for all SValuator implementations --*- 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 SValuator, the base class for all (complete) SValuator -// implementations. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/SValuator.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -using namespace clang; - - -SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op, - SVal L, SVal R, QualType T) { - - if (L.isUndef() || R.isUndef()) - return UndefinedVal(); - - if (L.isUnknown() || R.isUnknown()) - return UnknownVal(); - - if (isa<Loc>(L)) { - if (isa<Loc>(R)) - return EvalBinOpLL(Op, cast<Loc>(L), cast<Loc>(R), T); - - return EvalBinOpLN(ST, Op, cast<Loc>(L), cast<NonLoc>(R), T); - } - - if (isa<Loc>(R)) { - // Support pointer arithmetic where the increment/decrement operand - // is on the left and the pointer on the right. - assert(Op == BinaryOperator::Add || Op == BinaryOperator::Sub); - - // Commute the operands. - return EvalBinOpLN(ST, Op, cast<Loc>(R), cast<NonLoc>(L), T); - } - - return EvalBinOpNN(ST, Op, cast<NonLoc>(L), cast<NonLoc>(R), T); -} - -DefinedOrUnknownSVal SValuator::EvalEQ(const GRState *ST, - DefinedOrUnknownSVal L, - DefinedOrUnknownSVal R) { - return cast<DefinedOrUnknownSVal>(EvalBinOp(ST, BinaryOperator::EQ, L, R, - ValMgr.getContext().IntTy)); -} - -SValuator::CastResult SValuator::EvalCast(SVal val, const GRState *state, - QualType castTy, QualType originalTy){ - - if (val.isUnknownOrUndef() || castTy == originalTy) - return CastResult(state, val); - - ASTContext &C = ValMgr.getContext(); - - // For const casts, just propagate the value. - if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) - if (C.hasSameUnqualifiedType(castTy, originalTy)) - return CastResult(state, val); - - if (castTy->isIntegerType() && originalTy->isIntegerType()) - return CastResult(state, EvalCastNL(cast<NonLoc>(val), castTy)); - - // Check for casts from pointers to integers. - if (castTy->isIntegerType() && Loc::IsLocType(originalTy)) - return CastResult(state, EvalCastL(cast<Loc>(val), castTy)); - - // Check for casts from integers to pointers. - if (Loc::IsLocType(castTy) && originalTy->isIntegerType()) { - if (nonloc::LocAsInteger *LV = dyn_cast<nonloc::LocAsInteger>(&val)) { - if (const MemRegion *R = LV->getLoc().getAsRegion()) { - StoreManager &storeMgr = ValMgr.getStateManager().getStoreManager(); - R = storeMgr.CastRegion(R, castTy); - return R ? CastResult(state, loc::MemRegionVal(R)) - : CastResult(state, UnknownVal()); - } - return CastResult(state, LV->getLoc()); - } - goto DispatchCast; - } - - // Just pass through function and block pointers. - if (originalTy->isBlockPointerType() || originalTy->isFunctionPointerType()) { - assert(Loc::IsLocType(castTy)); - return CastResult(state, val); - } - - // Check for casts from array type to another type. - if (originalTy->isArrayType()) { - // We will always decay to a pointer. - val = ValMgr.getStateManager().ArrayToPointer(cast<Loc>(val)); - - // Are we casting from an array to a pointer? If so just pass on - // the decayed value. - if (castTy->isPointerType()) - return CastResult(state, val); - - // Are we casting from an array to an integer? If so, cast the decayed - // pointer value to an integer. - assert(castTy->isIntegerType()); - - // FIXME: Keep these here for now in case we decide soon that we - // need the original decayed type. - // QualType elemTy = cast<ArrayType>(originalTy)->getElementType(); - // QualType pointerTy = C.getPointerType(elemTy); - return CastResult(state, EvalCastL(cast<Loc>(val), castTy)); - } - - // Check for casts from a region to a specific type. - if (const MemRegion *R = val.getAsRegion()) { - // FIXME: We should handle the case where we strip off view layers to get - // to a desugared type. - - assert(Loc::IsLocType(castTy)); - // We get a symbolic function pointer for a dereference of a function - // pointer, but it is of function type. Example: - - // struct FPRec { - // void (*my_func)(int * x); - // }; - // - // int bar(int x); - // - // int f1_a(struct FPRec* foo) { - // int x; - // (*foo->my_func)(&x); - // return bar(x)+1; // no-warning - // } - - assert(Loc::IsLocType(originalTy) || originalTy->isFunctionType() || - originalTy->isBlockPointerType()); - - StoreManager &storeMgr = ValMgr.getStateManager().getStoreManager(); - - // Delegate to store manager to get the result of casting a region to a - // different type. If the MemRegion* returned is NULL, this expression - // evaluates to UnknownVal. - R = storeMgr.CastRegion(R, castTy); - return R ? CastResult(state, loc::MemRegionVal(R)) - : CastResult(state, UnknownVal()); - } - -DispatchCast: - // All other cases. - return CastResult(state, - isa<Loc>(val) ? EvalCastL(cast<Loc>(val), castTy) - : EvalCastNL(cast<NonLoc>(val), castTy)); -} - -SValuator::DefinedOrUnknownCastResult -SValuator::EvalCast(DefinedOrUnknownSVal V, const GRState *ST, - QualType castTy, QualType originalType) { - SValuator::CastResult X = EvalCast((SVal) V, ST, castTy, originalType); - return DefinedOrUnknownCastResult(X.getState(), - cast<DefinedOrUnknownSVal>(X.getSVal())); -} diff --git a/lib/Analysis/SimpleConstraintManager.cpp b/lib/Analysis/SimpleConstraintManager.cpp deleted file mode 100644 index eca20d5..0000000 --- a/lib/Analysis/SimpleConstraintManager.cpp +++ /dev/null @@ -1,249 +0,0 @@ -//== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines SimpleConstraintManager, a class that holds code shared -// between BasicConstraintManager and RangeConstraintManager. -// -//===----------------------------------------------------------------------===// - -#include "SimpleConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/Analysis/PathSensitive/Checker.h" - -namespace clang { - -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; - - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { - switch (SIE->getOpcode()) { - // We don't reason yet about bitwise-constraints on symbolic values. - case BinaryOperator::And: - case BinaryOperator::Or: - case BinaryOperator::Xor: - return false; - // We don't reason yet about arithmetic constraints on symbolic values. - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::Rem: - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - return false; - // All other cases. - default: - return true; - } - } - - return false; - } - - return true; -} - -const GRState *SimpleConstraintManager::Assume(const GRState *state, - DefinedSVal Cond, - bool Assumption) { - if (isa<NonLoc>(Cond)) - return Assume(state, cast<NonLoc>(Cond), Assumption); - else - return Assume(state, cast<Loc>(Cond), Assumption); -} - -const GRState *SimpleConstraintManager::Assume(const GRState *state, Loc cond, - bool assumption) { - state = AssumeAux(state, cond, assumption); - return SU.ProcessAssume(state, cond, assumption); -} - -const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, - Loc Cond, bool Assumption) { - - BasicValueFactory &BasicVals = state->getBasicVals(); - - switch (Cond.getSubKind()) { - default: - assert (false && "'Assume' not implemented for this Loc."); - return state; - - case loc::MemRegionKind: { - // FIXME: Should this go into the storemanager? - - const MemRegion *R = cast<loc::MemRegionVal>(Cond).getRegion(); - const SubRegion *SubR = dyn_cast<SubRegion>(R); - - while (SubR) { - // FIXME: now we only find the first symbolic region. - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR)) { - if (Assumption) - return AssumeSymNE(state, SymR->getSymbol(), - BasicVals.getZeroWithPtrWidth()); - else - return AssumeSymEQ(state, SymR->getSymbol(), - BasicVals.getZeroWithPtrWidth()); - } - SubR = dyn_cast<SubRegion>(SubR->getSuperRegion()); - } - - // FALL-THROUGH. - } - - case loc::GotoLabelKind: - return Assumption ? state : NULL; - - case loc::ConcreteIntKind: { - bool b = cast<loc::ConcreteInt>(Cond).getValue() != 0; - bool isFeasible = b ? Assumption : !Assumption; - return isFeasible ? state : NULL; - } - } // end switch -} - -const GRState *SimpleConstraintManager::Assume(const GRState *state, - NonLoc cond, - bool assumption) { - state = AssumeAux(state, cond, assumption); - return SU.ProcessAssume(state, cond, assumption); -} - -const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, - NonLoc Cond, - bool Assumption) { - - // We cannot reason about SymIntExpr and SymSymExpr. - 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; - } - - BasicValueFactory &BasicVals = state->getBasicVals(); - SymbolManager &SymMgr = state->getSymbolManager(); - - switch (Cond.getSubKind()) { - default: - assert(false && "'Assume' not implemented for this NonLoc"); - - 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); - - return Assumption ? AssumeSymNE(state, sym, zero) - : AssumeSymEQ(state, sym, zero); - } - - case nonloc::SymExprValKind: { - nonloc::SymExprVal V = cast<nonloc::SymExprVal>(Cond); - if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(V.getSymbolicExpression())){ - // FIXME: This is a hack. It silently converts the RHS integer to be - // of the same type as on the left side. This should be removed once - // we support truncation/extension of symbolic values. - GRStateManager &StateMgr = state->getStateManager(); - ASTContext &Ctx = StateMgr.getContext(); - QualType LHSType = SE->getLHS()->getType(Ctx); - BasicValueFactory &BasicVals = StateMgr.getBasicVals(); - const llvm::APSInt &RHS = BasicVals.Convert(LHSType, SE->getRHS()); - SymIntExpr SENew(SE->getLHS(), SE->getOpcode(), RHS, SE->getType(Ctx)); - - return AssumeSymInt(state, Assumption, &SENew); - } - - // For all other symbolic expressions, over-approximate and consider - // the constraint feasible. - return state; - } - - case nonloc::ConcreteIntKind: { - bool b = cast<nonloc::ConcreteInt>(Cond).getValue() != 0; - bool isFeasible = b ? Assumption : !Assumption; - return isFeasible ? state : NULL; - } - - case nonloc::LocAsIntegerKind: - return AssumeAux(state, cast<nonloc::LocAsInteger>(Cond).getLoc(), - Assumption); - } // end switch -} - -const GRState *SimpleConstraintManager::AssumeSymInt(const GRState *state, - bool Assumption, - const SymIntExpr *SE) { - - - // Here we assume that LHS is a symbol. This is consistent with the - // rest of the constraint manager logic. - SymbolRef Sym = cast<SymbolData>(SE->getLHS()); - const llvm::APSInt &Int = SE->getRHS(); - - switch (SE->getOpcode()) { - default: - // No logic yet for other operators. Assume the constraint is feasible. - return state; - - case BinaryOperator::EQ: - return Assumption ? AssumeSymEQ(state, Sym, Int) - : AssumeSymNE(state, Sym, Int); - - case BinaryOperator::NE: - return Assumption ? AssumeSymNE(state, Sym, Int) - : AssumeSymEQ(state, Sym, Int); - case BinaryOperator::GT: - return Assumption ? AssumeSymGT(state, Sym, Int) - : AssumeSymLE(state, Sym, Int); - - case BinaryOperator::GE: - return Assumption ? AssumeSymGE(state, Sym, Int) - : AssumeSymLT(state, Sym, Int); - - case BinaryOperator::LT: - return Assumption ? AssumeSymLT(state, Sym, Int) - : AssumeSymGE(state, Sym, Int); - - case BinaryOperator::LE: - return Assumption ? AssumeSymLE(state, Sym, Int) - : AssumeSymGT(state, Sym, Int); - } // end switch -} - -const GRState *SimpleConstraintManager::AssumeInBound(const GRState *state, - DefinedSVal Idx, - DefinedSVal UpperBound, - bool Assumption) { - - // Only support ConcreteInt for now. - if (!(isa<nonloc::ConcreteInt>(Idx) && isa<nonloc::ConcreteInt>(UpperBound))) - return state; - - const llvm::APSInt& Zero = state->getBasicVals().getZeroWithPtrWidth(false); - llvm::APSInt IdxV = cast<nonloc::ConcreteInt>(Idx).getValue(); - // IdxV might be too narrow. - if (IdxV.getBitWidth() < Zero.getBitWidth()) - IdxV.extend(Zero.getBitWidth()); - // UBV might be too narrow, too. - llvm::APSInt UBV = cast<nonloc::ConcreteInt>(UpperBound).getValue(); - if (UBV.getBitWidth() < Zero.getBitWidth()) - UBV.extend(Zero.getBitWidth()); - - bool InBound = (Zero <= IdxV) && (IdxV < UBV); - bool isFeasible = Assumption ? InBound : !InBound; - return isFeasible ? state : NULL; -} - -} // end of namespace clang diff --git a/lib/Analysis/SimpleConstraintManager.h b/lib/Analysis/SimpleConstraintManager.h deleted file mode 100644 index 8182398..0000000 --- a/lib/Analysis/SimpleConstraintManager.h +++ /dev/null @@ -1,83 +0,0 @@ -//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Code shared between BasicConstraintManager and RangeConstraintManager. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_ANALYSIS_SIMPLE_CONSTRAINT_MANAGER_H -#define LLVM_CLANG_ANALYSIS_SIMPLE_CONSTRAINT_MANAGER_H - -#include "clang/Analysis/PathSensitive/ConstraintManager.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -namespace clang { - -class SimpleConstraintManager : public ConstraintManager { - GRSubEngine &SU; -public: - SimpleConstraintManager(GRSubEngine &subengine) : SU(subengine) {} - virtual ~SimpleConstraintManager(); - - //===------------------------------------------------------------------===// - // Common implementation for the interface provided by ConstraintManager. - //===------------------------------------------------------------------===// - - bool canReasonAbout(SVal X) const; - - const GRState *Assume(const GRState *state, DefinedSVal Cond, - bool Assumption); - - const GRState *Assume(const GRState *state, Loc Cond, bool Assumption); - - const GRState *Assume(const GRState *state, NonLoc Cond, bool Assumption); - - const GRState *AssumeSymInt(const GRState *state, bool Assumption, - const SymIntExpr *SE); - - const GRState *AssumeInBound(const GRState *state, DefinedSVal Idx, - DefinedSVal UpperBound, - bool Assumption); - -protected: - - //===------------------------------------------------------------------===// - // Interface that subclasses must implement. - //===------------------------------------------------------------------===// - - virtual const GRState *AssumeSymNE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymEQ(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymLT(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymGT(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymLE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - virtual const GRState *AssumeSymGE(const GRState *state, SymbolRef sym, - const llvm::APSInt& V) = 0; - - //===------------------------------------------------------------------===// - // Internal implementation. - //===------------------------------------------------------------------===// - - const GRState *AssumeAux(const GRState *state, Loc Cond,bool Assumption); - - const GRState *AssumeAux(const GRState *state, NonLoc Cond, bool Assumption); -}; - -} // end clang namespace - -#endif diff --git a/lib/Analysis/SimpleSValuator.cpp b/lib/Analysis/SimpleSValuator.cpp deleted file mode 100644 index 8f2f5a1..0000000 --- a/lib/Analysis/SimpleSValuator.cpp +++ /dev/null @@ -1,428 +0,0 @@ -// SimpleSValuator.cpp - A basic SValuator ------------------------*- 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 SimpleSValuator, a basic implementation of SValuator. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/SValuator.h" -#include "clang/Analysis/PathSensitive/GRState.h" - -using namespace clang; - -namespace { -class SimpleSValuator : public SValuator { -protected: - virtual SVal EvalCastNL(NonLoc val, QualType castTy); - virtual SVal EvalCastL(Loc val, QualType castTy); - -public: - SimpleSValuator(ValueManager &valMgr) : SValuator(valMgr) {} - virtual ~SimpleSValuator() {} - - virtual SVal EvalMinus(NonLoc val); - virtual SVal EvalComplement(NonLoc val); - virtual SVal EvalBinOpNN(const GRState *state, BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, QualType resultTy); - virtual SVal EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, - QualType resultTy); - virtual SVal EvalBinOpLN(const GRState *state, BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy); -}; -} // end anonymous namespace - -SValuator *clang::CreateSimpleSValuator(ValueManager &valMgr) { - return new SimpleSValuator(valMgr); -} - -//===----------------------------------------------------------------------===// -// Transfer function for Casts. -//===----------------------------------------------------------------------===// - -SVal SimpleSValuator::EvalCastNL(NonLoc val, QualType castTy) { - - bool isLocType = Loc::IsLocType(castTy); - - if (nonloc::LocAsInteger *LI = dyn_cast<nonloc::LocAsInteger>(&val)) { - if (isLocType) - return LI->getLoc(); - - // FIXME: Correctly support promotions/truncations. - ASTContext &Ctx = ValMgr.getContext(); - unsigned castSize = Ctx.getTypeSize(castTy); - if (castSize == LI->getNumBits()) - return val; - - return ValMgr.makeLocAsInteger(LI->getLoc(), castSize); - } - - if (const SymExpr *se = val.getAsSymbolicExpression()) { - ASTContext &Ctx = ValMgr.getContext(); - QualType T = Ctx.getCanonicalType(se->getType(Ctx)); - if (T == Ctx.getCanonicalType(castTy)) - return val; - - // 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()) - return val; - - return UnknownVal(); - } - - if (!isa<nonloc::ConcreteInt>(val)) - return UnknownVal(); - - // Only handle casts from integers to integers. - if (!isLocType && !castTy->isIntegerType()) - return UnknownVal(); - - llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); - i.extOrTrunc(ValMgr.getContext().getTypeSize(castTy)); - - if (isLocType) - return ValMgr.makeIntLocVal(i); - else - return ValMgr.makeIntVal(i); -} - -SVal SimpleSValuator::EvalCastL(Loc val, QualType castTy) { - - // Casts from pointers -> pointers, just return the lval. - // - // Casts from pointers -> references, just return the lval. These - // can be introduced by the frontend for corner cases, e.g - // casting from va_list* to __builtin_va_list&. - // - if (Loc::IsLocType(castTy) || castTy->isReferenceType()) - return val; - - // FIXME: Handle transparent unions where a value can be "transparently" - // lifted into a union type. - if (castTy->isUnionType()) - return UnknownVal(); - - assert(castTy->isIntegerType()); - unsigned BitWidth = ValMgr.getContext().getTypeSize(castTy); - - if (!isa<loc::ConcreteInt>(val)) - return ValMgr.makeLocAsInteger(val, BitWidth); - - llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue(); - i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); - i.extOrTrunc(BitWidth); - return ValMgr.makeIntVal(i); -} - -//===----------------------------------------------------------------------===// -// Transfer function for unary operators. -//===----------------------------------------------------------------------===// - -SVal SimpleSValuator::EvalMinus(NonLoc val) { - switch (val.getSubKind()) { - case nonloc::ConcreteIntKind: - return cast<nonloc::ConcreteInt>(val).evalMinus(ValMgr); - default: - return UnknownVal(); - } -} - -SVal SimpleSValuator::EvalComplement(NonLoc X) { - switch (X.getSubKind()) { - case nonloc::ConcreteIntKind: - return cast<nonloc::ConcreteInt>(X).evalComplement(ValMgr); - default: - return UnknownVal(); - } -} - -//===----------------------------------------------------------------------===// -// Transfer function for binary operators. -//===----------------------------------------------------------------------===// - -static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { - switch (op) { - default: - assert(false && "Invalid opcode."); - case BinaryOperator::LT: return BinaryOperator::GE; - case BinaryOperator::GT: return BinaryOperator::LE; - case BinaryOperator::LE: return BinaryOperator::GT; - case BinaryOperator::GE: return BinaryOperator::LT; - case BinaryOperator::EQ: return BinaryOperator::NE; - case BinaryOperator::NE: return BinaryOperator::EQ; - } -} - -// Equality operators for Locs. -// FIXME: All this logic will be revamped when we have MemRegion::getLocation() -// implemented. - -static SVal EvalEquality(ValueManager &ValMgr, Loc lhs, Loc rhs, bool isEqual, - QualType resultTy) { - - switch (lhs.getSubKind()) { - default: - assert(false && "EQ/NE not implemented for this Loc."); - return UnknownVal(); - - case loc::ConcreteIntKind: { - if (SymbolRef rSym = rhs.getAsSymbol()) - return ValMgr.makeNonLoc(rSym, - isEqual ? BinaryOperator::EQ - : BinaryOperator::NE, - cast<loc::ConcreteInt>(lhs).getValue(), - resultTy); - break; - } - case loc::MemRegionKind: { - if (SymbolRef lSym = lhs.getAsLocSymbol()) { - if (isa<loc::ConcreteInt>(rhs)) { - return ValMgr.makeNonLoc(lSym, - isEqual ? BinaryOperator::EQ - : BinaryOperator::NE, - cast<loc::ConcreteInt>(rhs).getValue(), - resultTy); - } - } - break; - } - - case loc::GotoLabelKind: - break; - } - - return ValMgr.makeTruthVal(isEqual ? lhs == rhs : lhs != rhs, resultTy); -} - -SVal SimpleSValuator::EvalBinOpNN(const GRState *state, - BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, - QualType resultTy) { - // Handle trivial case where left-side and right-side are the same. - if (lhs == rhs) - switch (op) { - default: - break; - case BinaryOperator::EQ: - case BinaryOperator::LE: - case BinaryOperator::GE: - return ValMgr.makeTruthVal(true, resultTy); - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::NE: - return ValMgr.makeTruthVal(false, resultTy); - } - - while (1) { - switch (lhs.getSubKind()) { - default: - return UnknownVal(); - case nonloc::LocAsIntegerKind: { - Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); - switch (rhs.getSubKind()) { - case nonloc::LocAsIntegerKind: - return EvalBinOpLL(op, lhsL, cast<nonloc::LocAsInteger>(rhs).getLoc(), - resultTy); - case nonloc::ConcreteIntKind: { - // Transform the integer into a location and compare. - ASTContext& Ctx = ValMgr.getContext(); - llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue(); - i.setIsUnsigned(true); - i.extOrTrunc(Ctx.getTypeSize(Ctx.VoidPtrTy)); - return EvalBinOpLL(op, lhsL, ValMgr.makeLoc(i), resultTy); - } - default: - switch (op) { - case BinaryOperator::EQ: - return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: - return ValMgr.makeTruthVal(true, resultTy); - default: - // This case also handles pointer arithmetic. - return UnknownVal(); - } - } - } - case nonloc::SymExprValKind: { - // Logical not? - if (!(op == BinaryOperator::EQ && rhs.isZeroConstant())) - return UnknownVal(); - - const SymExpr *symExpr = - cast<nonloc::SymExprVal>(lhs).getSymbolicExpression(); - - // Only handle ($sym op constant) for now. - if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(symExpr)) { - BinaryOperator::Opcode opc = symIntExpr->getOpcode(); - switch (opc) { - case BinaryOperator::LAnd: - case BinaryOperator::LOr: - assert(false && "Logical operators handled by branching logic."); - return UnknownVal(); - case BinaryOperator::Assign: - case BinaryOperator::MulAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::RemAssign: - case BinaryOperator::AddAssign: - case BinaryOperator::SubAssign: - case BinaryOperator::ShlAssign: - case BinaryOperator::ShrAssign: - case BinaryOperator::AndAssign: - case BinaryOperator::XorAssign: - case BinaryOperator::OrAssign: - case BinaryOperator::Comma: - assert(false && "'=' and ',' operators handled by GRExprEngine."); - return UnknownVal(); - case BinaryOperator::PtrMemD: - case BinaryOperator::PtrMemI: - assert(false && "Pointer arithmetic not handled here."); - return UnknownVal(); - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::Rem: - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - case BinaryOperator::And: - case BinaryOperator::Xor: - case BinaryOperator::Or: - // Not handled yet. - return UnknownVal(); - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::LE: - case BinaryOperator::GE: - case BinaryOperator::EQ: - case BinaryOperator::NE: - opc = NegateComparison(opc); - assert(symIntExpr->getType(ValMgr.getContext()) == resultTy); - return ValMgr.makeNonLoc(symIntExpr->getLHS(), opc, - symIntExpr->getRHS(), resultTy); - } - } - } - case nonloc::ConcreteIntKind: { - if (isa<nonloc::ConcreteInt>(rhs)) { - const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); - return lhsInt.evalBinOp(ValMgr, op, cast<nonloc::ConcreteInt>(rhs)); - } - else { - // Swap the left and right sides and flip the operator if doing so - // allows us to better reason about the expression (this is a form - // of expression canonicalization). - NonLoc tmp = rhs; - rhs = lhs; - lhs = tmp; - - switch (op) { - case BinaryOperator::LT: op = BinaryOperator::GT; continue; - case BinaryOperator::GT: op = BinaryOperator::LT; continue; - case BinaryOperator::LE: op = BinaryOperator::GE; continue; - case BinaryOperator::GE: op = BinaryOperator::LE; continue; - case BinaryOperator::EQ: - case BinaryOperator::NE: - case BinaryOperator::Add: - case BinaryOperator::Mul: - continue; - default: - return UnknownVal(); - } - } - } - case nonloc::SymbolValKind: { - nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); - SymbolRef Sym = slhs->getSymbol(); - - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (Sym->getType(ValMgr.getContext())->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. - BasicValueFactory &BVF = ValMgr.getBasicValueFactory(); - const llvm::APSInt &lhs_I = BVF.Convert(resultTy, *Constant); - lhs = nonloc::ConcreteInt(lhs_I); - - // Also promote the RHS (if necessary). - - // For shifts, it necessary promote the RHS to the result type. - 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(BVF.Convert(resultTy, rhs_I->getValue())); - } - - continue; - } - - if (isa<nonloc::ConcreteInt>(rhs)) { - return ValMgr.makeNonLoc(slhs->getSymbol(), op, - cast<nonloc::ConcreteInt>(rhs).getValue(), - resultTy); - } - - return UnknownVal(); - } - } - } -} - -SVal SimpleSValuator::EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, - QualType resultTy) { - switch (op) { - default: - return UnknownVal(); - case BinaryOperator::EQ: - case BinaryOperator::NE: - return EvalEquality(ValMgr, lhs, rhs, op == BinaryOperator::EQ, resultTy); - case BinaryOperator::LT: - case BinaryOperator::GT: - // FIXME: Generalize. For now, just handle the trivial case where - // the two locations are identical. - if (lhs == rhs) - return ValMgr.makeTruthVal(false, resultTy); - return UnknownVal(); - } -} - -SVal SimpleSValuator::EvalBinOpLN(const GRState *state, - BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy) { - // Special case: 'rhs' is an integer that has the same width as a pointer and - // we are using the integer location in a comparison. Normally this cannot be - // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 - // can generate comparisons that trigger this code. - // FIXME: Are all locations guaranteed to have pointer width? - if (BinaryOperator::isEqualityOp(op)) { - if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) { - const llvm::APSInt *x = &rhsInt->getValue(); - ASTContext &ctx = ValMgr.getContext(); - if (ctx.getTypeSize(ctx.VoidPtrTy) == x->getBitWidth()) { - // Convert the signedness of the integer (if necessary). - if (x->isSigned()) - x = &ValMgr.getBasicValueFactory().getValue(*x, true); - - return EvalBinOpLL(op, lhs, loc::ConcreteInt(*x), resultTy); - } - } - } - - // Delegate pointer arithmetic to the StoreManager. - return state->getStateManager().getStoreManager().EvalBinOp(state, op, lhs, - rhs, resultTy); -} diff --git a/lib/Analysis/Store.cpp b/lib/Analysis/Store.cpp deleted file mode 100644 index 1724a92..0000000 --- a/lib/Analysis/Store.cpp +++ /dev/null @@ -1,250 +0,0 @@ -//== Store.cpp - Interface for maps from Locations to Values ----*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defined the types Store and StoreManager. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/Store.h" -#include "clang/Analysis/PathSensitive/GRState.h" -#include "clang/AST/CharUnits.h" - -using namespace clang; - -StoreManager::StoreManager(GRStateManager &stateMgr) - : ValMgr(stateMgr.getValueManager()), StateMgr(stateMgr), - MRMgr(ValMgr.getRegionManager()) {} - -const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, - QualType EleTy, uint64_t index) { - SVal idx = ValMgr.makeArrayIndex(index); - return MRMgr.getElementRegion(EleTy, idx, Base, ValMgr.getContext()); -} - -// FIXME: Merge with the implementation of the same method in MemRegion.cpp -static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { - if (const RecordType *RT = Ty->getAs<RecordType>()) { - const RecordDecl *D = RT->getDecl(); - if (!D->getDefinition(Ctx)) - return false; - } - - return true; -} - -const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) { - - ASTContext& Ctx = StateMgr.getContext(); - - // Handle casts to Objective-C objects. - if (CastToTy->isObjCObjectPointerType()) - return R->StripCasts(); - - if (CastToTy->isBlockPointerType()) { - // FIXME: We may need different solutions, depending on the symbol - // involved. Blocks can be casted to/from 'id', as they can be treated - // as Objective-C objects. This could possibly be handled by enhancing - // our reasoning of downcasts of symbolic objects. - if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R)) - return R; - - // We don't know what to make of it. Return a NULL region, which - // will be interpretted as UnknownVal. - return NULL; - } - - // Now assume we are casting from pointer to pointer. Other cases should - // already be handled. - QualType PointeeTy = CastToTy->getAs<PointerType>()->getPointeeType(); - QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); - - // Handle casts to void*. We just pass the region through. - if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy) - return R; - - // Handle casts from compatible types. - if (R->isBoundable()) - if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); - if (CanonPointeeTy == ObjTy) - return R; - } - - // Process region cast according to the kind of the region being cast. - switch (R->getKind()) { - case MemRegion::CXXThisRegionKind: - case MemRegion::GenericMemSpaceRegionKind: - case MemRegion::StackLocalsSpaceRegionKind: - case MemRegion::StackArgumentsSpaceRegionKind: - case MemRegion::HeapSpaceRegionKind: - case MemRegion::UnknownSpaceRegionKind: - case MemRegion::GlobalsSpaceRegionKind: { - assert(0 && "Invalid region cast"); - break; - } - - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: { - // CodeTextRegion should be cast to only a function or block pointer type, - // although they can in practice be casted to anything, e.g, void*, char*, - // etc. - // Just return the region. - return R; - } - - case MemRegion::StringRegionKind: - // FIXME: Need to handle arbitrary downcasts. - case MemRegion::SymbolicRegionKind: - case MemRegion::AllocaRegionKind: - case MemRegion::CompoundLiteralRegionKind: - case MemRegion::FieldRegionKind: - case MemRegion::ObjCIvarRegionKind: - case MemRegion::VarRegionKind: - case MemRegion::CXXObjectRegionKind: - return MakeElementRegion(R, PointeeTy); - - case MemRegion::ElementRegionKind: { - // If we are casting from an ElementRegion to another type, the - // algorithm is as follows: - // - // (1) Compute the "raw offset" of the ElementRegion from the - // base region. This is done by calling 'getAsRawOffset()'. - // - // (2a) If we get a 'RegionRawOffset' after calling - // 'getAsRawOffset()', determine if the absolute offset - // can be exactly divided into chunks of the size of the - // casted-pointee type. If so, create a new ElementRegion with - // the pointee-cast type as the new ElementType and the index - // being the offset divded by the chunk size. If not, create - // a new ElementRegion at offset 0 off the raw offset region. - // - // (2b) If we don't a get a 'RegionRawOffset' after calling - // 'getAsRawOffset()', it means that we are at offset 0. - // - // FIXME: Handle symbolic raw offsets. - - const ElementRegion *elementR = cast<ElementRegion>(R); - const RegionRawOffset &rawOff = elementR->getAsRawOffset(); - const MemRegion *baseR = rawOff.getRegion(); - - // If we cannot compute a raw offset, throw up our hands and return - // a NULL MemRegion*. - if (!baseR) - return NULL; - - CharUnits off = CharUnits::fromQuantity(rawOff.getByteOffset()); - - if (off.isZero()) { - // Edge case: we are at 0 bytes off the beginning of baseR. We - // check to see if type we are casting to is the same as the base - // region. If so, just return the base region. - if (const TypedRegion *TR = dyn_cast<TypedRegion>(baseR)) { - QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); - QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); - if (CanonPointeeTy == ObjTy) - return baseR; - } - - // Otherwise, create a new ElementRegion at offset 0. - return MakeElementRegion(baseR, PointeeTy); - } - - // We have a non-zero offset from the base region. We want to determine - // if the offset can be evenly divided by sizeof(PointeeTy). If so, - // we create an ElementRegion whose index is that value. Otherwise, we - // create two ElementRegions, one that reflects a raw offset and the other - // that reflects the cast. - - // Compute the index for the new ElementRegion. - int64_t newIndex = 0; - const MemRegion *newSuperR = 0; - - // We can only compute sizeof(PointeeTy) if it is a complete type. - if (IsCompleteType(Ctx, PointeeTy)) { - // Compute the size in **bytes**. - CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy); - - // Is the offset a multiple of the size? If so, we can layer the - // ElementRegion (with elementType == PointeeTy) directly on top of - // the base region. - if (off % pointeeTySize == 0) { - newIndex = off / pointeeTySize; - newSuperR = baseR; - } - } - - if (!newSuperR) { - // Create an intermediate ElementRegion to represent the raw byte. - // This will be the super region of the final ElementRegion. - newSuperR = MakeElementRegion(baseR, Ctx.CharTy, off.getQuantity()); - } - - return MakeElementRegion(newSuperR, PointeeTy, newIndex); - } - } - - assert(0 && "unreachable"); - return 0; -} - - -/// CastRetrievedVal - Used by subclasses of StoreManager to implement -/// implicit casts that arise from loads from regions that are reinterpreted -/// as another region. -SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, - QualType castTy, bool performTestOnly) { - - if (castTy.isNull()) - return V; - - ASTContext &Ctx = ValMgr.getContext(); - - if (performTestOnly) { - // Automatically translate references to pointers. - QualType T = R->getValueType(Ctx); - if (const ReferenceType *RT = T->getAs<ReferenceType>()) - T = Ctx.getPointerType(RT->getPointeeType()); - - assert(ValMgr.getContext().hasSameUnqualifiedType(castTy, T)); - return V; - } - - if (const Loc *L = dyn_cast<Loc>(&V)) - return ValMgr.getSValuator().EvalCastL(*L, castTy); - else if (const NonLoc *NL = dyn_cast<NonLoc>(&V)) - return ValMgr.getSValuator().EvalCastNL(*NL, castTy); - - return V; -} - -const GRState *StoreManager::InvalidateRegions(const GRState *state, - const MemRegion * const *I, - const MemRegion * const *End, - const Expr *E, - unsigned Count, - InvalidatedSymbols *IS) { - for ( ; I != End ; ++I) - state = InvalidateRegion(state, *I, E, Count, IS); - - return state; -} - -//===----------------------------------------------------------------------===// -// Common getLValueXXX methods. -//===----------------------------------------------------------------------===// - -/// getLValueCompoundLiteral - Returns an SVal representing the lvalue -/// of a compound literal. Within RegionStore a compound literal -/// has an associated region, and the lvalue of the compound literal -/// is the lvalue of that region. -SVal StoreManager::getLValueCompoundLiteral(const CompoundLiteralExpr* CL, - const LocationContext *LC) { - return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)); -} diff --git a/lib/Analysis/SymbolManager.cpp b/lib/Analysis/SymbolManager.cpp deleted file mode 100644 index 3fe36b0..0000000 --- a/lib/Analysis/SymbolManager.cpp +++ /dev/null @@ -1,228 +0,0 @@ -//== SymbolManager.h - Management of Symbolic Values ------------*- 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 SymbolManager, a class that manages symbolic values -// created for use by GRExprEngine and related classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/SymbolManager.h" -#include "clang/Analysis/PathSensitive/MemRegion.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; - -void SymExpr::dump() const { - dumpToStream(llvm::errs()); -} - -static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { - switch (Op) { - default: - assert(false && "operator printing not implemented"); - break; - case BinaryOperator::Mul: os << '*' ; break; - case BinaryOperator::Div: os << '/' ; break; - case BinaryOperator::Rem: os << '%' ; break; - case BinaryOperator::Add: os << '+' ; break; - case BinaryOperator::Sub: os << '-' ; break; - case BinaryOperator::Shl: os << "<<" ; break; - case BinaryOperator::Shr: os << ">>" ; break; - case BinaryOperator::LT: os << "<" ; break; - case BinaryOperator::GT: os << '>' ; break; - case BinaryOperator::LE: os << "<=" ; break; - case BinaryOperator::GE: os << ">=" ; break; - case BinaryOperator::EQ: os << "==" ; break; - case BinaryOperator::NE: os << "!=" ; break; - case BinaryOperator::And: os << '&' ; break; - case BinaryOperator::Xor: os << '^' ; break; - case BinaryOperator::Or: os << '|' ; break; - } -} - -void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const { - os << '('; - getLHS()->dumpToStream(os); - os << ") "; - print(os, getOpcode()); - os << ' ' << getRHS().getZExtValue(); - if (getRHS().isUnsigned()) os << 'U'; -} - -void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { - os << '('; - getLHS()->dumpToStream(os); - os << ") "; - os << '('; - getRHS()->dumpToStream(os); - os << ')'; -} - -void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const { - os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; -} - -void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const { - os << "derived_$" << getSymbolID() << '{' - << getParentSymbol() << ',' << getRegion() << '}'; -} - -void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const { - os << "reg_$" << getSymbolID() << "<" << R << ">"; -} - -const SymbolRegionValue* -SymbolManager::getRegionValueSymbol(const MemRegion* R, QualType T) { - llvm::FoldingSetNodeID profile; - SymbolRegionValue::Profile(profile, R, T); - void* InsertPos; - SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); - if (!SD) { - SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>(); - new (SD) SymbolRegionValue(SymbolCounter, R, T); - DataSet.InsertNode(SD, InsertPos); - ++SymbolCounter; - } - - return cast<SymbolRegionValue>(SD); -} - -const SymbolConjured* -SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count, - const void* SymbolTag) { - - llvm::FoldingSetNodeID profile; - SymbolConjured::Profile(profile, E, T, Count, SymbolTag); - void* InsertPos; - SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); - if (!SD) { - SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>(); - new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag); - DataSet.InsertNode(SD, InsertPos); - ++SymbolCounter; - } - - return cast<SymbolConjured>(SD); -} - -const SymbolDerived* -SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, - const TypedRegion *R) { - - llvm::FoldingSetNodeID profile; - SymbolDerived::Profile(profile, parentSymbol, R); - void* InsertPos; - SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); - if (!SD) { - SD = (SymExpr*) BPAlloc.Allocate<SymbolDerived>(); - new (SD) SymbolDerived(SymbolCounter, parentSymbol, R); - DataSet.InsertNode(SD, InsertPos); - ++SymbolCounter; - } - - return cast<SymbolDerived>(SD); -} - -const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, - BinaryOperator::Opcode op, - const llvm::APSInt& v, - QualType t) { - llvm::FoldingSetNodeID ID; - SymIntExpr::Profile(ID, lhs, op, v, t); - void *InsertPos; - SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!data) { - data = (SymIntExpr*) BPAlloc.Allocate<SymIntExpr>(); - new (data) SymIntExpr(lhs, op, v, t); - DataSet.InsertNode(data, InsertPos); - } - - return cast<SymIntExpr>(data); -} - -const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, - BinaryOperator::Opcode op, - const SymExpr *rhs, - QualType t) { - llvm::FoldingSetNodeID ID; - SymSymExpr::Profile(ID, lhs, op, rhs, t); - void *InsertPos; - SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); - - if (!data) { - data = (SymSymExpr*) BPAlloc.Allocate<SymSymExpr>(); - new (data) SymSymExpr(lhs, op, rhs, t); - DataSet.InsertNode(data, InsertPos); - } - - return cast<SymSymExpr>(data); -} - -QualType SymbolConjured::getType(ASTContext&) const { - return T; -} - - -QualType SymbolDerived::getType(ASTContext& Ctx) const { - return R->getValueType(Ctx); -} - -QualType SymbolRegionValue::getType(ASTContext& C) const { - if (!T.isNull()) - return T; - - if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) - return TR->getValueType(C); - - return QualType(); -} - -SymbolManager::~SymbolManager() {} - -bool SymbolManager::canSymbolicate(QualType T) { - return Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType()); -} - -void SymbolReaper::markLive(SymbolRef sym) { - TheLiving.insert(sym); - TheDead.erase(sym); -} - -bool SymbolReaper::maybeDead(SymbolRef sym) { - if (isLive(sym)) - return false; - - TheDead.insert(sym); - return true; -} - -bool SymbolReaper::isLive(SymbolRef sym) { - if (TheLiving.count(sym)) - return true; - - if (const SymbolDerived *derived = dyn_cast<SymbolDerived>(sym)) { - if (isLive(derived->getParentSymbol())) { - markLive(sym); - return true; - } - return false; - } - - // Interogate the symbol. It may derive from an input value to - // the analyzed function/method. - return isa<SymbolRegionValue>(sym); -} - -bool SymbolReaper::isLive(const Stmt *Loc, const VarRegion *VR) const { - const StackFrameContext *SFC = VR->getStackFrame(); - return SFC == CurrentStackFrame ? Liveness.isLive(Loc, VR->getDecl()) : true; -} - -SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/Analysis/UndefBranchChecker.cpp b/lib/Analysis/UndefBranchChecker.cpp deleted file mode 100644 index c739d1a..0000000 --- a/lib/Analysis/UndefBranchChecker.cpp +++ /dev/null @@ -1,117 +0,0 @@ -//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines UndefBranchChecker, which checks for undefined branch -// condition. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/Checker.h" - -using namespace clang; - -namespace { - -class UndefBranchChecker : public Checker { - BuiltinBug *BT; - - struct FindUndefExpr { - GRStateManager& VM; - const GRState* St; - - FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} - - Expr* FindExpr(Expr* Ex) { - if (!MatchesCriteria(Ex)) - return 0; - - for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) - if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) { - Expr* E2 = FindExpr(ExI); - if (E2) return E2; - } - - return Ex; - } - - bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); } - }; - -public: - UndefBranchChecker() : BT(0) {} - static void *getTag(); - void VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng, - Stmt *Condition, void *tag); -}; - -} - -void clang::RegisterUndefBranchChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefBranchChecker()); -} - -void *UndefBranchChecker::getTag() { - static int x; - return &x; -} - -void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder, - GRExprEngine &Eng, - Stmt *Condition, void *tag) { - const GRState *state = Builder.getState(); - SVal X = state->getSVal(Condition); - if (X.isUndef()) { - ExplodedNode *N = Builder.generateNode(state, true); - if (N) { - N->markAsSink(); - if (!BT) - BT = new BuiltinBug("Branch condition evaluates to a garbage value"); - - // What's going on here: we want to highlight the subexpression of the - // condition that is the most likely source of the "uninitialized - // branch condition." We do a recursive walk of the condition's - // subexpressions and roughly look for the most nested subexpression - // that binds to Undefined. We then highlight that expression's range. - BlockEdge B = cast<BlockEdge>(N->getLocation()); - Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); - assert (Ex && "Block must have a terminator."); - - // Get the predecessor node and check if is a PostStmt with the Stmt - // being the terminator condition. We want to inspect the state - // of that node instead because it will contain main information about - // the subexpressions. - assert (!N->pred_empty()); - - // Note: any predecessor will do. They should have identical state, - // since all the BlockEdge did was act as an error sink since the value - // had to already be undefined. - ExplodedNode *PrevN = *N->pred_begin(); - ProgramPoint P = PrevN->getLocation(); - const GRState* St = N->getState(); - - if (PostStmt* PS = dyn_cast<PostStmt>(&P)) - if (PS->getStmt() == Ex) - St = PrevN->getState(); - - FindUndefExpr FindIt(Eng.getStateManager(), St); - Ex = FindIt.FindExpr(Ex); - - // Emit the bug report. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); - R->addRange(Ex->getSourceRange()); - - Eng.getBugReporter().EmitReport(R); - } - - Builder.markInfeasible(true); - Builder.markInfeasible(false); - } -} diff --git a/lib/Analysis/UndefResultChecker.cpp b/lib/Analysis/UndefResultChecker.cpp deleted file mode 100644 index acc86dd..0000000 --- a/lib/Analysis/UndefResultChecker.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines UndefResultChecker, a builtin check in GRExprEngine that -// performs checks for undefined results of non-assignment binary operators. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" - -using namespace clang; - -namespace { -class UndefResultChecker - : public CheckerVisitor<UndefResultChecker> { - - BugType *BT; - -public: - UndefResultChecker() : BT(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; -} // end anonymous namespace - -void clang::RegisterUndefResultChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefResultChecker()); -} - -void UndefResultChecker::PostVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - const GRState *state = C.getState(); - if (state->getSVal(B).isUndef()) { - // Generate an error node. - ExplodedNode *N = C.GenerateSink(); - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Result of operation is garbage or undefined"); - - llvm::SmallString<256> sbuf; - llvm::raw_svector_ostream OS(sbuf); - const Expr *Ex = NULL; - bool isLeft = true; - - if (state->getSVal(B->getLHS()).isUndef()) { - Ex = B->getLHS()->IgnoreParenCasts(); - isLeft = true; - } - else if (state->getSVal(B->getRHS()).isUndef()) { - Ex = B->getRHS()->IgnoreParenCasts(); - isLeft = false; - } - - if (Ex) { - OS << "The " << (isLeft ? "left" : "right") - << " operand of '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' is a garbage value"; - } - else { - // Neither operand was undefined, but the result is undefined. - OS << "The result of the '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' expression is undefined"; - } - EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); - if (Ex) { - report->addRange(Ex->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); - } - else - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); - C.EmitReport(report); - } -} diff --git a/lib/Analysis/UndefinedArraySubscriptChecker.cpp b/lib/Analysis/UndefinedArraySubscriptChecker.cpp deleted file mode 100644 index d6aacaf..0000000 --- a/lib/Analysis/UndefinedArraySubscriptChecker.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines UndefinedArraySubscriptChecker, a builtin check in GRExprEngine -// that performs checks for undefined array subscripts. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" -#include "GRExprEngineInternalChecks.h" - -using namespace clang; - -namespace { -class UndefinedArraySubscriptChecker - : public CheckerVisitor<UndefinedArraySubscriptChecker> { - BugType *BT; -public: - UndefinedArraySubscriptChecker() : BT(0) {} - static void *getTag() { - static int x = 0; - return &x; - } - void PreVisitArraySubscriptExpr(CheckerContext &C, - const ArraySubscriptExpr *A); -}; -} // end anonymous namespace - -void clang::RegisterUndefinedArraySubscriptChecker(GRExprEngine &Eng) { - Eng.registerCheck(new UndefinedArraySubscriptChecker()); -} - -void -UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, - const ArraySubscriptExpr *A) { - if (C.getState()->getSVal(A->getIdx()).isUndef()) { - if (ExplodedNode *N = C.GenerateSink()) { - if (!BT) - BT = new BuiltinBug("Array subscript is undefined"); - - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addRange(A->getIdx()->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, - A->getIdx()); - C.EmitReport(R); - } - } -} diff --git a/lib/Analysis/UndefinedAssignmentChecker.cpp b/lib/Analysis/UndefinedAssignmentChecker.cpp deleted file mode 100644 index 4630b82..0000000 --- a/lib/Analysis/UndefinedAssignmentChecker.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines UndefinedAssginmentChecker, a builtin check in GRExprEngine that -// checks for assigning undefined values. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" - -using namespace clang; - -namespace { -class UndefinedAssignmentChecker - : public CheckerVisitor<UndefinedAssignmentChecker> { - BugType *BT; -public: - UndefinedAssignmentChecker() : BT(0) {} - static void *getTag(); - virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE, - const Stmt *StoreE, SVal location, - SVal val); -}; -} - -void clang::RegisterUndefinedAssignmentChecker(GRExprEngine &Eng){ - Eng.registerCheck(new UndefinedAssignmentChecker()); -} - -void *UndefinedAssignmentChecker::getTag() { - static int x = 0; - return &x; -} - -void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, - const Stmt *AssignE, - const Stmt *StoreE, - SVal location, - SVal val) { - if (!val.isUndef()) - return; - - ExplodedNode *N = C.GenerateSink(); - - if (!N) - return; - - if (!BT) - BT = new BuiltinBug("Assigned value is garbage or undefined"); - - // Generate a report for this bug. - EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - - if (AssignE) { - const Expr *ex = 0; - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(AssignE)) - ex = B->getRHS(); - else if (const DeclStmt *DS = dyn_cast<DeclStmt>(AssignE)) { - const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl()); - ex = VD->getInit(); - } - if (ex) { - R->addRange(ex->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); - } - } - - C.EmitReport(R); -} - diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index 6fa4b53..bdc0e7c 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -13,7 +13,6 @@ #include "clang/Analysis/Analyses/UninitializedValues.h" #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" -#include "clang/Analysis/LocalCheckers.h" #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/FlowSensitive/DataflowSolver.h" diff --git a/lib/Analysis/VLASizeChecker.cpp b/lib/Analysis/VLASizeChecker.cpp deleted file mode 100644 index 2690d6f..0000000 --- a/lib/Analysis/VLASizeChecker.cpp +++ /dev/null @@ -1,96 +0,0 @@ -//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This defines VLASizeChecker, a builtin check in GRExprEngine that -// performs checks for declaration of VLA of undefined or zero size. -// -//===----------------------------------------------------------------------===// - -#include "GRExprEngineInternalChecks.h" -#include "clang/Analysis/PathSensitive/CheckerVisitor.h" -#include "clang/Analysis/PathSensitive/GRExprEngine.h" -#include "clang/Analysis/PathSensitive/BugReporter.h" - -using namespace clang; - -namespace { -class VLASizeChecker : public CheckerVisitor<VLASizeChecker> { - BugType *BT_zero; - BugType *BT_undef; - -public: - VLASizeChecker() : BT_zero(0), BT_undef(0) {} - static void *getTag() { static int tag = 0; return &tag; } - void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); -}; -} // end anonymous namespace - -void clang::RegisterVLASizeChecker(GRExprEngine &Eng) { - Eng.registerCheck(new VLASizeChecker()); -} - -void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { - if (!DS->isSingleDecl()) - return; - - const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); - if (!VD) - return; - - const VariableArrayType *VLA - = C.getASTContext().getAsVariableArrayType(VD->getType()); - if (!VLA) - return; - - // FIXME: Handle multi-dimensional VLAs. - const Expr* SE = VLA->getSizeExpr(); - const GRState *state = C.getState(); - SVal sizeV = state->getSVal(SE); - - if (sizeV.isUndef()) { - // Generate an error node. - ExplodedNode *N = C.GenerateSink(); - if (!N) - return; - - if (!BT_undef) - BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " - "garbage value as its size"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); - report->addRange(SE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); - C.EmitReport(report); - return; - } - - // Check if the size is zero. - DefinedOrUnknownSVal sizeD = cast<DefinedOrUnknownSVal>(sizeV); - - const GRState *stateNotZero, *stateZero; - llvm::tie(stateNotZero, stateZero) = state->Assume(sizeD); - - if (stateZero && !stateNotZero) { - ExplodedNode* N = C.GenerateSink(stateZero); - if (!BT_zero) - BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " - "size"); - - EnhancedBugReport *report = - new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); - report->addRange(SE->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); - C.EmitReport(report); - return; - } - - // From this point on, assume that the size is not zero. - C.addTransition(stateNotZero); -} diff --git a/lib/Analysis/ValueManager.cpp b/lib/Analysis/ValueManager.cpp deleted file mode 100644 index d091373..0000000 --- a/lib/Analysis/ValueManager.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//== ValueManager.cpp - Aggregate manager of symbols and SVals --*- 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 ValueManager, a class that manages symbolic values -// and SVals created for use by GRExprEngine and related classes. It -// wraps and owns SymbolManager, MemRegionManager, and BasicValueFactory. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/PathSensitive/ValueManager.h" -#include "clang/Analysis/PathSensitive/AnalysisContext.h" - -using namespace clang; -using namespace llvm; - -//===----------------------------------------------------------------------===// -// Utility methods for constructing SVals. -//===----------------------------------------------------------------------===// - -DefinedOrUnknownSVal ValueManager::makeZeroVal(QualType T) { - if (Loc::IsLocType(T)) - return makeNull(); - - if (T->isIntegerType()) - return makeIntVal(0, T); - - // FIXME: Handle floats. - // FIXME: Handle structs. - return UnknownVal(); -} - -//===----------------------------------------------------------------------===// -// Utility methods for constructing Non-Locs. -//===----------------------------------------------------------------------===// - -NonLoc ValueManager::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const APSInt& v, QualType T) { - // The Environment ensures we always get a persistent APSInt in - // BasicValueFactory, so we don't need to get the APSInt from - // BasicValueFactory again. - assert(!Loc::IsLocType(T)); - return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, v, T)); -} - -NonLoc ValueManager::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const SymExpr *rhs, QualType T) { - assert(SymMgr.getType(lhs) == SymMgr.getType(rhs)); - assert(!Loc::IsLocType(T)); - return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, T)); -} - - -SVal ValueManager::convertToArrayIndex(SVal V) { - if (V.isUnknownOrUndef()) - return V; - - // Common case: we have an appropriately sized integer. - if (nonloc::ConcreteInt* CI = dyn_cast<nonloc::ConcreteInt>(&V)) { - const llvm::APSInt& I = CI->getValue(); - if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) - return V; - } - - return SVator->EvalCastNL(cast<NonLoc>(V), ArrayIndexTy); -} - -DefinedOrUnknownSVal ValueManager::getRegionValueSymbolVal(const MemRegion* R, - QualType T) { - - if (T.isNull()) { - const TypedRegion* TR = cast<TypedRegion>(R); - T = TR->getValueType(SymMgr.getContext()); - } - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getRegionValueSymbol(R, T); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - -DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, - const Expr *E, - unsigned Count) { - QualType T = E->getType(); - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getConjuredSymbol(E, Count, SymbolTag); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - -DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, - const Expr *E, - QualType T, - unsigned Count) { - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getConjuredSymbol(E, T, Count, SymbolTag); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - - -DefinedOrUnknownSVal -ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, - const TypedRegion *R) { - QualType T = R->getValueType(R->getContext()); - - if (!SymbolManager::canSymbolicate(T)) - return UnknownVal(); - - SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, R); - - if (Loc::IsLocType(T)) - return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - - return nonloc::SymbolVal(sym); -} - -DefinedSVal ValueManager::getFunctionPointer(const FunctionDecl* FD) { - return loc::MemRegionVal(MemMgr.getFunctionTextRegion(FD)); -} - -DefinedSVal ValueManager::getBlockPointer(const BlockDecl *D, - CanQualType locTy, - const LocationContext *LC) { - const BlockTextRegion *BC = - MemMgr.getBlockTextRegion(D, locTy, LC->getAnalysisContext()); - const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); - return loc::MemRegionVal(BD); -} - |