diff options
Diffstat (limited to 'lib/Analysis')
35 files changed, 18670 insertions, 0 deletions
diff --git a/lib/Analysis/BasicConstraintManager.cpp b/lib/Analysis/BasicConstraintManager.cpp new file mode 100644 index 0000000..b272214 --- /dev/null +++ b/lib/Analysis/BasicConstraintManager.cpp @@ -0,0 +1,342 @@ +//== 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/Compiler.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + + +namespace { class VISIBILITY_HIDDEN ConstNotEq {}; } +namespace { class VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN BasicConstraintManager + : public SimpleConstraintManager { + GRState::IntSetTy::Factory ISetFactory; +public: + BasicConstraintManager(GRStateManager& statemgr) + : SimpleConstraintManager(statemgr), ISetFactory(statemgr.getAllocator()) {} + + const GRState* AssumeSymNE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymLT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymGT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymGE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymLE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AddEQ(const GRState* St, SymbolRef sym, const llvm::APSInt& V); + + const GRState* AddNE(const GRState* St, SymbolRef sym, const llvm::APSInt& V); + + const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const; + bool isNotEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) + const; + bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) + const; + + const GRState* RemoveDeadBindings(const GRState* St, SymbolReaper& SymReaper); + + void print(const GRState* St, std::ostream& Out, + const char* nl, const char *sep); +}; + +} // end anonymous namespace + +ConstraintManager* clang::CreateBasicConstraintManager(GRStateManager& StateMgr) +{ + return new BasicConstraintManager(StateMgr); +} + +const GRState* +BasicConstraintManager::AssumeSymNE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible) { + // First, determine if sym == X, where X != V. + if (const llvm::APSInt* X = getSymVal(St, sym)) { + isFeasible = (*X != V); + return St; + } + + // Second, determine if sym != V. + if (isNotEqual(St, sym, V)) { + isFeasible = true; + return St; + } + + // If we reach here, sym is not a constant and we don't know if it is != V. + // Make that assumption. + isFeasible = true; + return AddNE(St, sym, V); +} + +const GRState* +BasicConstraintManager::AssumeSymEQ(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible) { + // First, determine if sym == X, where X != V. + if (const llvm::APSInt* X = getSymVal(St, sym)) { + isFeasible = *X == V; + return St; + } + + // Second, determine if sym != V. + if (isNotEqual(St, sym, V)) { + isFeasible = false; + return St; + } + + // If we reach here, sym is not a constant and we don't know if it is == V. + // Make that assumption. + + isFeasible = true; + return AddEQ(St, sym, V); +} + +// These logic will be handled in another ConstraintManager. +const GRState* +BasicConstraintManager::AssumeSymLT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible) { + + // 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. + isFeasible = false; + return St; + } + + // FIXME: For now have assuming x < y be the same as assuming sym != V; + return AssumeSymNE(St, sym, V, isFeasible); +} + +const GRState* +BasicConstraintManager::AssumeSymGT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible) { + + // 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. + isFeasible = false; + return St; + } + + // FIXME: For now have assuming x > y be the same as assuming sym != V; + return AssumeSymNE(St, sym, V, isFeasible); +} + +const GRState* +BasicConstraintManager::AssumeSymGE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible) { + + // Reject a path if the value of sym is a constant X and !(X >= V). + if (const llvm::APSInt* X = getSymVal(St, sym)) { + isFeasible = *X >= V; + return St; + } + + // 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. + isFeasible = !isNotEqual(St, 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. + if (isFeasible) + return AddEQ(St, sym, V); + } + else + isFeasible = true; + + return St; +} + +const GRState* +BasicConstraintManager::AssumeSymLE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible) { + + // Reject a path if the value of sym is a constant X and !(X <= V). + if (const llvm::APSInt* X = getSymVal(St, sym)) { + isFeasible = *X <= V; + return St; + } + + // 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. + isFeasible = !isNotEqual(St, 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. + if (isFeasible) + return AddEQ(St, sym, V); + } + else + isFeasible = true; + + return St; +} + +const GRState* BasicConstraintManager::AddEQ(const GRState* St, SymbolRef sym, + const llvm::APSInt& V) { + // Create a new state with the old binding replaced. + GRStateRef state(St, StateMgr); + return state.set<ConstEq>(sym, &V); +} + +const GRState* BasicConstraintManager::AddNE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V) { + + GRStateRef state(St, StateMgr); + + // 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* St, + SymbolRef sym) const { + const ConstEqTy::data_type* T = St->get<ConstEq>(sym); + return T ? *T : NULL; +} + +bool BasicConstraintManager::isNotEqual(const GRState* St, SymbolRef sym, + const llvm::APSInt& V) const { + + // Retrieve the NE-set associated with the given symbol. + const ConstNotEqTy::data_type* T = St->get<ConstNotEq>(sym); + + // See if V is present in the NE-set. + return T ? T->contains(&V) : false; +} + +bool BasicConstraintManager::isEqual(const GRState* St, SymbolRef sym, + const llvm::APSInt& V) const { + // Retrieve the EQ-set associated with the given symbol. + const ConstEqTy::data_type* T = St->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* St, + SymbolReaper& SymReaper) { + + GRStateRef state(St, StateMgr); + 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* St, std::ostream& Out, + const char* nl, const char *sep) { + // Print equality constraints. + + ConstEqTy CE = St->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(); + llvm::raw_os_ostream OS(Out); + OS << " : " << *I.getData(); + } + } + + // Print != constraints. + + ConstNotEqTy CNE = St->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 new file mode 100644 index 0000000..98e9551 --- /dev/null +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -0,0 +1,492 @@ +//== 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/LocalCheckers.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ASTContext.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) { + Expr* Receiver = ME->getReceiver(); + + if (!Receiver) + return NULL; + + QualType X = Receiver->getType(); + + if (X->isPointerType()) { + Type* TP = X.getTypePtr(); + const PointerType* T = TP->getAsPointerType(); + return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr()); + } + + // FIXME: Support ObjCQualifiedIdType? + return NULL; +} + +static const char* GetReceiverNameType(ObjCMessageExpr* ME) { + ObjCInterfaceType* ReceiverType = GetReceiverType(ME); + return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName() + : NULL; +} + +namespace { + +class VISIBILITY_HIDDEN APIMisuse : public BugType { +public: + APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} +}; + +class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck { + APIMisuse *BT; + BugReporter& BR; + ASTContext &Ctx; + GRStateManager* VMgr; + + SVal GetSVal(const GRState* St, Expr* E) { return VMgr->GetSVal(St, E); } + + bool isNSString(ObjCInterfaceType* T, const char* suffix); + bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME); + + void Warn(NodeTy* N, Expr* E, const std::string& s); + void WarnNilArg(NodeTy* N, Expr* E); + + bool CheckNilArg(NodeTy* N, unsigned Arg); + +public: + BasicObjCFoundationChecks(ASTContext& ctx, GRStateManager* vmgr, + BugReporter& br) + : BT(0), BR(br), Ctx(ctx), VMgr(vmgr) {} + + bool Audit(ExplodedNode<GRState>* N, GRStateManager&); + +private: + void WarnNilArg(NodeTy* N, 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().c_str(), N); + R->addRange(ME->getArg(Arg)->getSourceRange()); + BR.EmitReport(R); + } +}; + +} // end anonymous namespace + + +GRSimpleAPICheck* +clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, + GRStateManager* VMgr, BugReporter& BR) { + + return new BasicObjCFoundationChecks(Ctx, VMgr, BR); +} + + + +bool BasicObjCFoundationChecks::Audit(ExplodedNode<GRState>* N, + GRStateManager&) { + + ObjCMessageExpr* ME = + cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); + + ObjCInterfaceType* ReceiverType = GetReceiverType(ME); + + if (!ReceiverType) + return false; + + const char* name = ReceiverType->getDecl()->getIdentifier()->getName(); + + if (!name) + return false; + + if (name[0] != 'N' || name[1] != 'S') + return false; + + name += 2; + + // FIXME: Make all of this faster. + + if (isNSString(ReceiverType, name)) + return AuditNSString(N, ME); + + return false; +} + +static inline bool isNil(SVal X) { + return isa<loc::ConcreteInt>(X); +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) { + ObjCMessageExpr* ME = + cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); + + Expr * E = ME->getArg(Arg); + + if (isNil(GetSVal(N->getState(), E))) { + WarnNilArg(N, ME, Arg); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// NSString checking. +//===----------------------------------------------------------------------===// + +bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T, + const char* suffix) { + + return !strcmp("String", suffix) || !strcmp("MutableString", suffix); +} + +bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N, + 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 VISIBILITY_HIDDEN 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; + GRStateManager* VMgr; + BugReporter& BR; + + SVal GetSVal(const GRState* St, Expr* E) { return VMgr->GetSVal(St, E); } + +public: + AuditCFNumberCreate(ASTContext& ctx, GRStateManager* vmgr, BugReporter& br) + : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), VMgr(vmgr), BR(br){} + + ~AuditCFNumberCreate() {} + + bool Audit(ExplodedNode<GRState>* N, GRStateManager&); + +private: + void AddError(const TypedRegion* R, Expr* Ex, ExplodedNode<GRState> *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 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<GRState>* N,GRStateManager&){ + CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); + Expr* Callee = CE->getCallee(); + SVal CallV = GetSVal(N->getState(), 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 = GetSVal(N->getState(), 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 = GetSVal(N->getState(), 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->getRegion()); + if (!R) return false; + + while (const TypedViewRegion* ATR = dyn_cast<TypedViewRegion>(R)) { + R = dyn_cast<TypedRegion>(ATR->getSuperRegion()); + 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, Expr* Ex, + ExplodedNode<GRState> *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().c_str(), N); + report->addRange(Ex->getSourceRange()); + BR.EmitReport(report); +} + +GRSimpleAPICheck* +clang::CreateAuditCFNumberCreate(ASTContext& Ctx, + GRStateManager* VMgr, BugReporter& BR) { + return new AuditCFNumberCreate(Ctx, VMgr, BR); +} + +//===----------------------------------------------------------------------===// +// Check registration. + +void clang::RegisterAppleChecks(GRExprEngine& Eng) { + ASTContext& Ctx = Eng.getContext(); + GRStateManager* VMgr = &Eng.getStateManager(); + BugReporter &BR = Eng.getBugReporter(); + + Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, VMgr, BR), + Stmt::ObjCMessageExprClass); + + Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, VMgr, BR), + Stmt::CallExprClass); + + RegisterNSErrorChecks(BR, Eng); +} diff --git a/lib/Analysis/BasicObjCFoundationChecks.h b/lib/Analysis/BasicObjCFoundationChecks.h new file mode 100644 index 0000000..6c594ea --- /dev/null +++ b/lib/Analysis/BasicObjCFoundationChecks.h @@ -0,0 +1,47 @@ +//== 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/ExplodedGraph.h" +#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h" +#include "clang/Analysis/PathSensitive/GRState.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ASTContext.h" +#include "llvm/Support/Compiler.h" + +#ifndef LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS +#define LLVM_CLANG_ANALYSIS_BASICOBJCFOUNDATIONCHECKS + +namespace clang { + +class GRSimpleAPICheck; +class ASTContext; +class GRStateManager; +class BugReporter; +class GRExprEngine; + +GRSimpleAPICheck* CreateBasicObjCFoundationChecks(ASTContext& Ctx, + GRStateManager* VMgr, + BugReporter& BR); + +GRSimpleAPICheck* CreateAuditCFNumberCreate(ASTContext& Ctx, + GRStateManager* VMgr, + BugReporter& BR); + +void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng); + +} // end clang namespace + +#endif diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp new file mode 100644 index 0000000..2dd46c3 --- /dev/null +++ b/lib/Analysis/BasicStore.cpp @@ -0,0 +1,637 @@ +//== 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/GRState.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Streams.h" + +using namespace clang; + +typedef llvm::ImmutableMap<const MemRegion*,SVal> BindingsTy; + +namespace { + +class VISIBILITY_HIDDEN BasicStoreSubRegionMap : public SubRegionMap { +public: + BasicStoreSubRegionMap() {} + + bool iterSubRegions(const MemRegion* R, Visitor& V) const { + return true; // Do nothing. No subregions. + } +}; + +class VISIBILITY_HIDDEN BasicStoreManager : public StoreManager { + BindingsTy::Factory VBFactory; + const MemRegion* SelfRegion; + +public: + BasicStoreManager(GRStateManager& mgr) + : StoreManager(mgr), + VBFactory(mgr.getAllocator()), + SelfRegion(0) {} + + ~BasicStoreManager() {} + + SubRegionMap* getSubRegionMap(const GRState *state) { + return new BasicStoreSubRegionMap(); + } + + SVal Retrieve(const GRState *state, Loc loc, QualType T = QualType()); + + const GRState* Bind(const GRState* St, Loc L, SVal V) { + Store store = BindInternal(St->getStore(), L, V); + return StateMgr.MakeStateWithStore(St, store); + } + + Store scanForIvars(Stmt *B, const Decl* SelfDecl, Store St); + + Store BindInternal(Store St, Loc loc, SVal V); + Store Remove(Store St, Loc loc); + Store getInitialStore(); + + // FIXME: Investigate what is using this. This method should be removed. + virtual Loc getLoc(const VarDecl* VD) { + return Loc::MakeVal(MRMgr.getVarRegion(VD)); + } + + const GRState* BindCompoundLiteral(const GRState* St, + const CompoundLiteralExpr* CL, + SVal V) { + return St; + } + + SVal getLValueVar(const GRState* St, const VarDecl* VD); + SVal getLValueString(const GRState* St, const StringLiteral* S); + SVal getLValueCompoundLiteral(const GRState* St, + const CompoundLiteralExpr* CL); + SVal getLValueIvar(const GRState* St, const ObjCIvarDecl* D, SVal Base); + SVal getLValueField(const GRState* St, SVal Base, const FieldDecl* D); + SVal getLValueElement(const GRState* St, QualType elementType, + SVal Base, SVal Offset); + + /// ArrayToPointer - Used by GRExprEngine::VistCast to handle implicit + /// conversions between arrays and pointers. + SVal ArrayToPointer(Loc Array) { return Array; } + + /// getSelfRegion - Returns the region for the 'self' (Objective-C) or + /// 'this' object (C++). When used when analyzing a normal function this + /// method returns NULL. + const MemRegion* getSelfRegion(Store) { return SelfRegion; } + + /// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values. + /// It returns a new Store with these values removed, and populates LSymbols + /// and DSymbols with the known set of live and dead symbols respectively. + Store + RemoveDeadBindings(const GRState* state, Stmt* Loc, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); + + void iterBindings(Store store, BindingsHandler& f); + + const GRState* BindDecl(const GRState* St, const VarDecl* VD, SVal InitVal) { + Store store = BindDeclInternal(St->getStore(), VD, &InitVal); + return StateMgr.MakeStateWithStore(St, store); + } + + const GRState* BindDeclWithNoInit(const GRState* St, const VarDecl* VD) { + Store store = BindDeclInternal(St->getStore(), VD, 0); + return StateMgr.MakeStateWithStore(St, store); + } + + Store BindDeclInternal(Store store, const VarDecl* VD, SVal* InitVal); + + static inline BindingsTy GetBindings(Store store) { + return BindingsTy(static_cast<const BindingsTy::TreeTy*>(store)); + } + + void print(Store store, std::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 GRState* St, const VarDecl* VD) { + return Loc::MakeVal(MRMgr.getVarRegion(VD)); +} + +SVal BasicStoreManager::getLValueString(const GRState* St, + const StringLiteral* S) { + return Loc::MakeVal(MRMgr.getStringRegion(S)); +} + +SVal BasicStoreManager::getLValueCompoundLiteral(const GRState* St, + const CompoundLiteralExpr* CL){ + return Loc::MakeVal(MRMgr.getCompoundLiteralRegion(CL)); +} + +SVal BasicStoreManager::getLValueIvar(const GRState* St, 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(); + + if (BaseR == SelfRegion) + return loc::MemRegionVal(MRMgr.getObjCIvarRegion(D, BaseR)); + } + + return UnknownVal(); +} + +SVal BasicStoreManager::getLValueField(const GRState* St, SVal Base, + const FieldDecl* D) { + + 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 Loc::MakeVal(MRMgr.getFieldRegion(D, BaseR)); +} + +SVal BasicStoreManager::getLValueElement(const GRState* St, + QualType elementType, + SVal Base, SVal Offset) { + + if (Base.isUnknownOrUndef()) + return Base; + + Loc BaseL = cast<Loc>(Base); + const TypedRegion* 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 (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + BaseR = TR; + break; + } + + if (const SymbolicRegion* SR = dyn_cast<SymbolicRegion>(R)) { + SymbolRef Sym = SR->getSymbol(); + BaseR = MRMgr.getTypedViewRegion(Sym->getType(getContext()), SR); + } + + 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 Loc::MakeVal(MRMgr.getElementRegion(elementType, UnknownVal(), + BaseR)); + else + return UnknownVal(); +} + +static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { + bool foundPointer = false; + while (1) { + const PointerType *PT = T->getAsPointerType(); + 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(); + } +} + +SVal BasicStoreManager::Retrieve(const GRState* state, Loc loc, QualType T) { + + if (isa<UnknownVal>(loc)) + return UnknownVal(); + + assert (!isa<UndefinedVal>(loc)); + + switch (loc.getSubKind()) { + + case loc::MemRegionKind: { + const MemRegion* R = cast<loc::MemRegionVal>(loc).getRegion(); + + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + // Just support void**, void***, intptr_t*, intptr_t**, etc., for now. + // This is needed to handle OSCompareAndSwapPtr() and friends. + ASTContext &Ctx = StateMgr.getContext(); + QualType T = ER->getLocationType(Ctx); + + if (!isHigherOrderRawPtr(T, Ctx)) + return UnknownVal(); + + // FIXME: Should check for element 0. + // Otherwise, strip the element region. + R = ER->getSuperRegion(); + } + + if (!(isa<VarRegion>(R) || isa<ObjCIvarRegion>(R))) + return UnknownVal(); + + BindingsTy B = GetBindings(state->getStore()); + BindingsTy::data_type* T = B.lookup(R); + return T ? *T : UnknownVal(); + } + + 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 UndefinedVal(); + + default: + assert (false && "Invalid Loc."); + break; + } + + return UnknownVal(); +} + +Store BasicStoreManager::BindInternal(Store store, Loc loc, SVal V) { + switch (loc.getSubKind()) { + case loc::MemRegionKind: { + 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; + + // We only track bindings to self.ivar. + if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) + if (IVR->getSuperRegion() != SelfRegion) + 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 (const TypedRegion *TyR = dyn_cast<TypedRegion>(R)) { + if (TyR->isBoundable(C) && + 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(); + } + default: + assert ("SetSVal for given Loc type not yet implemented."); + return store; + } +} + +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; + } +} + +Store +BasicStoreManager::RemoveDeadBindings(const 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->getDecl())) + 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)); + + // 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, Loc::MakeVal(R)); + SVal X = I.getData(); + + for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) + SymReaper.maybeDead(*SI); + } + } + + return store; +} + +Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, 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, Loc::MakeVal(IVR), X); + } + } + } + else + St = scanForIvars(*CI, SelfDecl, St); + } + + return St; +} + +Store BasicStoreManager::getInitialStore() { + // 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 = StateMgr.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 = StateMgr.getCodeDecl(); + if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) { + if (MD->getSelfDecl() == PD) { + // Create a region for "self". + assert (SelfRegion == 0); + SelfRegion = MRMgr.getObjCObjectRegion(MD->getClassInterface(), + MRMgr.getHeapRegion()); + + St = BindInternal(St, Loc::MakeVal(MRMgr.getVarRegion(PD)), + Loc::MakeVal(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(getContext()), PD, St); + } + } + } + else if (VarDecl* VD = dyn_cast<VarDecl>(ND)) { + // Punt on static variables for now. + if (VD->getStorageClass() == VarDecl::Static) + continue; + + // 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 = StateMgr.getRegion(VD); + SVal X = (VD->hasGlobalStorage() || isa<ParmVarDecl>(VD) || + isa<ImplicitParamDecl>(VD)) + ? ValMgr.getRegionValueSymbolVal(R) + : UndefinedVal(); + + St = BindInternal(St, Loc::MakeVal(R), X); + } + } + return St; +} + +Store BasicStoreManager::BindDeclInternal(Store store, const VarDecl* VD, + SVal* InitVal) { + + BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + + // 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, getLoc(VD), + loc::ConcreteInt(BasicVals.getValue(0, T))); + else if (T->isIntegerType()) + store = BindInternal(store, getLoc(VD), + nonloc::ConcreteInt(BasicVals.getValue(0, T))); + else { + // assert(0 && "ignore other types of variables"); + } + } else { + store = BindInternal(store, getLoc(VD), *InitVal); + } + } + } else { + // Process local scalar variables. + QualType T = VD->getType(); + if (Loc::IsLocType(T) || T->isIntegerType()) { + SVal V = InitVal ? *InitVal : UndefinedVal(); + store = BindInternal(store, getLoc(VD), V); + } + } + + return store; +} + +void BasicStoreManager::print(Store store, std::ostream& O, + const char* nl, const char *sep) { + + llvm::raw_os_ostream Out(O); + 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().print(Out); + } +} + + +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() {} diff --git a/lib/Analysis/BasicValueFactory.cpp b/lib/Analysis/BasicValueFactory.cpp new file mode 100644 index 0000000..72ad0a5 --- /dev/null +++ b/lib/Analysis/BasicValueFactory.cpp @@ -0,0 +1,264 @@ +//=== 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()); +} + +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 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 new file mode 100644 index 0000000..32998e1 --- /dev/null +++ b/lib/Analysis/BugReporter.cpp @@ -0,0 +1,1697 @@ +// 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 for analyses based on GRSimpleVals. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/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 Stmt* GetStmt(ProgramPoint P) { + if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) + return PS->getStmt(); + else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) + return BE->getSrc()->getTerminator(); + + return 0; +} + +static inline const ExplodedNode<GRState>* +GetPredecessorNode(const ExplodedNode<GRState>* N) { + return N->pred_empty() ? NULL : *(N->pred_begin()); +} + +static inline const ExplodedNode<GRState>* +GetSuccessorNode(const ExplodedNode<GRState>* N) { + return N->succ_empty() ? NULL : *(N->succ_begin()); +} + +static Stmt* GetPreviousStmt(const ExplodedNode<GRState>* N) { + for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) + if (Stmt *S = GetStmt(N->getLocation())) + return S; + + return 0; +} + +static Stmt* GetNextStmt(const ExplodedNode<GRState>* N) { + for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) + if (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; + } + return S; + } + + return 0; +} + +static inline Stmt* GetCurrentOrPreviousStmt(const ExplodedNode<GRState>* N) { + if (Stmt *S = GetStmt(N->getLocation())) + return S; + + return GetPreviousStmt(N); +} + +static inline Stmt* GetCurrentOrNextStmt(const ExplodedNode<GRState>* N) { + if (Stmt *S = GetStmt(N->getLocation())) + return S; + + return GetNextStmt(N); +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticBuilder and its associated routines and helper objects. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap<const ExplodedNode<GRState>*, +const ExplodedNode<GRState>*> NodeBackMap; + +namespace { +class VISIBILITY_HIDDEN NodeMapClosure : public BugReport::NodeResolver { + NodeBackMap& M; +public: + NodeMapClosure(NodeBackMap *m) : M(*m) {} + ~NodeMapClosure() {} + + const ExplodedNode<GRState>* getOriginalNode(const ExplodedNode<GRState>* N) { + NodeBackMap::iterator I = M.find(N); + return I == M.end() ? 0 : I->second; + } +}; + +class VISIBILITY_HIDDEN 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<GRState>* N); + + PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, + const ExplodedNode<GRState>* N); + + ParentMap& getParentMap() { + if (PM.get() == 0) + PM.reset(new ParentMap(getCodeDecl().getBody(getASTContext()))); + return *PM.get(); + } + + 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<GRState>* N) { + if (Stmt *S = GetNextStmt(N)) + return PathDiagnosticLocation(S, getSourceManager()); + + return FullSourceLoc(getCodeDecl().getBodyRBrace(getASTContext()), + getSourceManager()); +} + +PathDiagnosticLocation +PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os, + const ExplodedNode<GRState>* 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 " + << (isa<ObjCMethodDecl>(getCodeDecl()) ? "method" : "function") << '.'; + + 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<GRState>* N, + GRStateManager& VMgr, SVal X) { + + for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { + + ProgramPoint P = N->getLocation(); + + if (!isa<PostStmt>(P)) + continue; + + DeclRefExpr* DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt()); + + if (!DR) + continue; + + SVal Y = VMgr.GetSVal(N->getState(), DR); + + if (X != Y) + continue; + + VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()); + + if (!VD) + continue; + + return VD; + } + + return 0; +} + +namespace { +class VISIBILITY_HIDDEN NotableSymbolHandler +: public StoreManager::BindingsHandler { + + SymbolRef Sym; + const GRState* PrevSt; + const Stmt* S; + GRStateManager& VMgr; + const ExplodedNode<GRState>* Pred; + PathDiagnostic& PD; + BugReporter& BR; + +public: + + NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, + GRStateManager& vmgr, const ExplodedNode<GRState>* 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 = VMgr.GetSVal(PrevSt, 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<GRState>* N, + const Stmt* S, + SymbolRef Sym, BugReporter& BR, + PathDiagnostic& PD) { + + const ExplodedNode<GRState>* 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 VISIBILITY_HIDDEN ScanNotableSymbols +: public StoreManager::BindingsHandler { + + llvm::SmallSet<SymbolRef, 10> AlreadyProcessed; + const ExplodedNode<GRState>* N; + Stmt* S; + GRBugReporter& BR; + PathDiagnostic& PD; + +public: + ScanNotableSymbols(const ExplodedNode<GRState>* n, 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<GRState> *N) { + + SourceManager& SMgr = PDB.getSourceManager(); + const ExplodedNode<GRState>* 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: { + 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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(PDB.getASTContext())) + 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<GRState> *N) { + + + EdgeBuilder EB(PD, PDB); + + const ExplodedNode<GRState>* 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() {} +void BugType::FlushReports(BugReporter &BR) {} + +//===----------------------------------------------------------------------===// +// Methods for BugReport and subclasses. +//===----------------------------------------------------------------------===// +BugReport::~BugReport() {} +RangedBugReport::~RangedBugReport() {} + +Stmt* BugReport::getStmt(BugReporter& BR) const { + ProgramPoint ProgP = EndNode->getLocation(); + Stmt *S = NULL; + + if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP)) { + if (BE->getBlock() == &BR.getCFG()->getExit()) S = GetPreviousStmt(EndNode); + } + if (!S) S = GetStmt(ProgP); + + return S; +} + +PathDiagnosticPiece* +BugReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode<GRState>* EndPathNode) { + + Stmt* S = getStmt(BRC.getBugReporter()); + + if (!S) + return NULL; + + const SourceRange *Beg, *End; + getRanges(BRC.getBugReporter(), 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(BugReporter& BR, const SourceRange*& beg, + const SourceRange*& end) { + + if (Expr* E = dyn_cast_or_null<Expr>(getStmt(BR))) { + R = E->getSourceRange(); + assert(R.isValid()); + beg = &R; + end = beg+1; + } + else + beg = end = 0; +} + +SourceLocation BugReport::getLocation() const { + if (EndNode) + if (Stmt* S = GetCurrentOrPreviousStmt(EndNode)) { + // For member expressions, return the location of the '.' or '->'. + if (MemberExpr* ME = dyn_cast<MemberExpr>(S)) + return ME->getMemberLoc(); + + return S->getLocStart(); + } + + return FullSourceLoc(); +} + +PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode<GRState>* N, + const ExplodedNode<GRState>* 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() { FlushReports(); } +BugReporterData::~BugReporterData() {} + +ExplodedGraph<GRState>& +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. This will also delete the equivalence + // classes. + delete BT; + } + + // Remove all references to the BugType objects. + BugTypes = F.GetEmptySet(); +} + +//===----------------------------------------------------------------------===// +// PathDiagnostics generation. +//===----------------------------------------------------------------------===// + +static std::pair<std::pair<ExplodedGraph<GRState>*, NodeBackMap*>, + std::pair<ExplodedNode<GRState>*, unsigned> > +MakeReportGraph(const ExplodedGraph<GRState>* G, + const ExplodedNode<GRState>** NStart, + const ExplodedNode<GRState>** 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<GRState>* GTrim; + InterExplodedGraphMap<GRState>* 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<GRState> > AutoReleaseGTrim(GTrim); + llvm::OwningPtr<InterExplodedGraphMap<GRState> > 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<GRState>*> WS; + typedef llvm::DenseMap<const ExplodedNode<GRState>*,unsigned> IndexMapTy; + IndexMapTy IndexMap; + + for (const ExplodedNode<GRState>** I = NStart; I != NEnd; ++I) + if (const ExplodedNode<GRState> *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<GRState> *GNew = + new ExplodedGraph<GRState>(GTrim->getCFG(), GTrim->getCodeDecl(), + 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<GRState>* Root = 0; + + while (!WS.empty()) { + const ExplodedNode<GRState>* Node = WS.front(); + WS.pop(); + + if (Visited.find(Node) != Visited.end()) + continue; + + Visited[Node] = cnt++; + + if (Node->pred_empty()) { + Root = Node; + break; + } + + for (ExplodedNode<GRState>::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<GRState> *Last = 0, *First = 0; + NodeBackMap *BM = new NodeBackMap(); + unsigned NodeIndex = 0; + + for ( const ExplodedNode<GRState> *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<GRState>* 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<GRState>*) IMitr->second; + + // Link up the new node with the previous node. + if (Last) + NewN->addPredecessor(Last); + + Last = NewN; + + // Are we at the final node? + IndexMapTy::iterator IMI = + IndexMap.find((const ExplodedNode<GRState>*)(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<GRState>::const_succ_iterator SI = N->succ_begin(); + ExplodedNode<GRState>::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<GRState>*> Nodes; + + for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { + const ExplodedNode<GRState>* 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<GRState>*, NodeBackMap*>, + std::pair<ExplodedNode<GRState>*, 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<GRState> > ReportGraph(GPair.first.first); + llvm::OwningPtr<NodeBackMap> BackMap(GPair.first.second); + const ExplodedNode<GRState> *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); +} + +void BugReporter::FlushReport(BugReportEquivClass& EQ) { + assert(!EQ.Reports.empty()); + BugReport &R = **EQ.begin(); + 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); + + // 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(*this, Beg, End); + Diagnostic& Diag = getDiagnostic(); + FullSourceLoc L(R.getLocation(), getSourceManager()); + unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, + R.getShortDescription().c_str()); + + 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(const char* name, const char* str, + SourceLocation Loc, + SourceRange* RBeg, unsigned NumRanges) { + EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); +} + +void BugReporter::EmitBasicReport(const char* name, const char* category, + const char* str, SourceLocation Loc, + 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/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp new file mode 100644 index 0000000..30ff67f --- /dev/null +++ b/lib/Analysis/CFRefCount.cpp @@ -0,0 +1,3635 @@ +// 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 "GRSimpleVals.h" +#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/AST/DeclObjC.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Compiler.h" +#include "llvm/ADT/STLExtras.h" +#include <ostream> +#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::CStrInCStrNoCase; +using llvm::StringsEqualNoCase; + +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->getName(); + + // 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) { + InPossiblePrefix = false; + AtBeginning = true; + // Discard whatever 'convention' we + // had already derived since it occurs + // in the prefix. + 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 && StringsEqualNoCase("new", s, len)) + C = CreateRule; + break; + case 4: + // Methods starting with 'alloc' or contain 'copy' follow the + // create rule + if (C == NoConvention && StringsEqualNoCase("copy", s, len)) + C = CreateRule; + else // Methods starting with 'init' follow the init rule. + if (AtBeginning && StringsEqualNoCase("init", s, len)) + C = InitRule; + break; + case 5: + if (AtBeginning && StringsEqualNoCase("alloc", s, len)) + 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, ASTContext &Context) { + ObjCInterfaceDecl *ID = + const_cast<ObjCInterfaceDecl*>(MD->getClassInterface()); + + return MD->isInstanceMethod() + ? ID->lookupInstanceMethod(Context, MD->getSelector()) + : ID->lookupClassMethod(Context, MD->getSelector()); +} + +namespace { +class VISIBILITY_HIDDEN GenericNodeBuilder { + GRStmtNodeBuilder<GRState> *SNB; + Stmt *S; + const void *tag; + GREndPathNodeBuilder<GRState> *ENB; +public: + GenericNodeBuilder(GRStmtNodeBuilder<GRState> &snb, Stmt *s, + const void *t) + : SNB(&snb), S(s), tag(t), ENB(0) {} + GenericNodeBuilder(GREndPathNodeBuilder<GRState> &enb) + : SNB(0), S(0), tag(0), ENB(&enb) {} + + ExplodedNode<GRState> *MakeNode(const GRState *state, + ExplodedNode<GRState> *Pred) { + if (SNB) + return SNB->generateNode(PostStmt(S, tag), state, Pred); + + assert(ENB); + return ENB->generateNode(state, Pred); + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Selector creation functions. +//===----------------------------------------------------------------------===// + +static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(0, &II); +} + +static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(1, &II); +} + +//===----------------------------------------------------------------------===// +// Type querying functions. +//===----------------------------------------------------------------------===// + +static bool hasPrefix(const char* s, const char* prefix) { + if (!prefix) + return true; + + char c = *s; + char cP = *prefix; + + while (c != '\0' && cP != '\0') { + if (c != cP) break; + c = *(++s); + cP = *(++prefix); + } + + return cP == '\0'; +} + +static bool hasSuffix(const char* s, const char* suffix) { + const char* loc = strstr(s, suffix); + return loc && strcmp(suffix, loc) == 0; +} + +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 (1) { + if (TypedefType* TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) { + const char* TDName = TD->getDecl()->getIdentifier()->getName(); + if (hasPrefix(TDName, prefix) && hasSuffix(TDName, "Ref")) + return true; + + RetTy = TD->getDecl()->getUnderlyingType(); + continue; + } + break; + } + + if (!Ctx || !name) + return false; + + // Is the type void*? + const PointerType* PT = RetTy->getAsPointerType(); + if (!(PT->getPointeeType().getUnqualifiedType() == Ctx->VoidTy)) + return false; + + // Does the name start with the prefix? + return hasPrefix(name, 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 VISIBILITY_HIDDEN 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); + } +}; + + +class VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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()); + } + + static bool isPod() { + return DenseMapInfo<ObjCInterfaceDecl*>::isPod() && + DenseMapInfo<Selector>::isPod(); + } +}; +} // end llvm namespace + +namespace { +class VISIBILITY_HIDDEN ObjCSummaryCache { + typedef llvm::DenseMap<ObjCSummaryKey, RetainSummary*> MapTy; + MapTy M; +public: + ObjCSummaryCache() {} + + typedef MapTy::iterator iterator; + + iterator 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); + } + + iterator 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; + + // 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 I; + } + + // Cache the summary with original key to make the next lookup faster + // and return the iterator. + M[K] = I->second; + return I; + } + + + iterator find(Expr* Receiver, Selector S) { + return find(getReceiverDecl(Receiver), S); + } + + iterator 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. + iterator I = M.find(ObjCSummaryKey(II, S)); + return I == M.end() ? M.find(ObjCSummaryKey(S)) : I; + } + + ObjCInterfaceDecl* getReceiverDecl(Expr* E) { + + const PointerType* PT = E->getType()->getAsPointerType(); + if (!PT) return 0; + + ObjCInterfaceType* OI = dyn_cast<ObjCInterfaceType>(PT->getPointeeType()); + if (!OI) return 0; + + return OI ? OI->getDecl() : 0; + } + + iterator end() { return M.end(); } + + RetainSummary*& operator[](ObjCMessageExpr* ME) { + + Selector S = ME->getSelector(); + + if (Expr* Receiver = ME->getReceiver()) { + 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 VISIBILITY_HIDDEN 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; + + 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)), + 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(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(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, Ctx)) + 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 (!Ctx.isObjCObjectPointerType(Ty)) + return false; + + // We assume that id<..>, id, and "Class" all represent tracked objects. + const PointerType *PT = Ty->getAsPointerType(); + if (PT == 0) + return true; + + const ObjCInterfaceType *OT = PT->getPointeeType()->getAsObjCInterfaceType(); + + // We assume that id<..>, id, and "Class" all represent tracked objects. + if (!OT) + return true; + + // Does the interface subclass NSObject? + // FIXME: We can memoize here if this gets too expensive. + ObjCInterfaceDecl* ID = OT->getDecl(); + + // 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 'getAsFunctionType' to strip away any typedefs on the + // function's type. + const FunctionType* FT = FD->getType()->getAsFunctionType(); + const char* FName = FD->getIdentifier()->getName(); + + // 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. + if (strcmp(FName, "IOServiceGetMatchingServices") == 0) { + // 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. + assert (ScratchArgs.isEmpty()); + ScratchArgs = AF.Add(ScratchArgs, 1, DecRef); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + 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 + + // Handle: id NSMakeCollectable(CFTypeRef) + if (strcmp(FName, "NSMakeCollectable") == 0) { + S = (RetTy == Ctx.getObjCIdType()) + ? getUnarySummary(FT, cfmakecollectable) + : getPersistentStopSummary(); + + break; + } + + 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" or "AddValue" then + // we assume that arguments may "escape." + // + ArgEffect E = (CStrInCStrNoCase(FName, "InsertValue") || + CStrInCStrNoCase(FName, "AddValue") || + CStrInCStrNoCase(FName, "SetValue") || + CStrInCStrNoCase(FName, "AppendValue")) + ? 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(RetEffect::MakeOwnedWhenTrackedReceiver(), + DecRefMsg); + + return getDefaultSummary(); +} + +void +RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, + const FunctionDecl *FD) { + if (!FD) + return; + + // Determine if there is a special return effect for this method. + if (isTrackedObjCObjectType(FD->getResultType())) { + if (FD->getAttr<NSReturnsRetainedAttr>()) { + Summ.setRetEffect(ObjCAllocRetE); + } + else if (FD->getAttr<CFReturnsRetainedAttr>()) { + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + } + } +} + +void +RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, + const ObjCMethodDecl *MD) { + if (!MD) + return; + + // Determine if there is a special return effect for this method. + if (isTrackedObjCObjectType(MD->getResultType())) { + if (MD->getAttr<NSReturnsRetainedAttr>()) { + Summ.setRetEffect(ObjCAllocRetE); + } + else if (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.getUnqualifiedType() == 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 (CStrInCStrNoCase(&str[0], "delegate:")) 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(Selector S, + IdentifierInfo *ClsName, + const ObjCInterfaceDecl* ID, + const ObjCMethodDecl *MD, + QualType RetTy) { + + // Look up a summary in our summary cache. + ObjCMethodSummariesTy::iterator I = ObjCMethodSummaries.find(ID, ClsName, S); + + if (I != ObjCMethodSummaries.end()) + return I->second; + + assert(ScratchArgs.isEmpty()); + RetainSummary *Summ = 0; + + // "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."); + ObjCMethodSummariesTy::iterator I = + ObjCClassMethodSummaries.find(ID, ClsName, S); + + if (I != ObjCClassMethodSummaries.end()) + return I->second; + + RetainSummary *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. + addClsMethSummary(&Ctx.Idents.get("NSAssertionHandler"), + GetNullarySelector("currentHandler", Ctx), + getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); + + // Create the [NSAutoreleasePool addObject:] summary. + ScratchArgs = AF.Add(ScratchArgs, 0, Autorelease); + addClsMethSummary(&Ctx.Idents.get("NSAutoreleasePool"), + GetUnarySelector("addObject", Ctx), + getPersistentSummary(RetEffect::MakeNoRet(), + DoNothing, Autorelease)); + + // 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. + addNSObjectMethSummary(GetNullarySelector("init", Ctx), + getPersistentSummary(RetEffect::MakeOwnedWhenTrackedReceiver(), + DecRefMsg)); + + // The next methods are allocators. + RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); + + // 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'. + addInstMethSummary("CIContext", AllocSumm, + "createCGImage", "fromRect", NULL); + addInstMethSummary("CIContext", AllocSumm, + "createCGImage", "fromRect", "format", "colorSpace", NULL); +} + +//===----------------------------------------------------------------------===// +// Reference-counting logic (typestate + counts). +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN 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(std::ostream& Out) const; +}; + +void RefVal::print(std::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; +static int RefBIndex = 0; + +namespace clang { + template<> + struct GRStateTrait<RefBindings> : public GRStatePartialTrait<RefBindings> { + static inline void* GDMIndex() { return &RefBIndex; } + }; +} + +//===----------------------------------------------------------------------===// +// 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 VISIBILITY_HIDDEN AutoreleasePoolContents {}; } +namespace { class VISIBILITY_HIDDEN 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 GRStateRef SendAutorelease(GRStateRef 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 VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals { +public: + class BindingsPrinter : public GRState::Printer { + public: + virtual void Print(std::ostream& Out, const GRState* state, + const char* nl, const char* sep); + }; + +private: + typedef llvm::DenseMap<const GRExprEngine::NodeTy*, 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; + + GRStateRef Update(GRStateRef state, SymbolRef sym, RefVal V, ArgEffect E, + RefVal::Kind& hasErr); + + void ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst, + GRStmtNodeBuilder<GRState>& Builder, + Expr* NodeExpr, Expr* ErrorExpr, + ExplodedNode<GRState>* Pred, + const GRState* St, + RefVal::Kind hasErr, SymbolRef Sym); + + GRStateRef HandleSymbolDeath(GRStateRef state, SymbolRef sid, RefVal V, + llvm::SmallVectorImpl<SymbolRef> &Leaked); + + ExplodedNode<GRState>* ProcessLeaks(GRStateRef state, + llvm::SmallVectorImpl<SymbolRef> &Leaked, + GenericNodeBuilder &Builder, + GRExprEngine &Eng, + ExplodedNode<GRState> *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(BugReporter &BR); + + 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<GRState> *N) const { + SummaryLogTy::const_iterator I = SummaryLog.find(N); + return I == SummaryLog.end() ? 0 : I->second; + } + + // Calls. + + void EvalSummary(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + Expr* Ex, + Expr* Receiver, + const RetainSummary& Summ, + ExprIterator arg_beg, ExprIterator arg_end, + ExplodedNode<GRState>* Pred); + + virtual void EvalCall(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + CallExpr* CE, SVal L, + ExplodedNode<GRState>* Pred); + + + virtual void EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + ObjCMessageExpr* ME, + ExplodedNode<GRState>* Pred); + + bool EvalObjCMessageExprAux(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + ObjCMessageExpr* ME, + ExplodedNode<GRState>* Pred); + + // Stores. + virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val); + + // End-of-path. + + virtual void EvalEndPath(GRExprEngine& Engine, + GREndPathNodeBuilder<GRState>& Builder); + + virtual void EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + ExplodedNode<GRState>* Pred, + Stmt* S, const GRState* state, + SymbolReaper& SymReaper); + + std::pair<ExplodedNode<GRState>*, GRStateRef> + HandleAutoreleaseCounts(GRStateRef state, GenericNodeBuilder Bd, + ExplodedNode<GRState>* Pred, GRExprEngine &Eng, + SymbolRef Sym, RefVal V, bool &stop); + // Return statements. + + virtual void EvalReturn(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + ReturnStmt* S, + ExplodedNode<GRState>* Pred); + + // Assumptions. + + virtual const GRState* EvalAssume(GRStateManager& VMgr, + const GRState* St, SVal Cond, + bool Assumption, bool& isFeasible); +}; + +} // end anonymous namespace + +static void PrintPool(std::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(std::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 VISIBILITY_HIDDEN CFRefBug : public BugType { + protected: + CFRefCount& TF; + + CFRefBug(CFRefCount* tf, const char* 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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 is not owned at this point by the caller"; + } + }; + + class VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN Leak : public CFRefBug { + const bool isReturn; + protected: + Leak(CFRefCount* tf, const char* name, bool isRet) + : CFRefBug(tf, name), isReturn(isRet) {} + public: + + const char* getDescription() const { return ""; } + + bool isLeak() const { return true; } + }; + + class VISIBILITY_HIDDEN LeakAtReturn : public Leak { + public: + LeakAtReturn(CFRefCount* tf, const char* name) + : Leak(tf, name, true) {} + }; + + class VISIBILITY_HIDDEN LeakWithinFunction : public Leak { + public: + LeakWithinFunction(CFRefCount* tf, const char* name) + : Leak(tf, name, false) {} + }; + + //===---------===// + // Bug Reports. // + //===---------===// + + class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport { + protected: + SymbolRef Sym; + const CFRefCount &TF; + public: + CFRefReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode<GRState> *n, SymbolRef sym) + : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} + + CFRefReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode<GRState> *n, SymbolRef sym, const char* 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(BugReporter& BR, const SourceRange*& beg, + const SourceRange*& end) { + + if (!getBugType().isLeak()) + RangedBugReport::getRanges(BR, beg, end); + else + beg = end = 0; + } + + SymbolRef getSymbol() const { return Sym; } + + PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode<GRState>* N); + + std::pair<const char**,const char**> getExtraDescriptiveText(); + + PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState>* N, + const ExplodedNode<GRState>* PrevN, + BugReporterContext& BRC); + }; + + class VISIBILITY_HIDDEN CFRefLeakReport : public CFRefReport { + SourceLocation AllocSite; + const MemRegion* AllocBinding; + public: + CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode<GRState> *n, SymbolRef sym, + GRExprEngine& Eng); + + PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode<GRState>* N); + + SourceLocation getLocation() const { return AllocSite; } + }; +} // end anonymous namespace + +void CFRefCount::RegisterChecks(BugReporter& BR) { + 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"; + } + + leakAtReturn = new LeakAtReturn(this, name); + 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"; + } + + leakWithinFunction = new LeakWithinFunction(this, name); + BR.Register(leakWithinFunction); + + // Save the reference to the BugReporter. + this->BR = &BR; +} + +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<GRState>* N, + const ExplodedNode<GRState>* PrevN, + BugReporterContext& BRC) { + + if (!isa<PostStmt>(N->getLocation())) + return NULL; + + // Check if the type state has changed. + GRStateManager &StMgr = BRC.getStateManager(); + GRStateRef PrevSt(PrevN->getState(), StMgr); + GRStateRef CurrSt(N->getState(), StMgr); + + 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) { + Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); + + if (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. + Stmt* S = cast<PostStmt>(N->getLocation()).getStmt(); + + if (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::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 (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { + if (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. + 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! + + 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::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Expr* Exp = dyn_cast_or_null<Expr>(*I)) + if (CurrSt.GetSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { + P->addRange(Exp->getSourceRange()); + break; + } + + return P; +} + +namespace { + class VISIBILITY_HIDDEN 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<GRState>*,const MemRegion*> +GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode<GRState>* N, + SymbolRef Sym) { + + // Find both first node that referred to the tracked symbol and the + // memory location that value was store to. + const ExplodedNode<GRState>* 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<GRState>* 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<GRState>* 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<GRState>* AllocNode = 0; + const MemRegion* FirstBinding = 0; + + llvm::tie(AllocNode, FirstBinding) = + GetAllocationSite(BRC.getStateManager(), EndN, Sym); + + // Get the allocate site. + assert(AllocNode); + 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<GRState>* 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 = BRC.getCodeDecl(); + L = PathDiagnosticLocation(D.getBodyRBrace(BRC.getASTContext()), 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>(BRC.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>(BRC.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<GRState> *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<GRState>* 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(Expr* RetE, ASTContext& Ctx) { + + QualType RetTy = RetE->getType(); + + // FIXME: We aren't handling id<...>. + const PointerType* PT = RetTy->getAsPointerType(); + if (!PT) + return RetTy; + + // If RetEx is not a message expression just return its type. + // If RetEx is a message expression, return its types if it is something + /// more specific than id. + + ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(RetE); + + if (!ME || !Ctx.isObjCIdStructType(PT->getPointeeType())) + return RetTy; + + ObjCInterfaceDecl* D = ME->getClassInfo().first; + + // At this point we know the return type of the message expression is id. + // If we have an ObjCInterceDecl, we know this is a call to a class method + // whose type we can resolve. In such cases, promote the return type to + // Class*. + return !D ? RetTy : Ctx.getPointerType(Ctx.getObjCInterfaceType(D)); +} + + +void CFRefCount::EvalSummary(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + Expr* Ex, + Expr* Receiver, + const RetainSummary& Summ, + ExprIterator arg_beg, ExprIterator arg_end, + ExplodedNode<GRState>* Pred) { + + // Get the state. + GRStateManager& StateMgr = Eng.getStateManager(); + GRStateRef state(Builder.GetState(Pred), StateMgr); + ASTContext& Ctx = StateMgr.getContext(); + ValueManager &ValMgr = Eng.getValueManager(); + + // Evaluate the effect of the arguments. + RefVal::Kind hasErr = (RefVal::Kind) 0; + unsigned idx = 0; + Expr* ErrorExpr = NULL; + SymbolRef ErrorSym = 0; + + 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; + } + + 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. + + // FIXME: Either this logic should also be replicated in GRSimpleVals + // or should be pulled into a separate "constraint engine." + + // 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. + + const TypedRegion* R = dyn_cast<TypedRegion>(MR->getRegion()); + + if (R) { + // 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? + } + + // Is the invalidated variable something that we were tracking? + SymbolRef Sym = state.GetSValAsScalarOrLoc(R).getAsLocSymbol(); + + // Remove any existing reference-count binding. + if (Sym) state = state.remove<RefBindings>(Sym); + + if (R->isBoundable(Ctx)) { + // Set the value of the variable to be a conjured symbol. + unsigned Count = Builder.getCurrentBlockCount(); + QualType T = R->getValueType(Ctx); + + if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())){ + ValueManager &ValMgr = Eng.getValueManager(); + SVal V = ValMgr.getConjuredSymbolVal(*I, T, Count); + state = state.BindLoc(Loc::MakeVal(R), V); + } + else if (const RecordType *RT = T->getAsStructureType()) { + // Handle structs in a not so awesome way. Here we just + // eagerly bind new symbols to the fields. In reality we + // should have the store manager handle this. The idea is just + // to prototype some basic functionality here. All of this logic + // should one day soon just go away. + const RecordDecl *RD = RT->getDecl()->getDefinition(Ctx); + + // No record definition. There is nothing we can do. + if (!RD) + continue; + + MemRegionManager &MRMgr = state.getManager().getRegionManager(); + + // Iterate through the fields and construct new symbols. + for (RecordDecl::field_iterator FI=RD->field_begin(Ctx), + FE=RD->field_end(Ctx); FI!=FE; ++FI) { + + // For now just handle scalar fields. + FieldDecl *FD = *FI; + QualType FT = FD->getType(); + + if (Loc::IsLocType(FT) || + (FT->isIntegerType() && FT->isScalarType())) { + const FieldRegion* FR = MRMgr.getFieldRegion(FD, R); + + SVal V = ValMgr.getConjuredSymbolVal(*I, FT, Count); + state = state.BindLoc(Loc::MakeVal(FR), V); + } + } + } else if (const ArrayType *AT = Ctx.getAsArrayType(T)) { + // Set the default value of the array to conjured symbol. + StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); + SVal V = ValMgr.getConjuredSymbolVal(*I, AT->getElementType(), + Count); + state = GRStateRef(StoreMgr.setDefaultValue(state, R, V), + StateMgr); + } else { + // Just blast away other values. + state = state.BindLoc(*MR, UnknownVal()); + } + } + } + else + state = state.BindLoc(*MR, UnknownVal()); + } + else { + // Nuke all other arguments passed by reference. + state = state.Unbind(cast<Loc>(V)); + } + } + else if (isa<nonloc::LocAsInteger>(V)) + state = state.Unbind(cast<nonloc::LocAsInteger>(V).getLoc()); + } + + // 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: This is basically copy-and-paste from GRSimpleVals. We + // should compose behavior, not copy it. + + // FIXME: We eventually should handle structs and other compound types + // that are returned by value. + + QualType T = Ex->getType(); + + if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { + unsigned Count = Builder.getCurrentBlockCount(); + ValueManager &ValMgr = Eng.getValueManager(); + SVal X = ValMgr.getConjuredSymbolVal(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.makeRegionVal(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.makeRegionVal(Sym), false); + break; + } + } + + // Generate a sink node if we are at the end of a path. + GRExprEngine::NodeTy *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<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + CallExpr* CE, SVal L, + ExplodedNode<GRState>* Pred) { + const FunctionDecl* FD = L.getAsFunctionDecl(); + RetainSummary* Summ = !FD ? Summaries.getDefaultSummary() + : Summaries.getSummary(const_cast<FunctionDecl*>(FD)); + + assert(Summ); + EvalSummary(Dst, Eng, Builder, CE, 0, *Summ, + CE->arg_begin(), CE->arg_end(), Pred); +} + +void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + ObjCMessageExpr* ME, + ExplodedNode<GRState>* Pred) { + RetainSummary* Summ = 0; + + if (Expr* Receiver = ME->getReceiver()) { + // We need the type-information of the tracked receiver object + // Retrieve it from the state. + const ObjCInterfaceDecl* ID = 0; + + // FIXME: Wouldn't it be great if this code could be reduced? It's just + // a chain of lookups. + // FIXME: Is this really working as expected? There are cases where + // we just use the 'ID' from the message expression. + const GRState* St = Builder.GetState(Pred); + SVal V = Eng.getStateManager().GetSValAsScalarOrLoc(St, Receiver); + + SymbolRef Sym = V.getAsLocSymbol(); + if (Sym) { + if (const RefVal* T = St->get<RefBindings>(Sym)) { + QualType Ty = T->getType(); + + if (const PointerType* PT = Ty->getAsPointerType()) { + QualType PointeeTy = PT->getPointeeType(); + + if (ObjCInterfaceType* IT = dyn_cast<ObjCInterfaceType>(PointeeTy)) + ID = IT->getDecl(); + } + } + } + + // FIXME: this is a hack. This may or may not be the actual method + // that is called. + if (!ID) { + if (const PointerType *PT = Receiver->getType()->getAsPointerType()) + if (const ObjCInterfaceType *p = + PT->getPointeeType()->getAsObjCInterfaceType()) + ID = p->getDecl(); + } + + // FIXME: The receiver could be a reference to a class, meaning that + // we should use the class method. + Summ = Summaries.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>(&Eng.getGraph().getCodeDecl())) { + if (Expr* Receiver = ME->getReceiver()) { + SVal X = Eng.getStateManager().GetSValAsScalarOrLoc(St, Receiver); + if (loc::MemRegionVal* L = dyn_cast<loc::MemRegionVal>(&X)) + if (L->getRegion() == Eng.getStateManager().getSelfRegion(St)) { + // Update the summary to make the default argument effect + // 'StopTracking'. + Summ = Summaries.copySummary(Summ); + Summ->setDefaultArgEffect(StopTracking); + } + } + } + } + else + Summ = Summaries.getClassMethodSummary(ME); + + if (!Summ) + Summ = Summaries.getDefaultSummary(); + + EvalSummary(Dst, Eng, Builder, ME, ME->getReceiver(), *Summ, + ME->arg_begin(), ME->arg_end(), Pred); +} + +namespace { +class VISIBILITY_HIDDEN StopTrackingCallback : public SymbolVisitor { + GRStateRef state; +public: + StopTrackingCallback(GRStateRef st) : state(st) {} + GRStateRef getState() { return state; } + + bool VisitSymbol(SymbolRef sym) { + state = state.remove<RefBindings>(sym); + return true; + } + + const GRState* getState() const { return state.getState(); } +}; +} // 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. + GRStateRef state = B.getState(); + + if (!isa<loc::MemRegionVal>(location)) + escapes = true; + else { + const MemRegion* R = cast<loc::MemRegionVal>(location).getRegion(); + escapes = !B.getStateManager().hasStackStorage(R); + + 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<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + ReturnStmt* S, + ExplodedNode<GRState>* Pred) { + + Expr* RetE = S->getRetValue(); + if (!RetE) + return; + + GRStateRef state(Builder.GetState(Pred), Eng.getStateManager()); + 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) { + const Decl *CD = &Eng.getStateManager().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. + X = X ^ RefVal::ErrorGCLeakReturned; + + // Keep this false until this is properly tested. + hasError = true; + } + 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<GRState> *N = + Builder.generateNode(PostStmt(S, &ReturnOwnLeakTag), state, Pred); + if (N) { + CFRefReport *report = + new CFRefLeakReport(*static_cast<CFRefBug*>(leakAtReturn), *this, + N, Sym, Eng); + BR->EmitReport(report); + } + } + } + } + else if (X.isReturnedNotOwned()) { + const Decl *CD = &Eng.getStateManager().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<GRState> *N = + Builder.generateNode(PostStmt(S, &ReturnNotOwnedForOwnedTag), + state, Pred)) { + CFRefReport *report = + new CFRefReport(*static_cast<CFRefBug*>(returnNotOwnedForOwned), + *this, N, Sym); + BR->EmitReport(report); + } + } + } + } +} + +// Assumptions. + +const GRState* CFRefCount::EvalAssume(GRStateManager& VMgr, + const GRState* St, + SVal Cond, bool Assumption, + bool& isFeasible) { + + // 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 = St->get<RefBindings>(); + + if (B.isEmpty()) + return St; + + bool changed = false; + + GRStateRef state(St, VMgr); + 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 (VMgr.getSymVal(St, I.getKey())) { + changed = true; + B = RefBFactory.Remove(B, I.getKey()); + } + } + + if (changed) + state = state.set<RefBindings>(B); + + return state; +} + +GRStateRef CFRefCount::Update(GRStateRef 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<GRState>*, GRStateRef> +CFRefCount::HandleAutoreleaseCounts(GRStateRef state, GenericNodeBuilder Bd, + ExplodedNode<GRState>* 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<GRState> *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<GRState> *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().c_str()); + BR->EmitReport(report); + } + + return std::make_pair((ExplodedNode<GRState>*)0, state); +} + +GRStateRef +CFRefCount::HandleSymbolDeath(GRStateRef 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<GRState>* +CFRefCount::ProcessLeaks(GRStateRef state, + llvm::SmallVectorImpl<SymbolRef> &Leaked, + GenericNodeBuilder &Builder, + GRExprEngine& Eng, + ExplodedNode<GRState> *Pred) { + + if (Leaked.empty()) + return Pred; + + // Generate an intermediate node representing the leak point. + ExplodedNode<GRState> *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<GRState>& Builder) { + + GRStateRef state(Builder.getState(), Eng.getStateManager()); + GenericNodeBuilder Bd(Builder); + RefBindings B = state.get<RefBindings>(); + ExplodedNode<GRState> *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<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + ExplodedNode<GRState>* Pred, + Stmt* S, + const GRState* St, + SymbolReaper& SymReaper) { + + GRStateRef state(St, Eng.getStateManager()); + 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<GRState>& Dst, + GRStmtNodeBuilder<GRState>& Builder, + Expr* NodeExpr, Expr* ErrorExpr, + ExplodedNode<GRState>* Pred, + const GRState* St, + RefVal::Kind hasErr, SymbolRef Sym) { + Builder.BuildSinks = true; + GRExprEngine::NodeTy* 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); +} + +//===----------------------------------------------------------------------===// +// Transfer function creation for external clients. +//===----------------------------------------------------------------------===// + +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 new file mode 100644 index 0000000..9e8248f --- /dev/null +++ b/lib/Analysis/CMakeLists.txt @@ -0,0 +1,36 @@ +set(LLVM_NO_RTTI 1) + +add_clang_library(clangAnalysis + BasicConstraintManager.cpp + BasicObjCFoundationChecks.cpp + BasicStore.cpp + BasicValueFactory.cpp + BugReporter.cpp + CFRefCount.cpp + CheckDeadStores.cpp + CheckNSError.cpp + CheckObjCDealloc.cpp + CheckObjCInstMethSignature.cpp + CheckObjCUnusedIVars.cpp + Environment.cpp + ExplodedGraph.cpp + GRBlockCounter.cpp + GRCoreEngine.cpp + GRExprEngine.cpp + GRExprEngineInternalChecks.cpp + GRSimpleVals.cpp + GRState.cpp + GRTransferFuncs.cpp + LiveVariables.cpp + MemRegion.cpp + PathDiagnostic.cpp + RangeConstraintManager.cpp + RegionStore.cpp + SimpleConstraintManager.cpp + Store.cpp + SVals.cpp + SymbolManager.cpp + UninitializedValues.cpp + ) + +add_dependencies(clangAnalysis ClangDiagnosticAnalysis) diff --git a/lib/Analysis/CheckDeadStores.cpp b/lib/Analysis/CheckDeadStores.cpp new file mode 100644 index 0000000..69433d6 --- /dev/null +++ b/lib/Analysis/CheckDeadStores.cpp @@ -0,0 +1,259 @@ +//==- 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" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +namespace { + +class VISIBILITY_HIDDEN 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.c_str(), L, R); + } + + void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, + DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + if (VD->hasLocalStorage() && !Live(VD, AD) && !VD->getAttr<UnusedAttr>()) + 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())) { + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + + // Special case: check for assigning null to a pointer. + // This is a common form of defensive programming. + if (VD->getType()->isPointerType()) { + if (IntegerLiteral* L = dyn_cast<IntegerLiteral>(RHS)) + // FIXME: Probably should have an Expr::isNullPointerConstant. + if (L->getValue() == 0) + return; + } + // 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()) + if (Expr* E = V->getInit()) { + // 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 VISIBILITY_HIDDEN 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(LiveVariables& L, BugReporter& BR) { + FindEscaped FS(BR.getCFG()); + FS.getCFG().VisitBlockStmts(FS); + DeadStoreObs A(BR.getContext(), BR, BR.getParentMap(), FS.Escaped); + L.runOnAllBlocks(*BR.getCFG(), &A); +} diff --git a/lib/Analysis/CheckNSError.cpp b/lib/Analysis/CheckNSError.cpp new file mode 100644 index 0000000..ff9da0f --- /dev/null +++ b/lib/Analysis/CheckNSError.cpp @@ -0,0 +1,231 @@ +//=- CheckNSError.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 "BasicObjCFoundationChecks.h" +#include "llvm/Support/Compiler.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN NSErrorCheck : public BugType { + const bool isNSErrorWarning; + IdentifierInfo * const II; + GRExprEngine &Eng; + + void CheckSignature(ObjCMethodDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams); + + void CheckSignature(FunctionDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams); + + bool CheckNSErrorArgument(QualType ArgTy); + bool CheckCFErrorArgument(QualType ArgTy); + + void CheckParamDeref(VarDecl* V, GRStateRef state, BugReporter& BR); + + void EmitRetTyWarning(BugReporter& BR, Decl& CodeDecl); + +public: + NSErrorCheck(bool isNSError, GRExprEngine& eng) + : BugType(isNSError ? "NSError** null dereference" + : "CFErrorRef* null dereference", + "Coding Conventions (Apple)"), + 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) { + BR.Register(new NSErrorCheck(true, Eng)); + BR.Register(new NSErrorCheck(false, Eng)); +} + +void NSErrorCheck::FlushReports(BugReporter& BR) { + // Get the analysis engine and the exploded analysis graph. + GRExprEngine::GraphTy& G = Eng.getGraph(); + + // Get the declaration of the method/function that was analyzed. + Decl& CodeDecl = G.getCodeDecl(); + + // Get the ASTContext, which is useful for querying type information. + ASTContext &Ctx = BR.getContext(); + + QualType ResultTy; + llvm::SmallVector<VarDecl*, 5> ErrorParams; + + if (ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl)) + CheckSignature(*MD, ResultTy, ErrorParams); + else if (FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl)) + CheckSignature(*FD, ResultTy, ErrorParams); + else + return; + + if (ErrorParams.empty()) + return; + + if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); + + for (GRExprEngine::GraphTy::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, GRStateRef((*RI)->getState(),Eng.getStateManager()), + BR); + + } +} + +void NSErrorCheck::EmitRetTyWarning(BugReporter& BR, 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 occured."; + + BR.EmitBasicReport(isNSErrorWarning + ? "Bad return type when passing NSError**" + : "Bad return type when passing CFError*", + getCategory().c_str(), os.str().c_str(), + CodeDecl.getLocation()); +} + +void +NSErrorCheck::CheckSignature(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 +NSErrorCheck::CheckSignature(FunctionDecl& F, QualType& ResultTy, + llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { + + ResultTy = F.getResultType(); + + for (FunctionDecl::param_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 NSErrorCheck::CheckNSErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAsPointerType(); + if (!PPT) return false; + + const PointerType* PT = PPT->getPointeeType()->getAsPointerType(); + if (!PT) return false; + + const ObjCInterfaceType *IT = + PT->getPointeeType()->getAsObjCInterfaceType(); + + if (!IT) return false; + return IT->getDecl()->getIdentifier() == II; +} + +bool NSErrorCheck::CheckCFErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAsPointerType(); + if (!PPT) return false; + + const TypedefType* TT = PPT->getPointeeType()->getAsTypedefType(); + if (!TT) return false; + + return TT->getDecl()->getIdentifier() == II; +} + +void NSErrorCheck::CheckParamDeref(VarDecl* Param, GRStateRef rootState, + BugReporter& BR) { + + SVal ParamL = rootState.GetLValue(Param); + 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. + for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(), + E=Eng.implicit_null_derefs_end(); I!=E; ++I) { + + GRStateRef state = GRStateRef((*I)->getState(), Eng.getStateManager()); + const SVal* X = state.get<GRState::NullDerefTag>(); + + if (!X || X->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().c_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/CheckObjCDealloc.cpp b/lib/Analysis/CheckObjCDealloc.cpp new file mode 100644 index 0000000..f50d7a1 --- /dev/null +++ b/lib/Analysis/CheckObjCDealloc.cpp @@ -0,0 +1,257 @@ +//==- 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)) + 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)) { + // 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(ObjCImplementationDecl* D, + const LangOptions& LOpts, BugReporter& BR) { + + assert (LOpts.getGCMode() != LangOptions::GCOnly); + + ASTContext& Ctx = BR.getContext(); + 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 (!Ctx.isObjCObjectPointerType(T) || + 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(Ctx), + E = D->instmeth_end(Ctx); 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().c_str(), D->getLocStart()); + return; + } + + // dealloc found. Scan for missing [super dealloc]. + if (MD->getBody(Ctx) && !scan_dealloc(MD->getBody(Ctx), 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().c_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(Ctx), + E = D->propimpl_end(Ctx); 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 (!Ctx.isObjCObjectPointerType(T)) // 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(Ctx), 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().c_str(), (*I)->getLocation()); + } + } +} + diff --git a/lib/Analysis/CheckObjCInstMethSignature.cpp b/lib/Analysis/CheckObjCInstMethSignature.cpp new file mode 100644 index 0000000..9fec7c1 --- /dev/null +++ b/lib/Analysis/CheckObjCInstMethSignature.cpp @@ -0,0 +1,120 @@ +//=- 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->isPointerType() || Derived->isObjCQualifiedIdType()) && + (Ancestor->isPointerType() || Ancestor->isObjCQualifiedIdType())) + return true; + + return C.typesAreCompatible(Derived, Ancestor); +} + +static void CompareReturnTypes(ObjCMethodDecl* MethDerived, + ObjCMethodDecl* MethAncestor, + BugReporter& BR, ASTContext& Ctx, + 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().c_str(), MethDerived->getLocStart()); + } +} + +void clang::CheckObjCInstMethSignature(ObjCImplementationDecl* ID, + BugReporter& BR) { + + ObjCInterfaceDecl* D = ID->getClassInterface(); + 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(Ctx), + E=ID->instmeth_end(Ctx); 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(Ctx), + E=C->instmeth_end(Ctx); 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 new file mode 100644 index 0000000..7979f9c --- /dev/null +++ b/lib/Analysis/CheckObjCUnusedIVars.cpp @@ -0,0 +1,111 @@ +//==- 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 <sstream> + +using namespace clang; + +enum IVarState { Unused, Used }; +typedef llvm::DenseMap<ObjCIvarDecl*,IVarState> IvarUsageMap; + +static void Scan(IvarUsageMap& M, Stmt* S) { + if (!S) + return; + + if (ObjCIvarRefExpr* Ex = dyn_cast<ObjCIvarRefExpr>(S)) { + ObjCIvarDecl* D = Ex->getDecl(); + IvarUsageMap::iterator I = M.find(D); + if (I != M.end()) I->second = Used; + return; + } + + for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E;++I) + Scan(M, *I); +} + +static void Scan(IvarUsageMap& M, ObjCPropertyImplDecl* D) { + if (!D) + return; + + ObjCIvarDecl* ID = D->getPropertyIvarDecl(); + + if (!ID) + return; + + IvarUsageMap::iterator I = M.find(ID); + if (I != M.end()) I->second = Used; +} + +void clang::CheckObjCUnusedIvar(ObjCImplementationDecl* D, BugReporter& BR) { + + ObjCInterfaceDecl* ID = D->getClassInterface(); + IvarUsageMap M; + + + ASTContext &Ctx = BR.getContext(); + + // Iterate over the ivars. + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); + I!=E; ++I) { + + 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 methods for accesses. + for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(Ctx), + E = D->instmeth_end(Ctx); I!=E; ++I) + Scan(M, (*I)->getBody(Ctx)); + + // Scan for @synthesized property methods that act as setters/getters + // to an ivar. + for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(Ctx), + E = D->propimpl_end(Ctx); I!=E; ++I) + Scan(M, *I); + + // Find ivars that are unused. + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + + std::ostringstream os; + 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().c_str(), I->first->getLocation()); + } +} + diff --git a/lib/Analysis/Environment.cpp b/lib/Analysis/Environment.cpp new file mode 100644 index 0000000..2bc071a --- /dev/null +++ b/lib/Analysis/Environment.cpp @@ -0,0 +1,167 @@ +//== 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" +#include "llvm/Support/Streams.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +SVal Environment::GetSVal(Stmt* E, BasicValueFactory& BasicVals) const { + + for (;;) { + + switch (E->getStmtClass()) { + + case Stmt::AddrLabelExprClass: + return Loc::MakeVal(cast<AddrLabelExpr>(E)); + + // ParenExprs are no-ops. + + case Stmt::ParenExprClass: + E = cast<ParenExpr>(E)->getSubExpr(); + continue; + + case Stmt::CharacterLiteralClass: { + CharacterLiteral* C = cast<CharacterLiteral>(E); + return NonLoc::MakeVal(BasicVals, C->getValue(), C->getType()); + } + + case Stmt::IntegerLiteralClass: { + return NonLoc::MakeVal(BasicVals, 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: { + 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); +} + +SVal Environment::GetBlkExprSVal(Stmt* E, BasicValueFactory& BasicVals) const { + + while (1) { + switch (E->getStmtClass()) { + case Stmt::ParenExprClass: + E = cast<ParenExpr>(E)->getSubExpr(); + continue; + + case Stmt::CharacterLiteralClass: { + CharacterLiteral* C = cast<CharacterLiteral>(E); + return NonLoc::MakeVal(BasicVals, C->getValue(), C->getType()); + } + + case Stmt::IntegerLiteralClass: { + return NonLoc::MakeVal(BasicVals, cast<IntegerLiteral>(E)); + } + + default: + return LookupBlkExpr(E); + } + } +} + +Environment EnvironmentManager::BindExpr(const Environment& Env, Stmt* E,SVal V, + bool isBlkExpr, bool Invalidate) { + assert (E); + + if (V.isUnknown()) { + if (Invalidate) + return isBlkExpr ? RemoveBlkExpr(Env, E) : RemoveSubExpr(Env, E); + else + return Env; + } + + return isBlkExpr ? AddBlkExpr(Env, E, V) : AddSubExpr(Env, E, V); +} + +namespace { +class VISIBILITY_HIDDEN 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, Stmt* Loc, + SymbolReaper& SymReaper, + GRStateManager& StateMgr, + const GRState *state, + llvm::SmallVectorImpl<const MemRegion*>& DRoots) { + + // Drop bindings for subexpressions. + Env = RemoveSubExprBindings(Env); + + // Iterate over the block-expr bindings. + for (Environment::beb_iterator I = Env.beb_begin(), E = Env.beb_end(); + I != E; ++I) { + Stmt* BlkExpr = I.getKey(); + + if (SymReaper.isLive(Loc, BlkExpr)) { + SVal X = I.getData(); + + // If the block expr's value is a memory region, then mark that region. + if (isa<loc::MemRegionVal>(X)) + DRoots.push_back(cast<loc::MemRegionVal>(X).getRegion()); + + // Mark all symbols in the block expr's value live. + MarkLiveCallback cb(SymReaper); + StateMgr.scanReachableSymbols(X, state, cb); + } else { + // The block expr is dead. + SVal X = I.getData(); + + // 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()) + continue; + + Env = RemoveBlkExpr(Env, BlkExpr); + } + } + + return Env; +} diff --git a/lib/Analysis/ExplodedGraph.cpp b/lib/Analysis/ExplodedGraph.cpp new file mode 100644 index 0000000..20de6c4 --- /dev/null +++ b/lib/Analysis/ExplodedGraph.cpp @@ -0,0 +1,241 @@ +//=-- 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/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. +ExplodedNodeImpl::Auditor::~Auditor() {} + +#ifndef NDEBUG +static ExplodedNodeImpl::Auditor* NodeAuditor = 0; +#endif + +void ExplodedNodeImpl::SetAuditor(ExplodedNodeImpl::Auditor* A) { +#ifndef NDEBUG + NodeAuditor = A; +#endif +} + +//===----------------------------------------------------------------------===// +// ExplodedNodeImpl. +//===----------------------------------------------------------------------===// + +static inline std::vector<ExplodedNodeImpl*>& getVector(void* P) { + return *reinterpret_cast<std::vector<ExplodedNodeImpl*>*>(P); +} + +void ExplodedNodeImpl::addPredecessor(ExplodedNodeImpl* V) { + assert (!V->isSink()); + Preds.addNode(V); + V->Succs.addNode(this); +#ifndef NDEBUG + if (NodeAuditor) NodeAuditor->AddEdge(V, this); +#endif +} + +void ExplodedNodeImpl::NodeGroup::addNode(ExplodedNodeImpl* N) { + + assert ((reinterpret_cast<uintptr_t>(N) & Mask) == 0x0); + assert (!getFlag()); + + if (getKind() == Size1) { + if (ExplodedNodeImpl* NOld = getNode()) { + std::vector<ExplodedNodeImpl*>* V = new std::vector<ExplodedNodeImpl*>(); + assert ((reinterpret_cast<uintptr_t>(V) & Mask) == 0x0); + V->push_back(NOld); + V->push_back(N); + 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); + } +} + + +unsigned ExplodedNodeImpl::NodeGroup::size() const { + if (getFlag()) + return 0; + + if (getKind() == Size1) + return getNode() ? 1 : 0; + else + return getVector(getPtr()).size(); +} + +ExplodedNodeImpl** ExplodedNodeImpl::NodeGroup::begin() const { + if (getFlag()) + return NULL; + + if (getKind() == Size1) + return (ExplodedNodeImpl**) (getPtr() ? &P : NULL); + else + return const_cast<ExplodedNodeImpl**>(&*(getVector(getPtr()).begin())); +} + +ExplodedNodeImpl** ExplodedNodeImpl::NodeGroup::end() const { + if (getFlag()) + return NULL; + + if (getKind() == Size1) + return (ExplodedNodeImpl**) (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<ExplodedNodeImpl**>(&getVector(getPtr()).back()) + 1; + } +} + +ExplodedNodeImpl::NodeGroup::~NodeGroup() { + if (getKind() == SizeOther) delete &getVector(getPtr()); +} + +ExplodedGraphImpl* +ExplodedGraphImpl::Trim(const ExplodedNodeImpl* const* BeginSources, + const ExplodedNodeImpl* const* EndSources, + InterExplodedGraphMapImpl* M, + llvm::DenseMap<const void*, const void*> *InverseMap) +const { + + typedef llvm::DenseSet<const ExplodedNodeImpl*> Pass1Ty; + Pass1Ty Pass1; + + typedef llvm::DenseMap<const ExplodedNodeImpl*, ExplodedNodeImpl*> Pass2Ty; + Pass2Ty& Pass2 = M->M; + + llvm::SmallVector<const ExplodedNodeImpl*, 10> WL1, WL2; + + // ===- Pass 1 (reverse DFS) -=== + for (const ExplodedNodeImpl* 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 ExplodedNodeImpl *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 (ExplodedNodeImpl** 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. + ExplodedGraphImpl* G = MakeEmptyGraph(); + + // ===- Pass 2 (forward DFS to construct the new graph) -=== + while (!WL2.empty()) { + const ExplodedNodeImpl* 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. + ExplodedNodeImpl* NewN = G->getNodeImpl(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 (ExplodedNodeImpl **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); + } + + // 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 (ExplodedNodeImpl **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); + 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; +} + +ExplodedNodeImpl* +InterExplodedGraphMapImpl::getMappedImplNode(const ExplodedNodeImpl* N) const { + llvm::DenseMap<const ExplodedNodeImpl*, ExplodedNodeImpl*>::iterator I = + M.find(N); + + return I == M.end() ? 0 : I->second; +} + +InterExplodedGraphMapImpl::InterExplodedGraphMapImpl() {} + diff --git a/lib/Analysis/GRBlockCounter.cpp b/lib/Analysis/GRBlockCounter.cpp new file mode 100644 index 0000000..f69a16d --- /dev/null +++ b/lib/Analysis/GRBlockCounter.cpp @@ -0,0 +1,54 @@ +//==- 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 new file mode 100644 index 0000000..ff7b548 --- /dev/null +++ b/lib/Analysis/GRCoreEngine.cpp @@ -0,0 +1,576 @@ +//==- 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/AST/Expr.h" +#include "llvm/Support/Compiler.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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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. +//===----------------------------------------------------------------------===// + +/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. +bool GRCoreEngineImpl::ExecuteWorkList(unsigned Steps) { + + if (G->num_roots() == 0) { // Initialize the analysis by constructing + // the root if none exists. + + CFGBlock* Entry = &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); + + // Set the current block counter to being empty. + WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); + + // Generate the root. + GenerateNode(StartLoc, getInitialState(), 0); + } + + while (Steps && WList->hasWork()) { + --Steps; + const GRWorkListUnit& WU = WList->Dequeue(); + + // Set the current block counter. + WList->setBlockCounter(WU.getBlockCounter()); + + // Retrieve the node. + ExplodedNodeImpl* 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 GRCoreEngineImpl::HandleBlockEdge(const BlockEdge& L, + ExplodedNodeImpl* Pred) { + + CFGBlock* Blk = L.getDst(); + + // Check if we are entering the EXIT block. + if (Blk == &getCFG().getExit()) { + + assert (getCFG().getExit().size() == 0 + && "EXIT block cannot contain Stmts."); + + // Process the final state transition. + GREndPathNodeBuilderImpl 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->State, Pred); +} + +void GRCoreEngineImpl::HandleBlockEntrance(const BlockEntrance& L, + ExplodedNodeImpl* 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 (Stmt* S = L.getFirstStmt()) { + GRStmtNodeBuilderImpl Builder(L.getBlock(), 0, Pred, this); + ProcessStmt(S, Builder); + } + else + HandleBlockExit(L.getBlock(), Pred); +} + +GRCoreEngineImpl::~GRCoreEngineImpl() { + delete WList; +} + +void GRCoreEngineImpl::HandleBlockExit(CFGBlock * B, ExplodedNodeImpl* 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); + + GRIndirectGotoNodeBuilderImpl + 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: { + GRSwitchNodeBuilderImpl 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->State, Pred); +} + +void GRCoreEngineImpl::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, + ExplodedNodeImpl* Pred) { + assert (B->succ_size() == 2); + + GRBranchNodeBuilderImpl Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), + Pred, this); + + ProcessBranch(Cond, Term, Builder); +} + +void GRCoreEngineImpl::HandlePostStmt(const PostStmt& L, CFGBlock* B, + unsigned StmtIdx, ExplodedNodeImpl* Pred) { + + assert (!B->empty()); + + if (StmtIdx == B->size()) + HandleBlockExit(B, Pred); + else { + GRStmtNodeBuilderImpl Builder(B, StmtIdx, Pred, this); + ProcessStmt((*B)[StmtIdx], Builder); + } +} + +/// GenerateNode - Utility method to generate nodes, hook up successors, +/// and add nodes to the worklist. +void GRCoreEngineImpl::GenerateNode(const ProgramPoint& Loc, const void* State, + ExplodedNodeImpl* Pred) { + + bool IsNew; + ExplodedNodeImpl* Node = G->getNodeImpl(Loc, State, &IsNew); + + if (Pred) + Node->addPredecessor(Pred); // 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); +} + +GRStmtNodeBuilderImpl::GRStmtNodeBuilderImpl(CFGBlock* b, unsigned idx, + ExplodedNodeImpl* N, GRCoreEngineImpl* e) + : Eng(*e), B(*b), Idx(idx), Pred(N), LastNode(N) { + Deferred.insert(N); +} + +GRStmtNodeBuilderImpl::~GRStmtNodeBuilderImpl() { + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) + GenerateAutoTransition(*I); +} + +void GRStmtNodeBuilderImpl::GenerateAutoTransition(ExplodedNodeImpl* N) { + assert (!N->isSink()); + + PostStmt Loc(getStmt()); + + 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; + ExplodedNodeImpl* Succ = Eng.G->getNodeImpl(Loc, N->State, &IsNew); + Succ->addPredecessor(N); + + if (IsNew) + Eng.WList->Enqueue(Succ, B, Idx+1); +} + +static inline PostStmt GetPostLoc(Stmt* S, ProgramPoint::Kind K, + const void *tag) { + switch (K) { + default: + assert(false && "Invalid PostXXXKind."); + + case ProgramPoint::PostStmtKind: + return PostStmt(S, tag); + + case ProgramPoint::PostLoadKind: + return PostLoad(S, tag); + + case ProgramPoint::PostUndefLocationCheckFailedKind: + return PostUndefLocationCheckFailed(S, tag); + + case ProgramPoint::PostLocationChecksSucceedKind: + return PostLocationChecksSucceed(S, tag); + + case ProgramPoint::PostOutOfBoundsCheckFailedKind: + return PostOutOfBoundsCheckFailed(S, tag); + + case ProgramPoint::PostNullCheckFailedKind: + return PostNullCheckFailed(S, tag); + + case ProgramPoint::PostStoreKind: + return PostStore(S, tag); + + case ProgramPoint::PostLValueKind: + return PostLValue(S, tag); + + case ProgramPoint::PostPurgeDeadSymbolsKind: + return PostPurgeDeadSymbols(S, tag); + } +} + +ExplodedNodeImpl* +GRStmtNodeBuilderImpl::generateNodeImpl(Stmt* S, const void* State, + ExplodedNodeImpl* Pred, + ProgramPoint::Kind K, + const void *tag) { + return generateNodeImpl(GetPostLoc(S, K, tag), State, Pred); +} + +ExplodedNodeImpl* +GRStmtNodeBuilderImpl::generateNodeImpl(PostStmt Loc, const void* State, + ExplodedNodeImpl* Pred) { + bool IsNew; + ExplodedNodeImpl* N = Eng.G->getNodeImpl(Loc, State, &IsNew); + N->addPredecessor(Pred); + Deferred.erase(Pred); + + if (IsNew) { + Deferred.insert(N); + LastNode = N; + return N; + } + + LastNode = NULL; + return NULL; +} + +ExplodedNodeImpl* GRBranchNodeBuilderImpl::generateNodeImpl(const void* State, + bool branch) { + bool IsNew; + + ExplodedNodeImpl* Succ = + Eng.G->getNodeImpl(BlockEdge(Src, branch ? DstT : DstF), State, &IsNew); + + Succ->addPredecessor(Pred); + + if (branch) GeneratedTrue = true; + else GeneratedFalse = true; + + if (IsNew) { + Deferred.push_back(Succ); + return Succ; + } + + return NULL; +} + +GRBranchNodeBuilderImpl::~GRBranchNodeBuilderImpl() { + if (!GeneratedTrue) generateNodeImpl(Pred->State, true); + if (!GeneratedFalse) generateNodeImpl(Pred->State, false); + + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) Eng.WList->Enqueue(*I); +} + + +ExplodedNodeImpl* +GRIndirectGotoNodeBuilderImpl::generateNodeImpl(const Iterator& I, + const void* St, + bool isSink) { + bool IsNew; + + ExplodedNodeImpl* Succ = + Eng.G->getNodeImpl(BlockEdge(Src, I.getBlock()), St, &IsNew); + + Succ->addPredecessor(Pred); + + if (IsNew) { + + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + + +ExplodedNodeImpl* +GRSwitchNodeBuilderImpl::generateCaseStmtNodeImpl(const Iterator& I, + const void* St) { + + bool IsNew; + + ExplodedNodeImpl* Succ = Eng.G->getNodeImpl(BlockEdge(Src, I.getBlock()), + St, &IsNew); + Succ->addPredecessor(Pred); + + if (IsNew) { + Eng.WList->Enqueue(Succ); + return Succ; + } + + return NULL; +} + + +ExplodedNodeImpl* +GRSwitchNodeBuilderImpl::generateDefaultCaseNodeImpl(const void* St, + bool isSink) { + + // Get the block for the default case. + assert (Src->succ_rbegin() != Src->succ_rend()); + CFGBlock* DefaultBlock = *Src->succ_rbegin(); + + bool IsNew; + + ExplodedNodeImpl* Succ = Eng.G->getNodeImpl(BlockEdge(Src, DefaultBlock), + St, &IsNew); + Succ->addPredecessor(Pred); + + if (IsNew) { + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + +GREndPathNodeBuilderImpl::~GREndPathNodeBuilderImpl() { + // Auto-generate an EOP node if one has not been generated. + if (!HasGeneratedNode) generateNodeImpl(Pred->State); +} + +ExplodedNodeImpl* +GREndPathNodeBuilderImpl::generateNodeImpl(const void* State, + const void *tag, + ExplodedNodeImpl* P) { + HasGeneratedNode = true; + bool IsNew; + + ExplodedNodeImpl* Node = + Eng.G->getNodeImpl(BlockEntrance(&B, tag), State, &IsNew); + + Node->addPredecessor(P ? P : Pred); + + if (IsNew) { + Eng.G->addEndOfPath(Node); + return Node; + } + + return NULL; +} diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp new file mode 100644 index 0000000..e8c5be5 --- /dev/null +++ b/lib/Analysis/GRExprEngine.cpp @@ -0,0 +1,3426 @@ +//=-- 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 "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/GRExprEngineBuilders.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/PrettyStackTrace.h" +#include "llvm/Support/Streams.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" + +#ifndef NDEBUG +#include "llvm/Support/GraphWriter.h" +#include <sstream> +#endif + +using namespace clang; +using llvm::dyn_cast; +using llvm::cast; +using llvm::APSInt; + +//===----------------------------------------------------------------------===// +// Engine construction and deletion. +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN 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(NodeTy* 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. + 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 + +//===----------------------------------------------------------------------===// +// Engine construction and deletion. +//===----------------------------------------------------------------------===// + +static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(0, &II); +} + + +GRExprEngine::GRExprEngine(CFG& cfg, Decl& CD, ASTContext& Ctx, + LiveVariables& L, BugReporterData& BRD, + bool purgeDead, bool eagerlyAssume, + StoreManagerCreator SMC, + ConstraintManagerCreator CMC) + : CoreEngine(cfg, CD, Ctx, *this), + G(CoreEngine.getGraph()), + Liveness(L), + Builder(NULL), + StateMgr(G.getContext(), SMC, CMC, G.getAllocator(), cfg, CD, L), + SymMgr(StateMgr.getSymbolManager()), + ValMgr(StateMgr.getValueManager()), + CurrentStmt(NULL), + NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), + RaiseSel(GetNullarySelector("raise", G.getContext())), + PurgeDead(purgeDead), + BR(BRD, *this), + EagerlyAssume(eagerlyAssume) {} + +GRExprEngine::~GRExprEngine() { + BR.FlushReports(); + delete [] NSExceptionInstanceRaiseSelectors; +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + + +void GRExprEngine::setTransferFunctions(GRTransferFuncs* tf) { + StateMgr.TF = tf; + tf->RegisterChecks(getBugReporter()); + tf->RegisterPrinters(getStateManager().Printers); +} + +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 GRState *state = StateMgr.getInitialState(); + + // Precondition: the first argument of 'main' is an integer guaranteed + // to be > 0. + // FIXME: It would be nice if we had a more general mechanism to add + // such preconditions. Some day. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(&StateMgr.getCodeDecl())) + if (strcmp(FD->getIdentifier()->getName(), "main") == 0 && + FD->getNumParams() > 0) { + const ParmVarDecl *PD = FD->getParamDecl(0); + QualType T = PD->getType(); + if (T->isIntegerType()) + if (const MemRegion *R = StateMgr.getRegion(PD)) { + SVal V = GetSVal(state, loc::MemRegionVal(R)); + SVal Constraint = EvalBinOp(state, BinaryOperator::GT, V, + ValMgr.makeZeroVal(T), + getContext().IntTy); + bool isFeasible = false; + const GRState *newState = Assume(state, Constraint, true, + isFeasible); + if (newState) state = newState; + } + } + + return state; +} + +//===----------------------------------------------------------------------===// +// Top-level transfer function logic (Dispatcher). +//===----------------------------------------------------------------------===// + +void GRExprEngine::ProcessStmt(Stmt* S, StmtNodeBuilder& builder) { + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + S->getLocStart(), + "Error evaluating statement"); + + Builder = &builder; + EntryNode = builder.getLastNode(); + + // FIXME: Consolidate. + CurrentStmt = S; + StateMgr.CurrentStmt = S; + + // Set up our simple checks. + if (BatchAuditor) + Builder->setAuditor(BatchAuditor.get()); + + // Create the cleaned state. + SymbolReaper SymReaper(Liveness, SymMgr); + CleanedState = PurgeDead ? StateMgr.RemoveDeadBindings(EntryNode->getState(), + CurrentStmt, SymReaper) + : EntryNode->getState(); + + // Process any special transfer function for dead symbols. + NodeSet 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; + + getTF().EvalDeadSymbols(Tmp, *this, *Builder, EntryNode, S, + CleanedState, SymReaper); + + if (!Builder->BuildSinks && !Builder->HasGeneratedNode) + Tmp.Add(EntryNode); + } + + bool HasAutoGenerated = false; + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + NodeSet Dst; + + // Set the cleaned state. + Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); + + // Visit the statement. + Visit(S, *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(S, GetState(EntryNode), *I); + } + } + + // NULL out these variables to cleanup. + CleanedState = NULL; + EntryNode = NULL; + + // FIXME: Consolidate. + StateMgr.CurrentStmt = 0; + CurrentStmt = 0; + + Builder = NULL; +} + +void GRExprEngine::Visit(Stmt* S, NodeTy* Pred, NodeSet& 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 && getCFG().isBlkExpr(S)) { + Dst.Add(Pred); + return; + } + + switch (S->getStmtClass()) { + + 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::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, BindExpr(state, B, GetSVal(state, B->getRHS()))); + break; + } + + if (EagerlyAssume && (B->isRelationalOp() || B->isEqualityOp())) { + NodeSet Tmp; + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp); + EvalEagerlyAssume(Dst, Tmp, cast<Expr>(S)); + } + else + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + + break; + } + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: { + CallExpr* C = cast<CallExpr>(S); + VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst); + break; + } + + // FIXME: ChooseExpr is really a constant. We need to fix + // the CFG do not model them as explicit control-flow. + + case Stmt::ChooseExprClass: { // __builtin_choose_expr + ChooseExpr* C = cast<ChooseExpr>(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + break; + } + + case Stmt::CompoundAssignOperatorClass: + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + 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::DeclRefExprClass: + case Stmt::QualifiedDeclRefExprClass: + VisitDeclRefExpr(cast<DeclRefExpr>(S), Pred, Dst, false); + break; + + case Stmt::DeclStmtClass: + VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); + break; + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: { + CastExpr* C = cast<CastExpr>(S); + VisitCast(C, C->getSubExpr(), 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); + 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, BindExpr(state, SE, GetSVal(state, LastExpr))); + } + else + Dst.Add(Pred); + + break; + } + + case Stmt::StringLiteralClass: + VisitLValue(cast<StringLiteral>(S), Pred, Dst); + break; + + case Stmt::UnaryOperatorClass: { + UnaryOperator *U = cast<UnaryOperator>(S); + if (EagerlyAssume && (U->getOpcode() == UnaryOperator::LNot)) { + NodeSet Tmp; + VisitUnaryOperator(U, Pred, Tmp, false); + EvalEagerlyAssume(Dst, Tmp, U); + } + else + VisitUnaryOperator(U, Pred, Dst, false); + break; + } + } +} + +void GRExprEngine::VisitLValue(Expr* Ex, NodeTy* Pred, NodeSet& Dst) { + + Ex = Ex->IgnoreParens(); + + if (Ex != CurrentStmt && getCFG().isBlkExpr(Ex)) { + Dst.Add(Pred); + return; + } + + switch (Ex->getStmtClass()) { + + case Stmt::ArraySubscriptExprClass: + VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(Ex), Pred, Dst, true); + return; + + case Stmt::DeclRefExprClass: + case Stmt::QualifiedDeclRefExprClass: + VisitDeclRefExpr(cast<DeclRefExpr>(Ex), Pred, Dst, true); + return; + + case Stmt::ObjCIvarRefExprClass: + VisitObjCIvarRefExpr(cast<ObjCIvarRefExpr>(Ex), Pred, Dst, true); + return; + + case Stmt::UnaryOperatorClass: + VisitUnaryOperator(cast<UnaryOperator>(Ex), Pred, Dst, true); + return; + + case Stmt::MemberExprClass: + VisitMemberExpr(cast<MemberExpr>(Ex), Pred, Dst, true); + return; + + case Stmt::CompoundLiteralExprClass: + VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(Ex), Pred, Dst, true); + return; + + case Stmt::ObjCPropertyRefExprClass: + case Stmt::ObjCKVCRefExprClass: + // 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 = StateMgr.GetLValue(state, cast<StringLiteral>(Ex)); + MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, V)); + 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. +//===----------------------------------------------------------------------===// + +GRExprEngine::NodeTy* GRExprEngine::MakeNode(NodeSet& Dst, Stmt* S, + NodeTy* 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 BindBlkExpr(state, 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 BindBlkExpr(state, C, UndefinedVal(Ex)); + } + + case Stmt::ChooseExprClass: { // ?: + + ChooseExpr* C = cast<ChooseExpr>(Terminator); + + Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); + return BindBlkExpr(state, 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 StateMgr.GetSVal(state, Ex); +} + +void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, + BranchNodeBuilder& builder) { + + // Remove old bindings for subexpressions. + const GRState* PrevState = + StateMgr.RemoveSubExprBindings(builder.getState()); + + // Check for NULL conditions; e.g. "for(;;)" + if (!Condition) { + builder.markInfeasible(false); + return; + } + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Condition->getLocStart(), + "Error evaluating branch"); + + SVal V = GetSVal(PrevState, Condition); + + switch (V.getBaseKind()) { + default: + break; + + case SVal::UnknownKind: { + if (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()) { + V = recovered; + break; + } + } + } + + builder.generateNode(MarkBranch(PrevState, Term, true), true); + builder.generateNode(MarkBranch(PrevState, Term, false), false); + return; + } + + case SVal::UndefinedKind: { + NodeTy* N = builder.generateNode(PrevState, true); + + if (N) { + N->markAsSink(); + UndefBranches.insert(N); + } + + builder.markInfeasible(false); + return; + } + } + + // Process the true branch. + + bool isFeasible = false; + const GRState* state = Assume(PrevState, V, true, isFeasible); + + if (isFeasible) + builder.generateNode(MarkBranch(state, Term, true), true); + else + builder.markInfeasible(true); + + // Process the false branch. + + isFeasible = false; + state = Assume(PrevState, V, false, isFeasible); + + if (isFeasible) + 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(IndirectGotoNodeBuilder& builder) { + + const GRState* state = builder.getState(); + SVal V = GetSVal(state, 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 IndirectGotoNodeBuilder::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. + NodeTy* N = builder.generateNode(builder.begin(), state, true); + 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, + NodeTy* Pred, NodeSet& Dst) { + + assert (Ex == CurrentStmt && getCFG().isBlkExpr(Ex)); + + const GRState* state = GetState(Pred); + SVal X = GetBlkExprSVal(state, Ex); + + assert (X.isUndef()); + + Expr* SE = (Expr*) cast<UndefinedVal>(X).getData(); + + assert (SE); + + X = GetBlkExprSVal(state, SE); + + // Make sure that we invalidate the previous binding. + MakeNode(Dst, Ex, Pred, StateMgr.BindExpr(state, Ex, X, true, true)); +} + +/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a switch statement. +void GRExprEngine::ProcessSwitch(SwitchNodeBuilder& builder) { + typedef SwitchNodeBuilder::iterator iterator; + const GRState* state = builder.getState(); + Expr* CondE = builder.getCondition(); + SVal CondV = GetSVal(state, CondE); + + if (CondV.isUndef()) { + NodeTy* N = builder.generateDefaultCaseNode(state, true); + UndefBranches.insert(N); + return; + } + + const GRState* DefaultSt = state; + bool DefaultFeasible = 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())); + SVal Res = EvalBinOp(DefaultSt, BinaryOperator::EQ, CondV, CaseVal, + getContext().IntTy); + + // Now "assume" that the case matches. + bool isFeasible = false; + const GRState* StNew = Assume(state, Res, true, isFeasible); + + if (isFeasible) { + builder.generateCaseStmtNode(I, StNew); + + // 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). + + isFeasible = false; + StNew = Assume(DefaultSt, Res, false, isFeasible); + + if (isFeasible) { + DefaultFeasible = true; + DefaultSt = StNew; + } + + // 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 (DefaultFeasible) builder.generateDefaultCaseNode(DefaultSt); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: logical operations ('&&', '||'). +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, NodeTy* Pred, + NodeSet& Dst) { + + assert (B->getOpcode() == BinaryOperator::LAnd || + B->getOpcode() == BinaryOperator::LOr); + + assert (B == CurrentStmt && getCFG().isBlkExpr(B)); + + const GRState* state = GetState(Pred); + SVal X = GetBlkExprSVal(state, B); + + assert (X.isUndef()); + + Expr* Ex = (Expr*) cast<UndefinedVal>(X).getData(); + + assert (Ex); + + if (Ex == B->getRHS()) { + + X = GetBlkExprSVal(state, Ex); + + // Handle undefined values. + + if (X.isUndef()) { + MakeNode(Dst, B, Pred, BindBlkExpr(state, B, X)); + return; + } + + // 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. + + bool isFeasible = false; + const GRState* NewState = Assume(state, X, true, isFeasible); + + if (isFeasible) + MakeNode(Dst, B, Pred, + BindBlkExpr(NewState, B, MakeConstantVal(1U, B))); + + isFeasible = false; + NewState = Assume(state, X, false, isFeasible); + + if (isFeasible) + MakeNode(Dst, B, Pred, + BindBlkExpr(NewState, B, MakeConstantVal(0U, B))); + } + 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 = MakeConstantVal( B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U, B); + MakeNode(Dst, B, Pred, BindBlkExpr(state, B, X)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Loads and stores. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitDeclRefExpr(DeclRefExpr* Ex, NodeTy* Pred, NodeSet& Dst, + bool asLValue) { + + const GRState* state = GetState(Pred); + + const NamedDecl* D = Ex->getDecl(); + + if (const VarDecl* VD = dyn_cast<VarDecl>(D)) { + + SVal V = StateMgr.GetLValue(state, VD); + + if (asLValue) + MakeNode(Dst, Ex, Pred, BindExpr(state, 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."); + + BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + SVal V = nonloc::ConcreteInt(BasicVals.getValue(ED->getInitVal())); + MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, V)); + return; + + } else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) { + assert(asLValue); + SVal V = ValMgr.getFunctionPointer(FD); + MakeNode(Dst, Ex, Pred, BindExpr(state, 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, NodeTy* Pred, + NodeSet& Dst, bool asLValue) { + + Expr* Base = A->getBase()->IgnoreParens(); + Expr* Idx = A->getIdx()->IgnoreParens(); + NodeSet 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 (NodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { + NodeSet Tmp2; + Visit(Idx, *I1, Tmp2); // Evaluate the index. + + for (NodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end(); I2!=E2; ++I2) { + const GRState* state = GetState(*I2); + SVal V = StateMgr.GetLValue(state, A->getType(), + GetSVal(state, Base), + GetSVal(state, Idx)); + + if (asLValue) + MakeNode(Dst, A, *I2, BindExpr(state, A, V), + ProgramPoint::PostLValueKind); + else + EvalLoad(Dst, A, *I2, state, V); + } + } +} + +/// VisitMemberExpr - Transfer function for member expressions. +void GRExprEngine::VisitMemberExpr(MemberExpr* M, NodeTy* Pred, + NodeSet& Dst, bool asLValue) { + + Expr* Base = M->getBase()->IgnoreParens(); + NodeSet 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 (NodeSet::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 = StateMgr.GetLValue(state, GetSVal(state, Base), Field); + + if (asLValue) + MakeNode(Dst, M, *I, BindExpr(state, 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(NodeSet& Dst, Expr* Ex, NodeTy* Pred, + const GRState* state, SVal location, SVal Val) { + + const GRState* newState = 0; + + 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 = StateMgr.BindLoc(state, 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, Pred, newState, Ex, + 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(NodeSet& Dst, Expr* Ex, NodeTy* Pred, + const GRState* state, SVal location, SVal Val, + const void *tag) { + + assert (Builder && "GRStmtNodeBuilder must be defined."); + + // Evaluate the location (checks for bad dereferences). + Pred = EvalLocation(Ex, Pred, state, location, tag); + + if (!Pred) + return; + + assert (!location.isUndef()); + state = GetState(Pred); + + // Proceed with the store. + SaveAndRestore<ProgramPoint::Kind> OldSPointKind(Builder->PointKind); + SaveAndRestore<const void*> OldTag(Builder->Tag); + Builder->PointKind = ProgramPoint::PostStoreKind; + Builder->Tag = tag; + EvalBind(Dst, Ex, Pred, state, location, Val); +} + +void GRExprEngine::EvalLoad(NodeSet& Dst, Expr* Ex, NodeTy* Pred, + const GRState* state, SVal location, + const void *tag) { + + // Evaluate the location (checks for bad dereferences). + Pred = EvalLocation(Ex, Pred, state, location, tag); + + if (!Pred) + return; + + state = GetState(Pred); + + // Proceed with the load. + ProgramPoint::Kind K = ProgramPoint::PostLoadKind; + + // FIXME: Currently symbolic analysis "generates" new symbols + // for the contents of values. We need a better approach. + + if (location.isUnknown()) { + // This is important. We must nuke the old binding. + MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, UnknownVal()), K, tag); + } + else { + SVal V = GetSVal(state, cast<Loc>(location), Ex->getType()); + MakeNode(Dst, Ex, Pred, BindExpr(state, Ex, V), K, tag); + } +} + +void GRExprEngine::EvalStore(NodeSet& Dst, Expr* Ex, Expr* StoreE, NodeTy* Pred, + const GRState* state, SVal location, SVal Val, + const void *tag) { + + NodeSet TmpDst; + EvalStore(TmpDst, StoreE, Pred, state, location, Val, tag); + + for (NodeSet::iterator I=TmpDst.begin(), E=TmpDst.end(); I!=E; ++I) + MakeNode(Dst, Ex, *I, (*I)->getState(), ProgramPoint::PostStmtKind, tag); +} + +GRExprEngine::NodeTy* GRExprEngine::EvalLocation(Stmt* Ex, NodeTy* Pred, + const GRState* state, + SVal location, + const void *tag) { + + SaveAndRestore<const void*> OldTag(Builder->Tag); + Builder->Tag = tag; + + // Check for loads/stores from/to undefined values. + if (location.isUndef()) { + NodeTy* N = + Builder->generateNode(Ex, state, Pred, + ProgramPoint::PostUndefLocationCheckFailedKind); + + if (N) { + N->markAsSink(); + UndefDeref.insert(N); + } + + return 0; + } + + // Check for loads/stores from/to unknown locations. Treat as No-Ops. + if (location.isUnknown()) + return Pred; + + // During a load, one of two possible situations arise: + // (1) A crash, because the location (pointer) was NULL. + // (2) The location (pointer) is not NULL, and the dereference works. + // + // We add these assumptions. + + Loc LV = cast<Loc>(location); + + // "Assume" that the pointer is not NULL. + bool isFeasibleNotNull = false; + const GRState* StNotNull = Assume(state, LV, true, isFeasibleNotNull); + + // "Assume" that the pointer is NULL. + bool isFeasibleNull = false; + GRStateRef StNull = GRStateRef(Assume(state, LV, false, isFeasibleNull), + getStateManager()); + + if (isFeasibleNull) { + + // Use the Generic Data Map to mark in the state what lval was null. + const SVal* PersistentLV = getBasicVals().getPersistentSVal(LV); + StNull = StNull.set<GRState::NullDerefTag>(PersistentLV); + + // We don't use "MakeNode" here because the node will be a sink + // and we have no intention of processing it later. + NodeTy* NullNode = + Builder->generateNode(Ex, StNull, Pred, + ProgramPoint::PostNullCheckFailedKind); + + if (NullNode) { + + NullNode->markAsSink(); + + if (isFeasibleNotNull) ImplicitNullDeref.insert(NullNode); + else ExplicitNullDeref.insert(NullNode); + } + } + + if (!isFeasibleNotNull) + return 0; + + // Check for out-of-bound array access. + if (isa<loc::MemRegionVal>(LV)) { + const MemRegion* R = cast<loc::MemRegionVal>(LV).getRegion(); + if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { + // Get the index of the accessed element. + SVal Idx = ER->getIndex(); + // Get the extent of the array. + SVal NumElements = getStoreManager().getSizeInElements(StNotNull, + ER->getSuperRegion()); + + bool isFeasibleInBound = false; + const GRState* StInBound = AssumeInBound(StNotNull, Idx, NumElements, + true, isFeasibleInBound); + + bool isFeasibleOutBound = false; + const GRState* StOutBound = AssumeInBound(StNotNull, Idx, NumElements, + false, isFeasibleOutBound); + + if (isFeasibleOutBound) { + // Report warning. Make sink node manually. + NodeTy* OOBNode = + Builder->generateNode(Ex, StOutBound, Pred, + ProgramPoint::PostOutOfBoundsCheckFailedKind); + + if (OOBNode) { + OOBNode->markAsSink(); + + if (isFeasibleInBound) + ImplicitOOBMemAccesses.insert(OOBNode); + else + ExplicitOOBMemAccesses.insert(OOBNode); + } + } + + if (!isFeasibleInBound) + return 0; + + StNotNull = StInBound; + } + } + + // Generate a new node indicating the checks succeed. + return Builder->generateNode(Ex, StNotNull, Pred, + ProgramPoint::PostLocationChecksSucceedKind); +} + +//===----------------------------------------------------------------------===// +// Transfer function: OSAtomics. +// +// FIXME: Eventually refactor into a more "plugin" infrastructure. +//===----------------------------------------------------------------------===// + +// Mac OS X: +// http://developer.apple.com/documentation/Darwin/Reference/Manpages/man3 +// atomic.3.html +// +static bool EvalOSAtomicCompareAndSwap(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + CallExpr* CE, SVal L, + ExplodedNode<GRState>* Pred) { + + // Not enough arguments to match OSAtomicCompareAndSwap? + if (CE->getNumArgs() != 3) + return false; + + ASTContext &C = Engine.getContext(); + Expr *oldValueExpr = CE->getArg(0); + QualType oldValueType = C.getCanonicalType(oldValueExpr->getType()); + + Expr *newValueExpr = CE->getArg(1); + QualType newValueType = C.getCanonicalType(newValueExpr->getType()); + + // Do the types of 'oldValue' and 'newValue' match? + if (oldValueType != newValueType) + return false; + + Expr *theValueExpr = CE->getArg(2); + const PointerType *theValueType = theValueExpr->getType()->getAsPointerType(); + + // theValueType not a pointer? + if (!theValueType) + return false; + + QualType theValueTypePointee = + C.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'. + GRStateManager &StateMgr = Engine.getStateManager(); + const GRState *state = Pred->getState(); + ExplodedNodeSet<GRState> Tmp; + SVal location = StateMgr.GetSVal(state, theValueExpr); + Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag); + + for (ExplodedNodeSet<GRState>::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode<GRState> *N = *I; + const GRState *stateLoad = N->getState(); + SVal theValueVal = StateMgr.GetSVal(stateLoad, theValueExpr); + SVal oldValueVal = StateMgr.GetSVal(stateLoad, oldValueExpr); + + // Perform the comparison. + SVal Cmp = Engine.EvalBinOp(stateLoad, + BinaryOperator::EQ, theValueVal, oldValueVal, + Engine.getContext().IntTy); + bool isFeasible = false; + const GRState *stateEqual = StateMgr.Assume(stateLoad, Cmp, true, + isFeasible); + + // Were they equal? + if (isFeasible) { + // Perform the store. + ExplodedNodeSet<GRState> TmpStore; + Engine.EvalStore(TmpStore, theValueExpr, N, stateEqual, location, + StateMgr.GetSVal(stateEqual, newValueExpr), + OSAtomicStoreTag); + + // Now bind the result of the comparison. + for (ExplodedNodeSet<GRState>::iterator I2 = TmpStore.begin(), + E2 = TmpStore.end(); I2 != E2; ++I2) { + ExplodedNode<GRState> *predNew = *I2; + const GRState *stateNew = predNew->getState(); + SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType()); + Engine.MakeNode(Dst, CE, predNew, Engine.BindExpr(stateNew, CE, Res)); + } + } + + // Were they not equal? + isFeasible = false; + const GRState *stateNotEqual = StateMgr.Assume(stateLoad, Cmp, false, + isFeasible); + + if (isFeasible) { + SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType()); + Engine.MakeNode(Dst, CE, N, Engine.BindExpr(stateNotEqual, CE, Res)); + } + } + + return true; +} + +static bool EvalOSAtomic(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + CallExpr* CE, SVal L, + ExplodedNode<GRState>* Pred) { + const FunctionDecl* FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + const char *FName = FD->getNameAsCString(); + + // Check for compare and swap. + if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 || + strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0) + return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, L, Pred); + + // FIXME: Other atomics. + return false; +} + +//===----------------------------------------------------------------------===// +// Transfer function: Function calls. +//===----------------------------------------------------------------------===// + +void GRExprEngine::EvalCall(NodeSet& Dst, CallExpr* CE, SVal L, NodeTy* Pred) { + assert (Builder && "GRStmtNodeBuilder must be defined."); + + // FIXME: Allow us to chain together transfer functions. + if (EvalOSAtomic(Dst, *this, *Builder, CE, L, Pred)) + return; + + getTF().EvalCall(Dst, *this, *Builder, CE, L, Pred); +} + +void GRExprEngine::VisitCall(CallExpr* CE, NodeTy* Pred, + CallExpr::arg_iterator AI, + CallExpr::arg_iterator AE, + NodeSet& Dst) +{ + // 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->getAsPointerType()) + Proto = FnTypePtr->getPointeeType()->getAsFunctionProtoType(); + + VisitCallRec(CE, Pred, AI, AE, Dst, Proto, /*ParamIdx=*/0); +} + +void GRExprEngine::VisitCallRec(CallExpr* CE, NodeTy* Pred, + CallExpr::arg_iterator AI, + CallExpr::arg_iterator AE, + NodeSet& Dst, const FunctionProtoType *Proto, + unsigned ParamIdx) { + + // Process the arguments. + if (AI != AE) { + // If the call argument is being bound to a reference parameter, + // visit it as an lvalue, not an rvalue. + bool VisitAsLvalue = false; + if (Proto && ParamIdx < Proto->getNumArgs()) + VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType(); + + NodeSet DstTmp; + if (VisitAsLvalue) + VisitLValue(*AI, Pred, DstTmp); + else + Visit(*AI, Pred, DstTmp); + ++AI; + + for (NodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI != DE; ++DI) + VisitCallRec(CE, *DI, AI, AE, Dst, Proto, ParamIdx + 1); + + return; + } + + // If we reach here we have processed all of the arguments. Evaluate + // the callee expression. + + NodeSet DstTmp; + Expr* Callee = CE->getCallee()->IgnoreParens(); + + Visit(Callee, Pred, DstTmp); + + // Finally, evaluate the function call. + for (NodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); DI!=DE; ++DI) { + + const GRState* state = GetState(*DI); + SVal L = GetSVal(state, Callee); + + // FIXME: Add support for symbolic function calls (calls involving + // function pointer values that are symbolic). + + // Check for undefined control-flow or calls to NULL. + + if (L.isUndef() || isa<loc::ConcreteInt>(L)) { + NodeTy* N = Builder->generateNode(CE, state, *DI); + + if (N) { + N->markAsSink(); + BadCalls.insert(N); + } + + continue; + } + + // Check for the "noreturn" attribute. + + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + const FunctionDecl* FD = L.getAsFunctionDecl(); + if (FD) { + if (FD->getAttr<NoReturnAttr>() || FD->getAttr<AnalyzerNoReturnAttr>()) + Builder->BuildSinks = true; + else { + // 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. + const char* s = FD->getIdentifier()->getName(); + unsigned n = strlen(s); + + switch (n) { + default: + break; + + case 4: + if (!memcmp(s, "exit", 4)) Builder->BuildSinks = true; + break; + + case 5: + if (!memcmp(s, "panic", 5)) Builder->BuildSinks = true; + else if (!memcmp(s, "error", 5)) { + if (CE->getNumArgs() > 0) { + SVal X = GetSVal(state, *CE->arg_begin()); + // FIXME: use Assume to inspect the possible symbolic value of + // X. Also check the specific signature of error(). + nonloc::ConcreteInt* CI = dyn_cast<nonloc::ConcreteInt>(&X); + if (CI && CI->getValue() != 0) + Builder->BuildSinks = true; + } + } + break; + + case 6: + if (!memcmp(s, "Assert", 6)) { + Builder->BuildSinks = true; + break; + } + + // FIXME: This is just a wrapper around throwing an exception. + // Eventually inter-procedural analysis should handle this easily. + if (!memcmp(s, "ziperr", 6)) Builder->BuildSinks = true; + + break; + + case 7: + if (!memcmp(s, "assfail", 7)) Builder->BuildSinks = true; + break; + + case 8: + if (!memcmp(s ,"db_error", 8) || + !memcmp(s, "__assert", 8)) + Builder->BuildSinks = true; + break; + + case 12: + if (!memcmp(s, "__assert_rtn", 12)) Builder->BuildSinks = true; + break; + + case 13: + if (!memcmp(s, "__assert_fail", 13)) Builder->BuildSinks = true; + break; + + case 14: + if (!memcmp(s, "dtrace_assfail", 14) || + !memcmp(s, "yy_fatal_error", 14)) + Builder->BuildSinks = true; + break; + + case 26: + if (!memcmp(s, "_XCAssertionFailureHandler", 26) || + !memcmp(s, "_DTAssertionFailureHandler", 26) || + !memcmp(s, "_TSAssertionFailureHandler", 26)) + Builder->BuildSinks = true; + + break; + } + + } + } + + // Evaluate the call. + + if (FD) { + + if (unsigned id = FD->getBuiltinID(getContext())) + 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 = GetSVal(state, *(CE->arg_begin())); + MakeNode(Dst, CE, *DI, BindExpr(state, CE, X)); + continue; + } + + case Builtin::BI__builtin_alloca: { + // FIXME: Refactor into StoreManager itself? + MemRegionManager& RM = getStateManager().getRegionManager(); + const MemRegion* R = + RM.getAllocaRegion(CE, Builder->getCurrentBlockCount()); + + // 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 = GetSVal(state, *(CE->arg_begin())); + state = getStoreManager().setExtent(state, R, Extent); + + MakeNode(Dst, CE, *DI, BindExpr(state, CE, loc::MemRegionVal(R))); + continue; + } + + default: + break; + } + } + + // Check any arguments passed-by-value against being undefined. + + bool badArg = false; + + for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) { + + if (GetSVal(GetState(*DI), *I).isUndef()) { + NodeTy* N = Builder->generateNode(CE, GetState(*DI), *DI); + + if (N) { + N->markAsSink(); + UndefArgs[N] = *I; + } + + badArg = true; + break; + } + } + + if (badArg) + continue; + + // Dispatch to the plug-in transfer function. + + unsigned size = Dst.size(); + SaveOr OldHasGen(Builder->HasGeneratedNode); + EvalCall(Dst, CE, L, *DI); + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + + if (!Builder->BuildSinks && Dst.size() == size && + !Builder->HasGeneratedNode) + MakeNode(Dst, CE, *DI, state); + } +} + +//===----------------------------------------------------------------------===// +// 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(NodeSet &Dst, NodeSet &Src, Expr *Ex) { + for (NodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { + NodeTy *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 = GetSVal(state, Ex); + if (isa<nonloc::SymExprVal>(V)) { + // First assume that the condition is true. + bool isFeasible = false; + const GRState *stateTrue = Assume(state, V, true, isFeasible); + if (isFeasible) { + stateTrue = BindExpr(stateTrue, Ex, MakeConstantVal(1U, Ex)); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag), + stateTrue, Pred)); + } + + // Next, assume that the condition is false. + isFeasible = false; + const GRState *stateFalse = Assume(state, V, false, isFeasible); + if (isFeasible) { + stateFalse = BindExpr(stateFalse, Ex, MakeConstantVal(0U, Ex)); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag), + stateFalse, Pred)); + } + } + else + Dst.Add(Pred); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, + NodeTy* Pred, NodeSet& Dst, + bool asLValue) { + + Expr* Base = cast<Expr>(Ex->getBase()); + NodeSet Tmp; + Visit(Base, Pred, Tmp); + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + SVal BaseVal = GetSVal(state, Base); + SVal location = StateMgr.GetLValue(state, Ex->getDecl(), BaseVal); + + if (asLValue) + MakeNode(Dst, Ex, *I, BindExpr(state, Ex, location)); + else + EvalLoad(Dst, Ex, *I, state, location); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C fast enumeration 'for' statements. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, + NodeTy* Pred, NodeSet& 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 = getStateManager().GetLValue(GetState(Pred), ElemD); + VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); + return; + } + + NodeSet Tmp; + VisitLValue(cast<Expr>(elem), Pred, Tmp); + + for (NodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + VisitObjCForCollectionStmtAux(S, *I, Dst, GetSVal(state, elem)); + } +} + +void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, + NodeTy* Pred, NodeSet& Dst, + SVal ElementV) { + + + + // Get the current state. Use 'EvalLocation' to determine if it is a null + // pointer, etc. + Stmt* elem = S->getElement(); + + Pred = EvalLocation(elem, Pred, GetState(Pred), ElementV); + if (!Pred) + return; + + GRStateRef state = GRStateRef(GetState(Pred), getStateManager()); + + // Handle the case where the container still has elements. + QualType IntTy = getContext().IntTy; + SVal TrueV = NonLoc::MakeVal(getBasicVals(), 1, IntTy); + GRStateRef hasElems = state.BindExpr(S, TrueV); + + // Handle the case where the container has no elements. + SVal FalseV = NonLoc::MakeVal(getBasicVals(), 0, IntTy); + GRStateRef 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 = Loc::MakeVal(getStoreManager().getRegionManager().getSymbolicRegion(Sym)); + hasElems = hasElems.BindLoc(ElementV, V); + + // Bind the location to 'nil' on the false branch. + SVal nilV = loc::ConcreteInt(getBasicVals().getValue(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, NodeTy* Pred, + NodeSet& Dst){ + + VisitObjCMessageExprArgHelper(ME, ME->arg_begin(), ME->arg_end(), + Pred, Dst); +} + +void GRExprEngine::VisitObjCMessageExprArgHelper(ObjCMessageExpr* ME, + ObjCMessageExpr::arg_iterator AI, + ObjCMessageExpr::arg_iterator AE, + NodeTy* Pred, NodeSet& Dst) { + if (AI == AE) { + + // Process the receiver. + + if (Expr* Receiver = ME->getReceiver()) { + NodeSet Tmp; + Visit(Receiver, Pred, Tmp); + + for (NodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI != NE; ++NI) + VisitObjCMessageExprDispatchHelper(ME, *NI, Dst); + + return; + } + + VisitObjCMessageExprDispatchHelper(ME, Pred, Dst); + return; + } + + NodeSet Tmp; + Visit(*AI, Pred, Tmp); + + ++AI; + + for (NodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI != NE; ++NI) + VisitObjCMessageExprArgHelper(ME, AI, AE, *NI, Dst); +} + +void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, + NodeTy* Pred, + NodeSet& Dst) { + + // FIXME: More logic for the processing the method call. + + const GRState* state = GetState(Pred); + bool RaisesException = false; + + + if (Expr* Receiver = ME->getReceiver()) { + + SVal L = GetSVal(state, Receiver); + + // Check for undefined control-flow. + if (L.isUndef()) { + NodeTy* N = Builder->generateNode(ME, state, Pred); + + if (N) { + N->markAsSink(); + UndefReceivers.insert(N); + } + + return; + } + + // "Assume" that the receiver is not NULL. + bool isFeasibleNotNull = false; + const GRState *StNotNull = Assume(state, L, true, isFeasibleNotNull); + + // "Assume" that the receiver is NULL. + bool isFeasibleNull = false; + const GRState *StNull = Assume(state, L, false, isFeasibleNull); + + if (isFeasibleNull) { + QualType RetTy = ME->getType(); + + // Check if the receiver was nil and the return value a struct. + if(RetTy->isRecordType()) { + if (BR.getParentMap().isConsumedExpr(ME)) { + // The [0 ...] expressions will return garbage. Flag either an + // explicit or implicit error. Because of the structure of this + // function we currently do not bifurfacte the state graph at + // this point. + // FIXME: We should bifurcate and fill the returned struct with + // garbage. + if (NodeTy* N = Builder->generateNode(ME, StNull, Pred)) { + N->markAsSink(); + if (isFeasibleNotNull) + NilReceiverStructRetImplicit.insert(N); + else + NilReceiverStructRetExplicit.insert(N); + } + } + } + else { + ASTContext& Ctx = getContext(); + if (RetTy != Ctx.VoidTy) { + if (BR.getParentMap().isConsumedExpr(ME)) { + // sizeof(void *) + const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); + // sizeof(return type) + const uint64_t returnTypeSize = Ctx.getTypeSize(ME->getType()); + + if(voidPtrSize < returnTypeSize) { + if (NodeTy* N = Builder->generateNode(ME, StNull, Pred)) { + N->markAsSink(); + if(isFeasibleNotNull) + NilReceiverLargerThanVoidPtrRetImplicit.insert(N); + else + NilReceiverLargerThanVoidPtrRetExplicit.insert(N); + } + } + else if (!isFeasibleNotNull) { + // Handle the safe cases where the return value is 0 if the + // receiver is nil. + // + // FIXME: For now take the conservative approach that we only + // return null values if we *know* that the receiver is nil. + // This is because we can have surprises like: + // + // ... = [[NSScreens screens] objectAtIndex:0]; + // + // What can happen is that [... screens] could return nil, but + // it most likely isn't nil. We should assume the semantics + // of this case unless we have *a lot* more knowledge. + // + SVal V = ValMgr.makeZeroVal(ME->getType()); + MakeNode(Dst, ME, Pred, BindExpr(StNull, ME, V)); + return; + } + } + } + } + // We have handled the cases where the receiver is nil. The remainder + // of this method should assume that the receiver is not nil. + if (!StNotNull) + return; + + state = StNotNull; + } + + // Check if the "raise" message was sent. + if (ME->getSelector() == RaiseSel) + RaisesException = true; + } + 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 for any arguments that are uninitialized/undefined. + + for (ObjCMessageExpr::arg_iterator I = ME->arg_begin(), E = ME->arg_end(); + I != E; ++I) { + + if (GetSVal(state, *I).isUndef()) { + + // Generate an error node for passing an uninitialized/undefined value + // as an argument to a message expression. This node is a sink. + NodeTy* N = Builder->generateNode(ME, state, Pred); + + if (N) { + N->markAsSink(); + MsgExprUndefArgs[N] = *I; + } + + return; + } + } + + // Check if we raise an exception. For now treat these as sinks. Eventually + // we will want to handle exceptions properly. + + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + + unsigned size = Dst.size(); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + EvalObjCMessageExpr(Dst, ME, Pred); + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + + if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode) + MakeNode(Dst, ME, Pred, state); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Miscellaneous statements. +//===----------------------------------------------------------------------===// + +void GRExprEngine::VisitCastPointerToInteger(SVal V, const GRState* state, + QualType PtrTy, + Expr* CastE, NodeTy* Pred, + NodeSet& Dst) { + if (!V.isUnknownOrUndef()) { + // FIXME: Determine if the number of bits of the target type is + // equal or exceeds the number of bits to store the pointer value. + // If not, flag an error. + MakeNode(Dst, CastE, Pred, BindExpr(state, CastE, EvalCast(cast<Loc>(V), + CastE->getType()))); + } + else + MakeNode(Dst, CastE, Pred, BindExpr(state, CastE, V)); +} + + +void GRExprEngine::VisitCast(Expr* CastE, Expr* Ex, NodeTy* Pred, NodeSet& Dst){ + NodeSet 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()) + VisitLValue(Ex, Pred, S1); + else + Visit(Ex, Pred, S1); + + // Check for casting to "void". + if (T->isVoidType()) { + for (NodeSet::iterator I1 = S1.begin(), E1 = S1.end(); I1 != E1; ++I1) + Dst.Add(*I1); + + return; + } + + // FIXME: The rest of this should probably just go into EvalCall, and + // let the transfer function object be responsible for constructing + // nodes. + + for (NodeSet::iterator I1 = S1.begin(), E1 = S1.end(); I1 != E1; ++I1) { + NodeTy* N = *I1; + const GRState* state = GetState(N); + SVal V = GetSVal(state, Ex); + ASTContext& C = getContext(); + + // Unknown? + if (V.isUnknown()) { + Dst.Add(N); + continue; + } + + // Undefined? + if (V.isUndef()) + goto PassThrough; + + // For const casts, just propagate the value. + if (C.getCanonicalType(T).getUnqualifiedType() == + C.getCanonicalType(ExTy).getUnqualifiedType()) + goto PassThrough; + + // Check for casts from pointers to integers. + if (T->isIntegerType() && Loc::IsLocType(ExTy)) { + VisitCastPointerToInteger(V, state, ExTy, CastE, N, Dst); + continue; + } + + // Check for casts from integers to pointers. + if (Loc::IsLocType(T) && ExTy->isIntegerType()) { + if (nonloc::LocAsInteger *LV = dyn_cast<nonloc::LocAsInteger>(&V)) { + // Just unpackage the lval and return it. + V = LV->getLoc(); + MakeNode(Dst, CastE, N, BindExpr(state, CastE, V)); + continue; + } + + goto DispatchCast; + } + + // Just pass through function and block pointers. + if (ExTy->isBlockPointerType() || ExTy->isFunctionPointerType()) { + assert(Loc::IsLocType(T)); + goto PassThrough; + } + + // Check for casts from array type to another type. + if (ExTy->isArrayType()) { + // We will always decay to a pointer. + V = StateMgr.ArrayToPointer(cast<Loc>(V)); + + // Are we casting from an array to a pointer? If so just pass on + // the decayed value. + if (T->isPointerType()) + goto PassThrough; + + // Are we casting from an array to an integer? If so, cast the decayed + // pointer value to an integer. + assert(T->isIntegerType()); + QualType ElemTy = cast<ArrayType>(ExTy)->getElementType(); + QualType PointerTy = getContext().getPointerType(ElemTy); + VisitCastPointerToInteger(V, state, PointerTy, CastE, N, Dst); + continue; + } + + // Check for casts from a region to a specific type. + if (loc::MemRegionVal *RV = dyn_cast<loc::MemRegionVal>(&V)) { + // FIXME: For TypedViewRegions, we should handle the case where the + // underlying symbolic pointer is a function pointer or + // block pointer. + + // FIXME: We should handle the case where we strip off view layers to get + // to a desugared type. + + assert(Loc::IsLocType(T)); + // 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(ExTy) || ExTy->isFunctionType()); + + const MemRegion* R = RV->getRegion(); + StoreManager& StoreMgr = getStoreManager(); + + // Delegate to store manager to get the result of casting a region + // to a different type. + const StoreManager::CastResult& Res = StoreMgr.CastRegion(state, R, T); + + // Inspect the result. If the MemRegion* returned is NULL, this + // expression evaluates to UnknownVal. + R = Res.getRegion(); + if (R) { V = loc::MemRegionVal(R); } else { V = UnknownVal(); } + + // Generate the new node in the ExplodedGraph. + MakeNode(Dst, CastE, N, BindExpr(Res.getState(), CastE, V)); + continue; + } + // All other cases. + DispatchCast: { + MakeNode(Dst, CastE, N, BindExpr(state, CastE, + EvalCast(V, CastE->getType()))); + continue; + } + + PassThrough: { + MakeNode(Dst, CastE, N, BindExpr(state, CastE, V)); + } + } +} + +void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, + NodeTy* Pred, NodeSet& Dst, + bool asLValue) { + InitListExpr* ILE = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); + NodeSet Tmp; + Visit(ILE, Pred, Tmp); + + for (NodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { + const GRState* state = GetState(*I); + SVal ILV = GetSVal(state, ILE); + state = StateMgr.BindCompoundLiteral(state, CL, ILV); + + if (asLValue) + MakeNode(Dst, CL, *I, BindExpr(state, CL, StateMgr.GetLValue(state, CL))); + else + MakeNode(Dst, CL, *I, BindExpr(state, CL, ILV)); + } +} + +void GRExprEngine::VisitDeclStmt(DeclStmt* DS, NodeTy* Pred, NodeSet& 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. + NodeSet Tmp; + + if (InitEx) + Visit(InitEx, Pred, Tmp); + + if (Tmp.empty()) + Tmp.Add(Pred); + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + unsigned Count = Builder->getCurrentBlockCount(); + + // Check if 'VD' is a VLA and if so check if has a non-zero size. + QualType T = getContext().getCanonicalType(VD->getType()); + if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) { + // FIXME: Handle multi-dimensional VLAs. + + Expr* SE = VLA->getSizeExpr(); + SVal Size = GetSVal(state, SE); + + if (Size.isUndef()) { + if (NodeTy* N = Builder->generateNode(DS, state, Pred)) { + N->markAsSink(); + ExplicitBadSizedVLA.insert(N); + } + continue; + } + + bool isFeasibleZero = false; + const GRState* ZeroSt = Assume(state, Size, false, isFeasibleZero); + + bool isFeasibleNotZero = false; + state = Assume(state, Size, true, isFeasibleNotZero); + + if (isFeasibleZero) { + if (NodeTy* N = Builder->generateNode(DS, ZeroSt, Pred)) { + N->markAsSink(); + if (isFeasibleNotZero) ImplicitBadSizedVLA.insert(N); + else ExplicitBadSizedVLA.insert(N); + } + } + + if (!isFeasibleNotZero) + continue; + } + + // Decls without InitExpr are not initialized explicitly. + if (InitEx) { + SVal InitVal = GetSVal(state, InitEx); + QualType T = VD->getType(); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) { + InitVal = ValMgr.getConjuredSymbolVal(InitEx, Count); + } + + state = StateMgr.BindDecl(state, VD, InitVal); + + // 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, state, DS,true); + getTF().EvalBind(BuilderRef, loc::MemRegionVal(StateMgr.getRegion(VD)), + InitVal); + } + else { + state = StateMgr.BindDeclWithNoInit(state, VD); + MakeNode(Dst, DS, *I, state); + } + } +} + +namespace { + // This class is used by VisitInitListExpr as an item in a worklist + // for processing the values contained in an InitListExpr. +class VISIBILITY_HIDDEN InitListWLItem { +public: + llvm::ImmutableList<SVal> Vals; + GRExprEngine::NodeTy* N; + InitListExpr::reverse_iterator Itr; + + InitListWLItem(GRExprEngine::NodeTy* n, llvm::ImmutableList<SVal> vals, + InitListExpr::reverse_iterator itr) + : Vals(vals), N(n), Itr(itr) {} +}; +} + + +void GRExprEngine::VisitInitListExpr(InitListExpr* E, NodeTy* Pred, + NodeSet& Dst) { + + const GRState* state = GetState(Pred); + QualType T = getContext().getCanonicalType(E->getType()); + unsigned NumInitElements = E->getNumInits(); + + if (T->isArrayType() || T->isStructureType()) { + + 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 = NonLoc::MakeCompoundVal(T, StartVals, getBasicVals()); + MakeNode(Dst, E, Pred, BindExpr(state, 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(); + + // Process the worklist until it is empty. + while (!WorkList.empty()) { + InitListWLItem X = WorkList.back(); + WorkList.pop_back(); + + NodeSet Tmp; + Visit(*X.Itr, X.N, Tmp); + + InitListExpr::reverse_iterator NewItr = X.Itr + 1; + + for (NodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + // Get the last initializer value. + state = GetState(*NI); + SVal InitV = GetSVal(state, 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 = NonLoc::MakeCompoundVal(T, NewVals, getBasicVals()); + + // Make final state and node. + MakeNode(Dst, E, *NI, BindExpr(state, E, V)); + } + else { + // Still some initializer values to go. Push them onto the worklist. + WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); + } + } + } + + return; + } + + if (T->isUnionType() || T->isVectorType()) { + // FIXME: to be implemented. + // Note: That vectors can return true for T->isIntegerType() + MakeNode(Dst, E, Pred, state); + return; + } + + if (Loc::IsLocType(T) || T->isIntegerType()) { + assert (E->getNumInits() == 1); + NodeSet Tmp; + Expr* Init = E->getInit(0); + Visit(Init, Pred, Tmp); + for (NodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I != EI; ++I) { + state = GetState(*I); + MakeNode(Dst, E, *I, BindExpr(state, E, GetSVal(state, Init))); + } + return; + } + + + printf("InitListExpr type = %s\n", T.getAsString().c_str()); + assert(0 && "unprocessed InitListExpr type"); +} + +/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). +void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, + NodeTy* Pred, + NodeSet& Dst) { + QualType T = Ex->getTypeOfArgument(); + uint64_t amt; + + if (Ex->isSizeOf()) { + if (T == getContext().VoidTy) { + // sizeof(void) == 1 byte. + amt = 1; + } + 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().getTypeSize(T) / 8; + } + } + else // Get alignment of the type. + amt = getContext().getTypeAlign(T) / 8; + + MakeNode(Dst, Ex, Pred, + BindExpr(GetState(Pred), Ex, + NonLoc::MakeVal(getBasicVals(), amt, Ex->getType()))); +} + + +void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, NodeTy* Pred, + NodeSet& Dst, bool asLValue) { + + switch (U->getOpcode()) { + + default: + break; + + case UnaryOperator::Deref: { + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + NodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + const GRState* state = GetState(*I); + SVal location = GetSVal(state, Ex); + + if (asLValue) + MakeNode(Dst, U, *I, BindExpr(state, U, location), + ProgramPoint::PostLValueKind); + else + EvalLoad(Dst, U, *I, state, location); + } + + return; + } + + case UnaryOperator::Real: { + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + NodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (NodeSet::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, BindExpr(state, U, GetSVal(state, Ex))); + } + + return; + } + + case UnaryOperator::Imag: { + + Expr* Ex = U->getSubExpr()->IgnoreParens(); + NodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (NodeSet::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 = NonLoc::MakeVal(getBasicVals(), 0, Ex->getType()); + MakeNode(Dst, U, *I, BindExpr(state, U, X)); + } + + return; + } + + // FIXME: Just report "Unknown" for OffsetOf. + case UnaryOperator::OffsetOf: + 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(); + NodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, BindExpr(state, U, GetSVal(state, Ex))); + } + + return; + } + + case UnaryOperator::AddrOf: { + + assert(!asLValue); + Expr* Ex = U->getSubExpr()->IgnoreParens(); + NodeSet Tmp; + VisitLValue(Ex, Pred, Tmp); + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + SVal V = GetSVal(state, Ex); + state = BindExpr(state, U, V); + MakeNode(Dst, U, *I, state); + } + + return; + } + + case UnaryOperator::LNot: + case UnaryOperator::Minus: + case UnaryOperator::Not: { + + assert (!asLValue); + Expr* Ex = U->getSubExpr()->IgnoreParens(); + NodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (NodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + + // Get the value of the subexpression. + SVal V = GetSVal(state, Ex); + + if (V.isUnknownOrUndef()) { + MakeNode(Dst, U, *I, BindExpr(state, 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 = BindExpr(state, U, EvalComplement(cast<NonLoc>(V))); + break; + + case UnaryOperator::Minus: + // FIXME: Do we need to handle promotions? + state = BindExpr(state, U, EvalMinus(U, 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". + + if (isa<Loc>(V)) { + Loc X = Loc::MakeNull(getBasicVals()); + SVal Result = EvalBinOp(state,BinaryOperator::EQ, cast<Loc>(V), X, + U->getType()); + state = BindExpr(state, U, Result); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); +#if 0 + SVal Result = EvalBinOp(BinaryOperator::EQ, cast<NonLoc>(V), X); + state = SetSVal(state, U, Result); +#else + EvalBinOp(Dst, U, BinaryOperator::EQ, cast<NonLoc>(V), X, *I, + U->getType()); + continue; +#endif + } + + break; + } + + MakeNode(Dst, U, *I, state); + } + + return; + } + } + + // Handle ++ and -- (both pre- and post-increment). + + assert (U->isIncrementDecrementOp()); + NodeSet Tmp; + Expr* Ex = U->getSubExpr()->IgnoreParens(); + VisitLValue(Ex, Pred, Tmp); + + for (NodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + + const GRState* state = GetState(*I); + SVal V1 = GetSVal(state, Ex); + + // Perform a load. + NodeSet Tmp2; + EvalLoad(Tmp2, Ex, *I, state, V1); + + for (NodeSet::iterator I2 = Tmp2.begin(), E2 = Tmp2.end(); I2!=E2; ++I2) { + + state = GetState(*I2); + SVal V2 = GetSVal(state, Ex); + + // Propagate unknown and undefined values. + if (V2.isUnknownOrUndef()) { + MakeNode(Dst, U, *I2, BindExpr(state, U, V2)); + continue; + } + + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BinaryOperator::Add + : BinaryOperator::Sub; + + SVal Result = EvalBinOp(state, Op, V2, MakeConstantVal(1U, U), + U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ + Result = ValMgr.getConjuredSymbolVal(Ex, + Builder->getCurrentBlockCount()); + + // 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())) { + SVal Constraint = EvalBinOp(state, BinaryOperator::EQ, V2, + ValMgr.makeZeroVal(U->getType()), + getContext().IntTy); + + bool isFeasible = false; + Assume(state, Constraint, true, isFeasible); + if (!isFeasible) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = EvalBinOp(state, BinaryOperator::EQ, Result, + ValMgr.makeZeroVal(U->getType()), + getContext().IntTy); + + bool isFeasible = false; + state = Assume(state, Constraint, false, isFeasible); + assert(isFeasible && state); + } + } + } + + state = BindExpr(state, U, U->isPostfix() ? V2 : Result); + + // Perform the store. + EvalStore(Dst, U, *I2, state, V1, Result); + } + } +} + +void GRExprEngine::VisitAsmStmt(AsmStmt* A, NodeTy* Pred, NodeSet& Dst) { + VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); +} + +void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A, + AsmStmt::outputs_iterator I, + AsmStmt::outputs_iterator E, + NodeTy* Pred, NodeSet& Dst) { + if (I == E) { + VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); + return; + } + + NodeSet Tmp; + VisitLValue(*I, Pred, Tmp); + + ++I; + + for (NodeSet::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, + NodeTy* Pred, NodeSet& 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 = GetSVal(state, *OI); + assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. + + if (isa<Loc>(X)) + state = BindLoc(state, cast<Loc>(X), UnknownVal()); + } + + MakeNode(Dst, A, Pred, state); + return; + } + + NodeSet Tmp; + Visit(*I, Pred, Tmp); + + ++I; + + for (NodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI != NE; ++NI) + VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); +} + +void GRExprEngine::EvalReturn(NodeSet& Dst, ReturnStmt* S, NodeTy* Pred) { + assert (Builder && "GRStmtNodeBuilder must be defined."); + + unsigned size = Dst.size(); + + SaveAndRestore<bool> OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + getTF().EvalReturn(Dst, *this, *Builder, S, Pred); + + // Handle the case where no nodes where generated. + + if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode) + MakeNode(Dst, S, Pred, GetState(Pred)); +} + +void GRExprEngine::VisitReturnStmt(ReturnStmt* S, NodeTy* Pred, NodeSet& Dst) { + + Expr* R = S->getRetValue(); + + if (!R) { + EvalReturn(Dst, S, Pred); + return; + } + + NodeSet Tmp; + Visit(R, Pred, Tmp); + + for (NodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { + SVal X = GetSVal((*I)->getState(), R); + + // Check if we return the address of a stack variable. + if (isa<loc::MemRegionVal>(X)) { + // Determine if the value is on the stack. + const MemRegion* R = cast<loc::MemRegionVal>(&X)->getRegion(); + + if (R && getStateManager().hasStackStorage(R)) { + // Create a special node representing the error. + if (NodeTy* N = Builder->generateNode(S, GetState(*I), *I)) { + N->markAsSink(); + RetsStackAddr.insert(N); + } + continue; + } + } + // Check if we return an undefined value. + else if (X.isUndef()) { + if (NodeTy* N = Builder->generateNode(S, GetState(*I), *I)) { + N->markAsSink(); + RetsUndef.insert(N); + } + continue; + } + + EvalReturn(Dst, S, *I); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Binary operators. +//===----------------------------------------------------------------------===// + +const GRState* GRExprEngine::CheckDivideZero(Expr* Ex, const GRState* state, + NodeTy* Pred, SVal Denom) { + + // Divide by undefined? (potentially zero) + + if (Denom.isUndef()) { + NodeTy* DivUndef = Builder->generateNode(Ex, state, Pred); + + if (DivUndef) { + DivUndef->markAsSink(); + ExplicitBadDivides.insert(DivUndef); + } + + return 0; + } + + // Check for divide/remainder-by-zero. + // First, "assume" that the denominator is 0 or undefined. + + bool isFeasibleZero = false; + const GRState* ZeroSt = Assume(state, Denom, false, isFeasibleZero); + + // Second, "assume" that the denominator cannot be 0. + + bool isFeasibleNotZero = false; + state = Assume(state, Denom, true, isFeasibleNotZero); + + // Create the node for the divide-by-zero (if it occurred). + + if (isFeasibleZero) + if (NodeTy* DivZeroNode = Builder->generateNode(Ex, ZeroSt, Pred)) { + DivZeroNode->markAsSink(); + + if (isFeasibleNotZero) + ImplicitBadDivides.insert(DivZeroNode); + else + ExplicitBadDivides.insert(DivZeroNode); + + } + + return isFeasibleNotZero ? state : 0; +} + +void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, + GRExprEngine::NodeTy* Pred, + GRExprEngine::NodeSet& Dst) { + + NodeSet Tmp1; + Expr* LHS = B->getLHS()->IgnoreParens(); + Expr* RHS = B->getRHS()->IgnoreParens(); + + // FIXME: Add proper support for ObjCKVCRefExpr. + if (isa<ObjCKVCRefExpr>(LHS)) { + Visit(RHS, Pred, Dst); + return; + } + + if (B->isAssignmentOp()) + VisitLValue(LHS, Pred, Tmp1); + else + Visit(LHS, Pred, Tmp1); + + for (NodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1 != E1; ++I1) { + + SVal LeftV = GetSVal((*I1)->getState(), LHS); + + // Process the RHS. + + NodeSet Tmp2; + Visit(RHS, *I1, Tmp2); + + // With both the LHS and RHS evaluated, process the operation itself. + + for (NodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end(); I2 != E2; ++I2) { + + const GRState* state = GetState(*I2); + const GRState* OldSt = state; + + SVal RightV = GetSVal(state, RHS); + BinaryOperator::Opcode Op = B->getOpcode(); + + switch (Op) { + + case 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(B->getRHS(), Count); + } + + // Simulate the effects of a "store": bind the value of the RHS + // to the L-Value represented by the LHS. + EvalStore(Dst, B, LHS, *I2, BindExpr(state, B, RightV), LeftV, + RightV); + continue; + } + + case BinaryOperator::Div: + case BinaryOperator::Rem: + + // Special checking for integer denominators. + if (RHS->getType()->isIntegerType() && + RHS->getType()->isScalarType()) { + + state = CheckDivideZero(B, state, *I2, RightV); + if (!state) continue; + } + + // FALL-THROUGH. + + default: { + + if (B->isAssignmentOp()) + break; + + // Process non-assignements 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(Dst, B, *I2, state); + } + else + Dst.Add(*I2); + + continue; + } + + if (Result.isUndef() && !LeftV.isUndef() && !RightV.isUndef()) { + + // The operands were *not* undefined, but the result is undefined. + // This is a special node that should be flagged as an error. + + if (NodeTy* UndefNode = Builder->generateNode(B, state, *I2)) { + UndefNode->markAsSink(); + UndefResults.insert(UndefNode); + } + + continue; + } + + // Otherwise, create a new node. + + MakeNode(Dst, B, *I2, BindExpr(state, B, Result)); + 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. + NodeSet Tmp3; + SVal location = GetSVal(state, LHS); + EvalLoad(Tmp3, LHS, *I2, state, location); + + for (NodeSet::iterator I3=Tmp3.begin(), E3=Tmp3.end(); I3!=E3; ++I3) { + + state = GetState(*I3); + SVal V = GetSVal(state, LHS); + + // Check for divide-by-zero. + if ((Op == BinaryOperator::Div || Op == BinaryOperator::Rem) + && RHS->getType()->isIntegerType() + && RHS->getType()->isScalarType()) { + + // CheckDivideZero returns a new state where the denominator + // is assumed to be non-zero. + state = CheckDivideZero(B, state, *I3, RightV); + + if (!state) + continue; + } + + // Propagate undefined values (left-side). + if (V.isUndef()) { + EvalStore(Dst, B, LHS, *I3, BindExpr(state, B, V), location, V); + continue; + } + + // Propagate unknown values (left and right-side). + if (RightV.isUnknown() || V.isUnknown()) { + EvalStore(Dst, B, LHS, *I3, BindExpr(state, B, UnknownVal()), + location, UnknownVal()); + continue; + } + + // At this point: + // + // The LHS is not Undef/Unknown. + // The RHS is not Unknown. + + // Get the computation type. + QualType CTy = cast<CompoundAssignOperator>(B)->getComputationResultType(); + CTy = getContext().getCanonicalType(CTy); + + QualType CLHSTy = cast<CompoundAssignOperator>(B)->getComputationLHSType(); + CLHSTy = getContext().getCanonicalType(CTy); + + QualType LTy = getContext().getCanonicalType(LHS->getType()); + QualType RTy = getContext().getCanonicalType(RHS->getType()); + + // Promote LHS. + V = EvalCast(V, CLHSTy); + + // Evaluate operands and promote to result type. + if (RightV.isUndef()) { + // Propagate undefined values (right-side). + EvalStore(Dst, B, LHS, *I3, BindExpr(state, B, RightV), location, + RightV); + continue; + } + + // Compute the result of the operation. + SVal Result = EvalCast(EvalBinOp(state, Op, V, RightV, CTy), + B->getType()); + + if (Result.isUndef()) { + // The operands were not undefined, but the result is undefined. + if (NodeTy* UndefNode = Builder->generateNode(B, state, *I3)) { + UndefNode->markAsSink(); + UndefResults.insert(UndefNode); + } + continue; + } + + // 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(B->getRHS(), LTy, Count); + + // However, we need to convert the symbol to the computation type. + Result = (LTy == CTy) ? LHSVal : EvalCast(LHSVal,CTy); + } + else { + // The left-hand side may bind to a different value then the + // computation type. + LHSVal = (LTy == CTy) ? Result : EvalCast(Result,LTy); + } + + EvalStore(Dst, B, LHS, *I3, BindExpr(state, B, Result), location, + LHSVal); + } + } + } +} + +//===----------------------------------------------------------------------===// +// Transfer-function Helpers. +//===----------------------------------------------------------------------===// + +void GRExprEngine::EvalBinOp(ExplodedNodeSet<GRState>& Dst, Expr* Ex, + BinaryOperator::Opcode Op, + NonLoc L, NonLoc R, + ExplodedNode<GRState>* Pred, QualType T) { + + GRStateSet OStates; + EvalBinOp(OStates, GetState(Pred), Ex, Op, L, R, T); + + for (GRStateSet::iterator I=OStates.begin(), E=OStates.end(); I!=E; ++I) + MakeNode(Dst, Ex, Pred, *I); +} + +void GRExprEngine::EvalBinOp(GRStateSet& OStates, const GRState* state, + Expr* Ex, BinaryOperator::Opcode Op, + NonLoc L, NonLoc R, QualType T) { + + GRStateSet::AutoPopulate AP(OStates, state); + if (R.isValid()) getTF().EvalBinOpNN(OStates, *this, state, Ex, Op, L, R, T); +} + +SVal GRExprEngine::EvalBinOp(const GRState* state, 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 getTF().EvalBinOp(*this, Op, cast<Loc>(L), cast<Loc>(R)); + else + return getTF().EvalBinOp(*this, state, Op, cast<Loc>(L), cast<NonLoc>(R)); + } + + 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 getTF().EvalBinOp(*this, state, Op, cast<Loc>(R), cast<NonLoc>(L)); + } + else + return getTF().DetermEvalBinOpNN(*this, Op, cast<NonLoc>(L), + cast<NonLoc>(R), T); +} + +//===----------------------------------------------------------------------===// +// Visualization. +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG +static GRExprEngine* GraphPrintCheckerState; +static SourceManager* GraphPrintSourceManager; + +namespace llvm { +template<> +struct VISIBILITY_HIDDEN DOTGraphTraits<GRExprEngine::NodeTy*> : + public DefaultDOTGraphTraits { + + static std::string getNodeAttributes(const GRExprEngine::NodeTy* N, void*) { + + if (GraphPrintCheckerState->isImplicitNullDeref(N) || + GraphPrintCheckerState->isExplicitNullDeref(N) || + GraphPrintCheckerState->isUndefDeref(N) || + GraphPrintCheckerState->isUndefStore(N) || + GraphPrintCheckerState->isUndefControlFlow(N) || + GraphPrintCheckerState->isExplicitBadDivide(N) || + GraphPrintCheckerState->isImplicitBadDivide(N) || + GraphPrintCheckerState->isUndefResult(N) || + GraphPrintCheckerState->isBadCall(N) || + GraphPrintCheckerState->isUndefArg(N)) + return "color=\"red\",style=\"filled\""; + + if (GraphPrintCheckerState->isNoReturnCall(N)) + return "color=\"blue\",style=\"filled\""; + + return ""; + } + + static std::string getNodeLabel(const GRExprEngine::NodeTy* N, void*) { + std::ostringstream Out; + + // 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 (isa<PostStmt>(Loc)) { + const PostStmt& L = cast<PostStmt>(Loc); + Stmt* S = L.getStmt(); + SourceLocation SLoc = S->getLocStart(); + + Out << S->getStmtClassName() << ' ' << (void*) S << ' '; + llvm::raw_os_ostream OutS(Out); + S->printPretty(OutS); + OutS.flush(); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) + << "\\l"; + } + + if (isa<PostLoad>(Loc)) + Out << "\\lPostLoad\\l;"; + else if (isa<PostStore>(Loc)) + Out << "\\lPostStore\\l"; + else if (isa<PostLValue>(Loc)) + Out << "\\lPostLValue\\l"; + else if (isa<PostLocationChecksSucceed>(Loc)) + Out << "\\lPostLocationChecksSucceed\\l"; + else if (isa<PostNullCheckFailed>(Loc)) + Out << "\\lPostNullCheckFailed\\l"; + + 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->isExplicitBadDivide(N)) + Out << "\\|Explicit divide-by zero or undefined value."; + else if (GraphPrintCheckerState->isImplicitBadDivide(N)) + Out << "\\|Implicit divide-by zero or undefined value."; + 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"; + + 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: "; + + llvm::raw_os_ostream OutS(Out); + E.getSrc()->printTerminator(OutS); + OutS.flush(); + + 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 "; + llvm::raw_os_ostream OutS(Out); + C->getLHS()->printPretty(OutS); + OutS.flush(); + + if (Stmt* RHS = C->getRHS()) { + Out << " .. "; + RHS->printPretty(OutS); + OutS.flush(); + } + + 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 (GraphPrintCheckerState->isUndefControlFlow(N)) { + Out << "\\|Control-flow based on\\lUndefined value.\\l"; + } + } + } + + Out << "\\|StateID: " << (void*) N->getState() << "\\|"; + + GRStateRef state(N->getState(), GraphPrintCheckerState->getStateManager()); + state.printDOT(Out); + + Out << "\\l"; + return Out.str(); + } +}; +} // end llvm namespace +#endif + +#ifndef NDEBUG +template <typename ITERATOR> +GRExprEngine::NodeTy* GetGraphNode(ITERATOR I) { return *I; } + +template <> +GRExprEngine::NodeTy* +GetGraphNode<llvm::DenseMap<GRExprEngine::NodeTy*, Expr*>::iterator> + (llvm::DenseMap<GRExprEngine::NodeTy*, Expr*>::iterator I) { + return I->first; +} +#endif + +void GRExprEngine::ViewGraph(bool trim) { +#ifndef NDEBUG + if (trim) { + std::vector<NodeTy*> 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(); + NodeTy *N = const_cast<NodeTy*>(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(NodeTy** Beg, NodeTy** End) { +#ifndef NDEBUG + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + std::auto_ptr<GRExprEngine::GraphTy> TrimmedG(G.Trim(Beg, End).first); + + if (!TrimmedG.get()) + llvm::cerr << "warning: Trimmed ExplodedGraph is empty.\n"; + else + llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedGRExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; +#endif +} diff --git a/lib/Analysis/GRExprEngineInternalChecks.cpp b/lib/Analysis/GRExprEngineInternalChecks.cpp new file mode 100644 index 0000000..9aea124 --- /dev/null +++ b/lib/Analysis/GRExprEngineInternalChecks.cpp @@ -0,0 +1,961 @@ +//=-- GRExprEngineInternalChecks.cpp - Builtin GRExprEngine Checks---*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the BugType classes used by GRExprEngine to report +// bugs derived from builtin checks in the path-sensitive engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +template <typename ITERATOR> inline +ExplodedNode<GRState>* GetNode(ITERATOR I) { + return *I; +} + +template <> inline +ExplodedNode<GRState>* GetNode(GRExprEngine::undef_arg_iterator I) { + return I->first; +} + +//===----------------------------------------------------------------------===// +// Forward declarations for bug reporter visitors. +//===----------------------------------------------------------------------===// + +static const Stmt *GetDerefExpr(const ExplodedNode<GRState> *N); +static const Stmt *GetReceiverExpr(const ExplodedNode<GRState> *N); +static const Stmt *GetDenomExpr(const ExplodedNode<GRState> *N); +static const Stmt *GetCalleeExpr(const ExplodedNode<GRState> *N); +static const Stmt *GetRetValExpr(const ExplodedNode<GRState> *N); + +static void registerTrackNullOrUndefValue(BugReporterContext& BRC, + const Stmt *ValExpr, + const ExplodedNode<GRState>* N); + +//===----------------------------------------------------------------------===// +// Bug Descriptions. +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN BuiltinBugReport : public RangedBugReport { +public: + BuiltinBugReport(BugType& bt, const char* desc, + ExplodedNode<GRState> *n) + : RangedBugReport(bt, desc, n) {} + + BuiltinBugReport(BugType& bt, const char *shortDesc, const char *desc, + ExplodedNode<GRState> *n) + : RangedBugReport(bt, shortDesc, desc, n) {} + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N); +}; + +class VISIBILITY_HIDDEN BuiltinBug : public BugType { + GRExprEngine &Eng; +protected: + const std::string desc; +public: + BuiltinBug(GRExprEngine *eng, const char* n, const char* d) + : BugType(n, "Logic errors"), Eng(*eng), desc(d) {} + + BuiltinBug(GRExprEngine *eng, const char* n) + : BugType(n, "Logic errors"), Eng(*eng), desc(n) {} + + virtual void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) = 0; + + void FlushReports(BugReporter& BR) { FlushReportsImpl(BR, Eng); } + + virtual void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) {} + + template <typename ITER> void Emit(BugReporter& BR, ITER I, ITER E); +}; + + +template <typename ITER> +void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) { + for (; I != E; ++I) BR.EmitReport(new BuiltinBugReport(*this, desc.c_str(), + GetNode(I))); +} + +void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N) { + static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this); +} + +class VISIBILITY_HIDDEN NullDeref : public BuiltinBug { +public: + NullDeref(GRExprEngine* eng) + : BuiltinBug(eng,"Null dereference", "Dereference of null pointer") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.null_derefs_begin(), Eng.null_derefs_end()); + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug { +public: + NilReceiverStructRet(GRExprEngine* eng) : + BuiltinBug(eng, "'nil' receiver with struct return type") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::nil_receiver_struct_ret_iterator + I=Eng.nil_receiver_struct_ret_begin(), + E=Eng.nil_receiver_struct_ret_end(); I!=E; ++I) { + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PostStmt P = cast<PostStmt>((*I)->getLocation()); + ObjCMessageExpr *ME = cast<ObjCMessageExpr>(P.getStmt()); + os << "The receiver in the message expression is 'nil' and results in the" + " returned value (of type '" + << ME->getType().getAsString() + << "') to be garbage or otherwise undefined."; + + BuiltinBugReport *R = new BuiltinBugReport(*this, os.str().c_str(), *I); + R->addRange(ME->getReceiver()->getSourceRange()); + BR.EmitReport(R); + } + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN NilReceiverLargerThanVoidPtrRet : public BuiltinBug { +public: + NilReceiverLargerThanVoidPtrRet(GRExprEngine* eng) : + BuiltinBug(eng, + "'nil' receiver with return type larger than sizeof(void *)") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::nil_receiver_larger_than_voidptr_ret_iterator + I=Eng.nil_receiver_larger_than_voidptr_ret_begin(), + E=Eng.nil_receiver_larger_than_voidptr_ret_end(); I!=E; ++I) { + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PostStmt P = cast<PostStmt>((*I)->getLocation()); + ObjCMessageExpr *ME = cast<ObjCMessageExpr>(P.getStmt()); + os << "The receiver in the message expression is 'nil' and results in the" + " returned value (of type '" + << ME->getType().getAsString() + << "' and of size " + << Eng.getContext().getTypeSize(ME->getType()) / 8 + << " bytes) to be garbage or otherwise undefined."; + + BuiltinBugReport *R = new BuiltinBugReport(*this, os.str().c_str(), *I); + R->addRange(ME->getReceiver()->getSourceRange()); + BR.EmitReport(R); + } + } + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug { +public: + UndefinedDeref(GRExprEngine* eng) + : BuiltinBug(eng,"Dereference of undefined pointer value") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.undef_derefs_begin(), Eng.undef_derefs_end()); + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN DivZero : public BuiltinBug { +public: + DivZero(GRExprEngine* eng) + : BuiltinBug(eng,"Division-by-zero", + "Division by zero or undefined value.") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.explicit_bad_divides_begin(), Eng.explicit_bad_divides_end()); + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetDenomExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN UndefResult : public BuiltinBug { +public: + UndefResult(GRExprEngine* eng) : BuiltinBug(eng,"Undefined result", + "Result of operation is undefined.") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.undef_results_begin(), Eng.undef_results_end()); + } +}; + +class VISIBILITY_HIDDEN BadCall : public BuiltinBug { +public: + BadCall(GRExprEngine *eng) + : BuiltinBug(eng, "Invalid function call", + "Called function pointer is a null or undefined pointer value") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.bad_calls_begin(), Eng.bad_calls_end()); + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetCalleeExpr(N), N); + } +}; + + +class VISIBILITY_HIDDEN ArgReport : public BuiltinBugReport { + const Stmt *Arg; +public: + ArgReport(BugType& bt, const char* desc, ExplodedNode<GRState> *n, + const Stmt *arg) + : BuiltinBugReport(bt, desc, n), Arg(arg) {} + + ArgReport(BugType& bt, const char *shortDesc, const char *desc, + ExplodedNode<GRState> *n, const Stmt *arg) + : BuiltinBugReport(bt, shortDesc, desc, n), Arg(arg) {} + + const Stmt *getArg() const { return Arg; } +}; + +class VISIBILITY_HIDDEN BadArg : public BuiltinBug { +public: + BadArg(GRExprEngine* eng) : BuiltinBug(eng,"Uninitialized argument", + "Pass-by-value argument in function call is undefined.") {} + + BadArg(GRExprEngine* eng, const char* d) + : BuiltinBug(eng,"Uninitialized argument", d) {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::UndefArgsTy::iterator I = Eng.undef_arg_begin(), + E = Eng.undef_arg_end(); I!=E; ++I) { + // Generate a report for this bug. + ArgReport *report = new ArgReport(*this, desc.c_str(), I->first, + I->second); + report->addRange(I->second->getSourceRange()); + BR.EmitReport(report); + } + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(R)->getArg(), + N); + } +}; + +class VISIBILITY_HIDDEN BadMsgExprArg : public BadArg { +public: + BadMsgExprArg(GRExprEngine* eng) + : BadArg(eng,"Pass-by-value argument in message expression is undefined"){} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::UndefArgsTy::iterator I=Eng.msg_expr_undef_arg_begin(), + E = Eng.msg_expr_undef_arg_end(); I!=E; ++I) { + // Generate a report for this bug. + ArgReport *report = new ArgReport(*this, desc.c_str(), I->first, + I->second); + report->addRange(I->second->getSourceRange()); + BR.EmitReport(report); + } + } +}; + +class VISIBILITY_HIDDEN BadReceiver : public BuiltinBug { +public: + BadReceiver(GRExprEngine* eng) + : BuiltinBug(eng,"Uninitialized receiver", + "Receiver in message expression is an uninitialized value") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::ErrorNodes::iterator I=Eng.undef_receivers_begin(), + End = Eng.undef_receivers_end(); I!=End; ++I) { + + // Generate a report for this bug. + BuiltinBugReport *report = new BuiltinBugReport(*this, desc.c_str(), *I); + ExplodedNode<GRState>* N = *I; + Stmt *S = cast<PostStmt>(N->getLocation()).getStmt(); + Expr* E = cast<ObjCMessageExpr>(S)->getReceiver(); + assert (E && "Receiver cannot be NULL"); + report->addRange(E->getSourceRange()); + BR.EmitReport(report); + } + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetReceiverExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN RetStack : public BuiltinBug { +public: + RetStack(GRExprEngine* eng) + : BuiltinBug(eng, "Return of address to stack-allocated memory") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::ret_stackaddr_iterator I=Eng.ret_stackaddr_begin(), + End = Eng.ret_stackaddr_end(); I!=End; ++I) { + + ExplodedNode<GRState>* N = *I; + Stmt *S = cast<PostStmt>(N->getLocation()).getStmt(); + Expr* E = cast<ReturnStmt>(S)->getRetValue(); + assert (E && "Return expression cannot be NULL"); + + // Get the value associated with E. + loc::MemRegionVal V = + cast<loc::MemRegionVal>(Eng.getStateManager().GetSVal(N->getState(), + E)); + + // Generate a report for this bug. + std::string buf; + llvm::raw_string_ostream os(buf); + SourceRange R; + + // Check if the region is a compound literal. + if (const CompoundLiteralRegion* CR = + dyn_cast<CompoundLiteralRegion>(V.getRegion())) { + + const CompoundLiteralExpr* CL = CR->getLiteralExpr(); + os << "Address of stack memory associated with a compound literal " + "declared on line " + << BR.getSourceManager() + .getInstantiationLineNumber(CL->getLocStart()) + << " returned."; + + R = CL->getSourceRange(); + } + else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(V.getRegion())) { + const Expr* ARE = AR->getExpr(); + SourceLocation L = ARE->getLocStart(); + R = ARE->getSourceRange(); + + os << "Address of stack memory allocated by call to alloca() on line " + << BR.getSourceManager().getInstantiationLineNumber(L) + << " returned."; + } + else { + os << "Address of stack memory associated with local variable '" + << V.getRegion()->getString() << "' returned."; + } + + RangedBugReport *report = new RangedBugReport(*this, os.str().c_str(), N); + report->addRange(E->getSourceRange()); + if (R.isValid()) report->addRange(R); + BR.EmitReport(report); + } + } +}; + +class VISIBILITY_HIDDEN RetUndef : public BuiltinBug { +public: + RetUndef(GRExprEngine* eng) : BuiltinBug(eng, "Uninitialized return value", + "Uninitialized or undefined value returned to caller.") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.ret_undef_begin(), Eng.ret_undef_end()); + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, GetRetValExpr(N), N); + } +}; + +class VISIBILITY_HIDDEN UndefBranch : public BuiltinBug { + struct VISIBILITY_HIDDEN FindUndefExpr { + GRStateManager& VM; + const GRState* St; + + FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} + + Expr* FindExpr(Expr* Ex) { + if (!MatchesCriteria(Ex)) + return 0; + + for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) + if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) { + Expr* E2 = FindExpr(ExI); + if (E2) return E2; + } + + return Ex; + } + + bool MatchesCriteria(Expr* Ex) { return VM.GetSVal(St, Ex).isUndef(); } + }; + +public: + UndefBranch(GRExprEngine *eng) + : BuiltinBug(eng,"Use of uninitialized value", + "Branch condition evaluates to an uninitialized value.") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::undef_branch_iterator I=Eng.undef_branches_begin(), + E=Eng.undef_branches_end(); I!=E; ++I) { + + // What's going on here: we want to highlight the subexpression of the + // condition that is the most likely source of the "uninitialized + // branch condition." We do a recursive walk of the condition's + // subexpressions and roughly look for the most nested subexpression + // that binds to Undefined. We then highlight that expression's range. + BlockEdge B = cast<BlockEdge>((*I)->getLocation()); + Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); + assert (Ex && "Block must have a terminator."); + + // Get the predecessor node and check if is a PostStmt with the Stmt + // being the terminator condition. We want to inspect the state + // of that node instead because it will contain main information about + // the subexpressions. + assert (!(*I)->pred_empty()); + + // Note: any predecessor will do. They should have identical state, + // since all the BlockEdge did was act as an error sink since the value + // had to already be undefined. + ExplodedNode<GRState> *N = *(*I)->pred_begin(); + ProgramPoint P = N->getLocation(); + const GRState* St = (*I)->getState(); + + if (PostStmt* PS = dyn_cast<PostStmt>(&P)) + if (PS->getStmt() == Ex) + St = N->getState(); + + FindUndefExpr FindIt(Eng.getStateManager(), St); + Ex = FindIt.FindExpr(Ex); + + ArgReport *R = new ArgReport(*this, desc.c_str(), *I, Ex); + R->addRange(Ex->getSourceRange()); + BR.EmitReport(R); + } + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(R)->getArg(), + N); + } +}; + +class VISIBILITY_HIDDEN OutOfBoundMemoryAccess : public BuiltinBug { +public: + OutOfBoundMemoryAccess(GRExprEngine* eng) + : BuiltinBug(eng,"Out-of-bounds memory access", + "Load or store into an out-of-bound memory position.") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + Emit(BR, Eng.explicit_oob_memacc_begin(), Eng.explicit_oob_memacc_end()); + } +}; + +class VISIBILITY_HIDDEN BadSizeVLA : public BuiltinBug { +public: + BadSizeVLA(GRExprEngine* eng) : + BuiltinBug(eng, "Bad variable-length array (VLA) size") {} + + void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { + for (GRExprEngine::ErrorNodes::iterator + I = Eng.ExplicitBadSizedVLA.begin(), + E = Eng.ExplicitBadSizedVLA.end(); I!=E; ++I) { + + // Determine whether this was a 'zero-sized' VLA or a VLA with an + // undefined size. + GRExprEngine::NodeTy* N = *I; + PostStmt PS = cast<PostStmt>(N->getLocation()); + DeclStmt *DS = cast<DeclStmt>(PS.getStmt()); + VarDecl* VD = cast<VarDecl>(*DS->decl_begin()); + QualType T = Eng.getContext().getCanonicalType(VD->getType()); + VariableArrayType* VT = cast<VariableArrayType>(T); + Expr* SizeExpr = VT->getSizeExpr(); + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "The expression used to specify the number of elements in the " + "variable-length array (VLA) '" + << VD->getNameAsString() << "' evaluates to "; + + bool isUndefined = Eng.getStateManager().GetSVal(N->getState(), + SizeExpr).isUndef(); + + if (isUndefined) + os << "an undefined or garbage value."; + else + os << "0. VLAs with no elements have undefined behavior."; + + std::string shortBuf; + llvm::raw_string_ostream os_short(shortBuf); + os_short << "Variable-length array '" << VD->getNameAsString() << "' " + << (isUndefined ? "garbage value for array size" + : "has zero elements (undefined behavior)"); + + ArgReport *report = new ArgReport(*this, os_short.str().c_str(), + os.str().c_str(), N, SizeExpr); + + report->addRange(SizeExpr->getSourceRange()); + BR.EmitReport(report); + } + } + + void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode<GRState>* N, + BuiltinBugReport *R) { + registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(R)->getArg(), + N); + } +}; + +//===----------------------------------------------------------------------===// +// __attribute__(nonnull) checking + +class VISIBILITY_HIDDEN CheckAttrNonNull : public GRSimpleAPICheck { + BugType *BT; + BugReporter &BR; + +public: + CheckAttrNonNull(BugReporter &br) : BT(0), BR(br) {} + + virtual bool Audit(ExplodedNode<GRState>* N, GRStateManager& VMgr) { + CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); + const GRState* state = N->getState(); + + SVal X = VMgr.GetSVal(state, CE->getCallee()); + + const FunctionDecl* FD = X.getAsFunctionDecl(); + if (!FD) + return false; + + const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); + + if (!Att) + return false; + + // Iterate through the arguments of CE and check them for null. + unsigned idx = 0; + bool hasError = false; + + for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; + ++I, ++idx) { + + if (!VMgr.isEqual(state, *I, 0) || !Att->isNonNull(idx)) + continue; + + // 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"); + + RangedBugReport *R = new RangedBugReport(*BT, + "Null pointer passed as an argument to a " + "'nonnull' parameter", N); + + R->addRange((*I)->getSourceRange()); + BR.EmitReport(R); + hasError = true; + } + + return hasError; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Definitions for bug reporter visitors. +//===----------------------------------------------------------------------===// + +static const Stmt *GetDerefExpr(const ExplodedNode<GRState> *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; +} + +static const Stmt *GetReceiverExpr(const ExplodedNode<GRState> *N) { + const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); + if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) + return ME->getReceiver(); + return NULL; +} + +static const Stmt *GetDenomExpr(const ExplodedNode<GRState> *N) { + const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); + if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S)) + return BE->getRHS(); + return NULL; +} + +static const Stmt *GetCalleeExpr(const ExplodedNode<GRState> *N) { + const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) + return CE->getCallee(); + return NULL; +} + +static const Stmt *GetRetValExpr(const ExplodedNode<GRState> *N) { + const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); + if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) + return RS->getRetValue(); + return NULL; +} + +namespace { +class VISIBILITY_HIDDEN FindLastStoreBRVisitor : public BugReporterVisitor { + const MemRegion *R; + SVal V; + bool satisfied; + const ExplodedNode<GRState> *StoreSite; +public: + FindLastStoreBRVisitor(SVal v, const MemRegion *r) + : R(r), V(v), satisfied(false), StoreSite(0) {} + + PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState> *N, + const ExplodedNode<GRState> *PrevN, + BugReporterContext& BRC) { + + if (satisfied) + return NULL; + + if (!StoreSite) { + GRStateManager &StateMgr = BRC.getStateManager(); + const ExplodedNode<GRState> *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 (StateMgr.GetSVal(Node->getState(), 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(C)) { + if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + if (C.isObjCObjectPointerType(TR->getValueType(C))) { + 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(C)) { + if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + if (C.isObjCObjectPointerType(TR->getValueType(C))) { + 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 + return NULL; + + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + os << '\'' << VR->getDecl()->getNameAsString() << '\''; + } + else + return NULL; + } + + // FIXME: Refactor this into BugReporterContext. + 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 VISIBILITY_HIDDEN TrackConstraintBRVisitor : public BugReporterVisitor { + SVal Constraint; + const bool Assumption; + bool isSatisfied; +public: + TrackConstraintBRVisitor(SVal constraint, bool assumption) + : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} + + PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState> *N, + const ExplodedNode<GRState> *PrevN, + BugReporterContext& BRC) { + if (isSatisfied) + return NULL; + + // Check if in the previous state it was feasible for this constraint + // to *not* be true. + + GRStateManager &StateMgr = BRC.getStateManager(); + bool isFeasible = false; + if (StateMgr.Assume(PrevN->getState(), Constraint, !Assumption, + isFeasible)) { + assert(isFeasible); // Eventually we don't need 'isFeasible'. + + 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. + isFeasible = false; + if (StateMgr.Assume(N->getState(), Constraint, !Assumption, + isFeasible)) { + assert(isFeasible); + 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. + 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, SVal Constraint, + bool Assumption) { + BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); +} + +static void registerTrackNullOrUndefValue(BugReporterContext& BRC, + const Stmt *S, + const ExplodedNode<GRState>* N) { + + 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); + + // What did we load? + SVal V = StateMgr.GetSVal(state, S); + + if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) + || V.isUndef()) { + registerFindLastStore(BRC, R, V); + } + } + } + + SVal V = StateMgr.GetSValAsScalarOrLoc(state, 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); + } + } +} + +//===----------------------------------------------------------------------===// +// Check registration. +//===----------------------------------------------------------------------===// + +void GRExprEngine::RegisterInternalChecks() { + // Register internal "built-in" BugTypes with the BugReporter. These BugTypes + // are different than what probably many checks will do since they don't + // create BugReports on-the-fly but instead wait until GRExprEngine finishes + // analyzing a function. Generation of BugReport objects is done via a call + // to 'FlushReports' from BugReporter. + BR.Register(new NullDeref(this)); + BR.Register(new UndefinedDeref(this)); + BR.Register(new UndefBranch(this)); + BR.Register(new DivZero(this)); + BR.Register(new UndefResult(this)); + BR.Register(new BadCall(this)); + BR.Register(new RetStack(this)); + BR.Register(new RetUndef(this)); + BR.Register(new BadArg(this)); + BR.Register(new BadMsgExprArg(this)); + BR.Register(new BadReceiver(this)); + BR.Register(new OutOfBoundMemoryAccess(this)); + BR.Register(new BadSizeVLA(this)); + BR.Register(new NilReceiverStructRet(this)); + BR.Register(new NilReceiverLargerThanVoidPtrRet(this)); + + // The following checks do not need to have their associated BugTypes + // explicitly registered with the BugReporter. If they issue any BugReports, + // their associated BugType will get registered with the BugReporter + // automatically. Note that the check itself is owned by the GRExprEngine + // object. + AddCheck(new CheckAttrNonNull(BR), Stmt::CallExprClass); +} diff --git a/lib/Analysis/GRSimpleVals.cpp b/lib/Analysis/GRSimpleVals.cpp new file mode 100644 index 0000000..e1c4848 --- /dev/null +++ b/lib/Analysis/GRSimpleVals.cpp @@ -0,0 +1,416 @@ +// GRSimpleVals.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 GRSimpleVals, a sub-class of GRTransferFuncs that +// provides transfer functions for performing simple value tracking with +// limited support for symbolics. +// +//===----------------------------------------------------------------------===// + +#include "GRSimpleVals.h" +#include "BasicObjCFoundationChecks.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/Analysis/PathSensitive/GRState.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/LocalCheckers.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "llvm/Support/Compiler.h" +#include <sstream> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Transfer Function creation for External clients. +//===----------------------------------------------------------------------===// + +GRTransferFuncs* clang::MakeGRSimpleValsTF() { return new GRSimpleVals(); } + +//===----------------------------------------------------------------------===// +// Transfer function for Casts. +//===----------------------------------------------------------------------===// + +SVal GRSimpleVals::EvalCast(GRExprEngine& Eng, NonLoc X, QualType T) { + + if (!isa<nonloc::ConcreteInt>(X)) + return UnknownVal(); + + bool isLocType = Loc::IsLocType(T); + + // Only handle casts from integers to integers. + if (!isLocType && !T->isIntegerType()) + return UnknownVal(); + + BasicValueFactory& BasicVals = Eng.getBasicVals(); + + llvm::APSInt V = cast<nonloc::ConcreteInt>(X).getValue(); + V.setIsUnsigned(T->isUnsignedIntegerType() || Loc::IsLocType(T)); + V.extOrTrunc(Eng.getContext().getTypeSize(T)); + + if (isLocType) + return loc::ConcreteInt(BasicVals.getValue(V)); + else + return nonloc::ConcreteInt(BasicVals.getValue(V)); +} + +// Casts. + +SVal GRSimpleVals::EvalCast(GRExprEngine& Eng, Loc X, QualType T) { + + // 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&. + // + assert (!X.isUnknownOrUndef()); + + if (Loc::IsLocType(T) || T->isReferenceType()) + return X; + + // FIXME: Handle transparent unions where a value can be "transparently" + // lifted into a union type. + if (T->isUnionType()) + return UnknownVal(); + + assert (T->isIntegerType()); + BasicValueFactory& BasicVals = Eng.getBasicVals(); + unsigned BitWidth = Eng.getContext().getTypeSize(T); + + if (!isa<loc::ConcreteInt>(X)) + return nonloc::LocAsInteger::Make(BasicVals, X, BitWidth); + + llvm::APSInt V = cast<loc::ConcreteInt>(X).getValue(); + V.setIsUnsigned(T->isUnsignedIntegerType() || Loc::IsLocType(T)); + V.extOrTrunc(BitWidth); + return nonloc::ConcreteInt(BasicVals.getValue(V)); +} + +// Unary operators. + +SVal GRSimpleVals::EvalMinus(GRExprEngine& Eng, UnaryOperator* U, NonLoc X){ + + switch (X.getSubKind()) { + + case nonloc::ConcreteIntKind: + return cast<nonloc::ConcreteInt>(X).EvalMinus(Eng.getBasicVals(), U); + + default: + return UnknownVal(); + } +} + +SVal GRSimpleVals::EvalComplement(GRExprEngine& Eng, NonLoc X) { + + switch (X.getSubKind()) { + + case nonloc::ConcreteIntKind: + return cast<nonloc::ConcreteInt>(X).EvalComplement(Eng.getBasicVals()); + + default: + return UnknownVal(); + } +} + +// Binary operators. + +static unsigned char LNotOpMap[] = { + (unsigned char) BinaryOperator::GE, /* LT => GE */ + (unsigned char) BinaryOperator::LE, /* GT => LE */ + (unsigned char) BinaryOperator::GT, /* LE => GT */ + (unsigned char) BinaryOperator::LT, /* GE => LT */ + (unsigned char) BinaryOperator::NE, /* EQ => NE */ + (unsigned char) BinaryOperator::EQ /* NE => EQ */ +}; + +SVal GRSimpleVals::DetermEvalBinOpNN(GRExprEngine& Eng, + BinaryOperator::Opcode Op, + NonLoc L, NonLoc R, + QualType T) { + + BasicValueFactory& BasicVals = Eng.getBasicVals(); + unsigned subkind = L.getSubKind(); + + while (1) { + + switch (subkind) { + default: + return UnknownVal(); + + case nonloc::LocAsIntegerKind: { + Loc LL = cast<nonloc::LocAsInteger>(L).getLoc(); + + switch (R.getSubKind()) { + case nonloc::LocAsIntegerKind: + return EvalBinOp(Eng, Op, LL, + cast<nonloc::LocAsInteger>(R).getLoc()); + + case nonloc::ConcreteIntKind: { + // Transform the integer into a location and compare. + ASTContext& Ctx = Eng.getContext(); + llvm::APSInt V = cast<nonloc::ConcreteInt>(R).getValue(); + V.setIsUnsigned(true); + V.extOrTrunc(Ctx.getTypeSize(Ctx.VoidPtrTy)); + return EvalBinOp(Eng, Op, LL, + loc::ConcreteInt(BasicVals.getValue(V))); + } + + default: + switch (Op) { + case BinaryOperator::EQ: + return NonLoc::MakeIntTruthVal(BasicVals, false); + case BinaryOperator::NE: + return NonLoc::MakeIntTruthVal(BasicVals, true); + default: + // This case also handles pointer arithmetic. + return UnknownVal(); + } + } + } + + case nonloc::SymExprValKind: { + // Logical not? + if (!(Op == BinaryOperator::EQ && R.isZeroConstant())) + return UnknownVal(); + + const SymExpr &SE=*cast<nonloc::SymExprVal>(L).getSymbolicExpression(); + + // Only handle ($sym op constant) for now. + if (const SymIntExpr *E = dyn_cast<SymIntExpr>(&SE)) { + BinaryOperator::Opcode Opc = E->getOpcode(); + + if (Opc < BinaryOperator::LT || Opc > BinaryOperator::NE) + return UnknownVal(); + + // For comparison operators, translate the constraint by + // changing the opcode. + int idx = (unsigned) Opc - (unsigned) BinaryOperator::LT; + + assert (idx >= 0 && + (unsigned) idx < sizeof(LNotOpMap)/sizeof(unsigned char)); + + Opc = (BinaryOperator::Opcode) LNotOpMap[idx]; + assert(E->getType(Eng.getContext()) == T); + E = Eng.getSymbolManager().getSymIntExpr(E->getLHS(), Opc, + E->getRHS(), T); + return nonloc::SymExprVal(E); + } + + return UnknownVal(); + } + + case nonloc::ConcreteIntKind: + + if (isa<nonloc::ConcreteInt>(R)) { + const nonloc::ConcreteInt& L_CI = cast<nonloc::ConcreteInt>(L); + const nonloc::ConcreteInt& R_CI = cast<nonloc::ConcreteInt>(R); + return L_CI.EvalBinOp(BasicVals, Op, R_CI); + } + else { + subkind = R.getSubKind(); + NonLoc tmp = R; + R = L; + L = tmp; + + // Swap the operators. + switch (Op) { + case BinaryOperator::LT: Op = BinaryOperator::GT; break; + case BinaryOperator::GT: Op = BinaryOperator::LT; break; + case BinaryOperator::LE: Op = BinaryOperator::GE; break; + case BinaryOperator::GE: Op = BinaryOperator::LE; break; + default: break; + } + + continue; + } + + case nonloc::SymbolValKind: + if (isa<nonloc::ConcreteInt>(R)) { + ValueManager &ValMgr = Eng.getValueManager(); + return ValMgr.makeNonLoc(cast<nonloc::SymbolVal>(L).getSymbol(), Op, + cast<nonloc::ConcreteInt>(R).getValue(), T); + } + else + return UnknownVal(); + } + } +} + + +// Binary Operators (except assignments and comma). + +SVal GRSimpleVals::EvalBinOp(GRExprEngine& Eng, BinaryOperator::Opcode Op, + Loc L, Loc R) { + + switch (Op) { + default: + return UnknownVal(); + case BinaryOperator::EQ: + case BinaryOperator::NE: + return EvalEquality(Eng, L, R, Op == BinaryOperator::EQ); + } +} + +SVal GRSimpleVals::EvalBinOp(GRExprEngine& Eng, const GRState *state, + BinaryOperator::Opcode Op, Loc L, NonLoc R) { + + // Special case: 'R' 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 *RInt = dyn_cast<nonloc::ConcreteInt>(&R)) { + const llvm::APSInt *X = &RInt->getValue(); + ASTContext &C = Eng.getContext(); + if (C.getTypeSize(C.VoidPtrTy) == X->getBitWidth()) { + // Convert the signedness of the integer (if necessary). + if (X->isSigned()) + X = &Eng.getBasicVals().getValue(*X, true); + + return EvalBinOp(Eng, Op, L, loc::ConcreteInt(*X)); + } + } + } + + // Delegate pointer arithmetic to store manager. + return Eng.getStoreManager().EvalBinOp(state, Op, L, R); +} + +// Equality operators for Locs. +// FIXME: All this logic will be revamped when we have MemRegion::getLocation() +// implemented. + +SVal GRSimpleVals::EvalEquality(GRExprEngine& Eng, Loc L, Loc R, bool isEqual) { + + BasicValueFactory& BasicVals = Eng.getBasicVals(); + + switch (L.getSubKind()) { + + default: + assert(false && "EQ/NE not implemented for this Loc."); + return UnknownVal(); + + case loc::ConcreteIntKind: + + if (isa<loc::ConcreteInt>(R)) { + bool b = cast<loc::ConcreteInt>(L).getValue() == + cast<loc::ConcreteInt>(R).getValue(); + + // Are we computing '!='? Flip the result. + if (!isEqual) + b = !b; + + return NonLoc::MakeIntTruthVal(BasicVals, b); + } + else if (SymbolRef Sym = R.getAsSymbol()) { + const SymIntExpr * SE = + Eng.getSymbolManager().getSymIntExpr(Sym, + isEqual ? BinaryOperator::EQ + : BinaryOperator::NE, + cast<loc::ConcreteInt>(L).getValue(), + Eng.getContext().IntTy); + return nonloc::SymExprVal(SE); + } + + break; + + case loc::MemRegionKind: { + if (SymbolRef LSym = L.getAsLocSymbol()) { + if (isa<loc::ConcreteInt>(R)) { + const SymIntExpr *SE = + Eng.getSymbolManager().getSymIntExpr(LSym, + isEqual ? BinaryOperator::EQ + : BinaryOperator::NE, + cast<loc::ConcreteInt>(R).getValue(), + Eng.getContext().IntTy); + + return nonloc::SymExprVal(SE); + } + } + } + + // Fall-through. + + case loc::GotoLabelKind: + return NonLoc::MakeIntTruthVal(BasicVals, isEqual ? L == R : L != R); + } + + return NonLoc::MakeIntTruthVal(BasicVals, isEqual ? false : true); +} + +//===----------------------------------------------------------------------===// +// Transfer function for function calls. +//===----------------------------------------------------------------------===// + +void GRSimpleVals::EvalCall(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + CallExpr* CE, SVal L, + ExplodedNode<GRState>* Pred) { + + GRStateManager& StateMgr = Eng.getStateManager(); + const GRState* St = Builder.GetState(Pred); + + // Invalidate all arguments passed in by reference (Locs). + + for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) { + + SVal V = StateMgr.GetSVal(St, *I); + + if (isa<loc::MemRegionVal>(V)) + St = StateMgr.BindLoc(St, cast<Loc>(V), UnknownVal()); + else if (isa<nonloc::LocAsInteger>(V)) + St = StateMgr.BindLoc(St, cast<nonloc::LocAsInteger>(V).getLoc(), + UnknownVal()); + + } + + // Make up a symbol for the return value of this function. + // FIXME: We eventually should handle structs and other compound types + // that are returned by value. + QualType T = CE->getType(); + if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { + unsigned Count = Builder.getCurrentBlockCount(); + SVal X = Eng.getValueManager().getConjuredSymbolVal(CE, Count); + St = StateMgr.BindExpr(St, CE, X, Eng.getCFG().isBlkExpr(CE), false); + } + + Builder.MakeNode(Dst, CE, Pred, St); +} + +//===----------------------------------------------------------------------===// +// Transfer function for Objective-C message expressions. +//===----------------------------------------------------------------------===// + +void GRSimpleVals::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Eng, + GRStmtNodeBuilder<GRState>& Builder, + ObjCMessageExpr* ME, + ExplodedNode<GRState>* Pred) { + + + // The basic transfer function logic for message expressions does nothing. + // We just invalidate all arguments passed in by references. + + GRStateManager& StateMgr = Eng.getStateManager(); + const GRState* St = Builder.GetState(Pred); + + for (ObjCMessageExpr::arg_iterator I = ME->arg_begin(), E = ME->arg_end(); + I != E; ++I) { + + SVal V = StateMgr.GetSVal(St, *I); + + if (isa<Loc>(V)) + St = StateMgr.BindLoc(St, cast<Loc>(V), UnknownVal()); + } + + Builder.MakeNode(Dst, ME, Pred, St); +} diff --git a/lib/Analysis/GRSimpleVals.h b/lib/Analysis/GRSimpleVals.h new file mode 100644 index 0000000..6ef49dc --- /dev/null +++ b/lib/Analysis/GRSimpleVals.h @@ -0,0 +1,86 @@ +// GRSimpleVals.h - 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 GRSimpleVals, a sub-class of GRTransferFuncs that +// provides transfer functions for performing simple value tracking with +// limited support for symbolics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_GRSIMPLEVALS +#define LLVM_CLANG_ANALYSIS_GRSIMPLEVALS + +#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" + +namespace clang { + +class PathDiagnostic; +class ASTContext; + +class GRSimpleVals : public GRTransferFuncs { +protected: + + virtual SVal DetermEvalBinOpNN(GRExprEngine& Eng, + BinaryOperator::Opcode Op, + NonLoc L, NonLoc R, QualType T); + +public: + GRSimpleVals() {} + virtual ~GRSimpleVals() {} + + // Casts. + + virtual SVal EvalCast(GRExprEngine& Engine, NonLoc V, QualType CastT); + virtual SVal EvalCast(GRExprEngine& Engine, Loc V, QualType CastT); + + // Unary Operators. + + virtual SVal EvalMinus(GRExprEngine& Engine, UnaryOperator* U, NonLoc X); + + virtual SVal EvalComplement(GRExprEngine& Engine, NonLoc X); + + // Binary Operators. + + virtual SVal EvalBinOp(GRExprEngine& Engine, BinaryOperator::Opcode Op, + Loc L, Loc R); + + // Pointer arithmetic. + + virtual SVal EvalBinOp(GRExprEngine& Engine, const GRState *state, + BinaryOperator::Opcode Op, Loc L, NonLoc R); + + // Calls. + + virtual void EvalCall(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + CallExpr* CE, SVal L, + ExplodedNode<GRState>* Pred); + + virtual void EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst, + GRExprEngine& Engine, + GRStmtNodeBuilder<GRState>& Builder, + ObjCMessageExpr* ME, + ExplodedNode<GRState>* Pred); + + + + static void GeneratePathDiagnostic(PathDiagnostic& PD, ASTContext& Ctx, + ExplodedNode<GRState>* N); + +protected: + + // Equality (==, !=) operators for Locs. + SVal EvalEquality(GRExprEngine& Engine, Loc L, Loc R, bool isEqual); +}; + +} // end clang namespace + +#endif diff --git a/lib/Analysis/GRState.cpp b/lib/Analysis/GRState.cpp new file mode 100644 index 0000000..e0e478c --- /dev/null +++ b/lib/Analysis/GRState.cpp @@ -0,0 +1,318 @@ +//= GRState*cpp - Path-Sens. "State" for tracking valuues -----*- 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 SymbolRef, ExprBindKey, and GRState* +// +//===----------------------------------------------------------------------===// + +#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. +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, *this, + state, RegionRoots); + + // Clean up the store. + NewState.St = StoreMgr->RemoveDeadBindings(&NewState, Loc, SymReaper, + RegionRoots); + + return ConstraintMgr->RemoveDeadBindings(getPersistentState(NewState), + SymReaper); +} + +const GRState* GRStateManager::Unbind(const GRState* St, Loc LV) { + Store OldStore = St->getStore(); + Store NewStore = StoreMgr->Remove(OldStore, LV); + + if (NewStore == OldStore) + return St; + + GRState NewSt = *St; + NewSt.St = NewStore; + return getPersistentState(NewSt); +} + +const GRState* GRStateManager::getInitialState() { + + GRState StateImpl(EnvMgr.getInitialEnvironment(), + StoreMgr->getInitialStore(), + GDMFactory.GetEmptyMap()); + + return getPersistentState(StateImpl); +} + +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* GRStateManager::MakeStateWithStore(const GRState* St, + Store store) { + GRState NewSt = *St; + NewSt.St = store; + return getPersistentState(NewSt); +} + + +//===----------------------------------------------------------------------===// +// State pretty-printing. +//===----------------------------------------------------------------------===// + +void GRState::print(std::ostream& Out, StoreManager& StoreMgr, + ConstraintManager& ConstraintMgr, + Printer** Beg, Printer** End, + const char* nl, const char* sep) const { + + // Print the store. + StoreMgr.print(getStore(), Out, nl, sep); + + // Print Subexpression bindings. + bool isFirst = true; + + for (seb_iterator I = seb_begin(), E = seb_end(); I != E; ++I) { + + if (isFirst) { + Out << nl << nl << "Sub-Expressions:" << nl; + isFirst = false; + } + else { Out << nl; } + + Out << " (" << (void*) I.getKey() << ") "; + llvm::raw_os_ostream OutS(Out); + I.getKey()->printPretty(OutS); + OutS.flush(); + Out << " : "; + I.getData().print(Out); + } + + // Print block-expression bindings. + isFirst = true; + + for (beb_iterator I = beb_begin(), E = beb_end(); I != E; ++I) { + + if (isFirst) { + Out << nl << nl << "Block-level Expressions:" << nl; + isFirst = false; + } + else { Out << nl; } + + Out << " (" << (void*) I.getKey() << ") "; + llvm::raw_os_ostream OutS(Out); + I.getKey()->printPretty(OutS); + OutS.flush(); + Out << " : "; + I.getData().print(Out); + } + + ConstraintMgr.print(this, Out, nl, sep); + + // Print checker-specific data. + for ( ; Beg != End ; ++Beg) (*Beg)->Print(Out, this, nl, sep); +} + +void GRStateRef::printDOT(std::ostream& Out) const { + print(Out, "\\l", "\\|"); +} + +void GRStateRef::printStdErr() const { + print(*llvm::cerr); +} + +void GRStateRef::print(std::ostream& Out, const char* nl, const char* sep)const{ + GRState::Printer **beg = Mgr->Printers.empty() ? 0 : &Mgr->Printers[0]; + GRState::Printer **end = !beg ? 0 : beg + Mgr->Printers.size(); + St->print(Out, *Mgr->StoreMgr, *Mgr->ConstraintMgr, beg, end, nl, sep); +} + +//===----------------------------------------------------------------------===// +// 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 VISIBILITY_HIDDEN ScanReachableSymbols : public SubRegionMap::Visitor { + typedef llvm::DenseSet<const MemRegion*> VisitedRegionsTy; + + VisitedRegionsTy visited; + GRStateRef state; + SymbolVisitor &visitor; + llvm::OwningPtr<SubRegionMap> SRM; +public: + + ScanReachableSymbols(GRStateManager* sm, const GRState *st, SymbolVisitor& v) + : state(st, *sm), 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 (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.getManager().getStoreManager().getSubRegionMap(state)); + + return SRM->iterSubRegions(R, *this); +} + +bool GRStateManager::scanReachableSymbols(SVal val, const GRState* state, + SymbolVisitor& visitor) { + ScanReachableSymbols S(this, state, visitor); + return S.scan(val); +} + +//===----------------------------------------------------------------------===// +// Queries. +//===----------------------------------------------------------------------===// + +bool GRStateManager::isEqual(const GRState* state, Expr* Ex, + const llvm::APSInt& Y) { + + SVal V = GetSVal(state, 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, Expr* Ex, uint64_t x) { + return isEqual(state, Ex, getBasicVals().getValue(x, Ex->getType())); +} + +//===----------------------------------------------------------------------===// +// Persistent values for indexing into the Generic Data Map. + +int GRState::NullDerefTag::TagInt = 0; + diff --git a/lib/Analysis/GRTransferFuncs.cpp b/lib/Analysis/GRTransferFuncs.cpp new file mode 100644 index 0000000..69c09d9 --- /dev/null +++ b/lib/Analysis/GRTransferFuncs.cpp @@ -0,0 +1,28 @@ +//== GRTransferFuncs.cpp - Path-Sens. Transfer Functions Interface -*- 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 GRTransferFuncs, which provides a base-class that +// defines an interface for transfer functions used by GRExprEngine. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" + +using namespace clang; + +void GRTransferFuncs::EvalBinOpNN(GRStateSet& OStates, + GRExprEngine& Eng, + const GRState *St, Expr* Ex, + BinaryOperator::Opcode Op, + NonLoc L, NonLoc R, QualType T) { + + OStates.Add(Eng.getStateManager().BindExpr(St, Ex, + DetermEvalBinOpNN(Eng, Op, L, R, T))); +} diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp new file mode 100644 index 0000000..b0eb37b --- /dev/null +++ b/lib/Analysis/LiveVariables.cpp @@ -0,0 +1,359 @@ +//=- LiveVariables.cpp - Live Variable Analysis for Source CFGs -*- 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 Live Variables analysis for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Basic/SourceManager.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/CFG.h" +#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" +#include "clang/Analysis/FlowSensitive/DataflowSolver.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" + +#include <string.h> +#include <stdio.h> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Useful constants. +//===----------------------------------------------------------------------===// + +static const bool Alive = true; +static const bool Dead = false; + +//===----------------------------------------------------------------------===// +// Dataflow initialization logic. +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN RegisterDecls + : public CFGRecStmtDeclVisitor<RegisterDecls> { + + LiveVariables::AnalysisDataTy& AD; + + typedef llvm::SmallVector<VarDecl*, 20> AlwaysLiveTy; + AlwaysLiveTy AlwaysLive; + + +public: + RegisterDecls(LiveVariables::AnalysisDataTy& ad) : AD(ad) {} + + ~RegisterDecls() { + + AD.AlwaysLive.resetValues(AD); + + for (AlwaysLiveTy::iterator I = AlwaysLive.begin(), E = AlwaysLive.end(); + I != E; ++ I) + AD.AlwaysLive(*I, AD) = Alive; + } + + void VisitImplicitParamDecl(ImplicitParamDecl* IPD) { + // Register the VarDecl for tracking. + AD.Register(IPD); + } + + void VisitVarDecl(VarDecl* VD) { + // Register the VarDecl for tracking. + AD.Register(VD); + + // Does the variable have global storage? If so, it is always live. + if (VD->hasGlobalStorage()) + AlwaysLive.push_back(VD); + } + + CFG& getCFG() { return AD.getCFG(); } +}; +} // end anonymous namespace + +LiveVariables::LiveVariables(ASTContext& Ctx, CFG& cfg) { + // Register all referenced VarDecls. + getAnalysisData().setCFG(cfg); + getAnalysisData().setContext(Ctx); + + RegisterDecls R(getAnalysisData()); + cfg.VisitBlockStmts(R); +} + +//===----------------------------------------------------------------------===// +// Transfer functions. +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN TransferFuncs : public CFGRecStmtVisitor<TransferFuncs>{ + LiveVariables::AnalysisDataTy& AD; + LiveVariables::ValTy LiveState; +public: + TransferFuncs(LiveVariables::AnalysisDataTy& ad) : AD(ad) {} + + LiveVariables::ValTy& getVal() { return LiveState; } + CFG& getCFG() { return AD.getCFG(); } + + void VisitDeclRefExpr(DeclRefExpr* DR); + void VisitBinaryOperator(BinaryOperator* B); + void VisitAssign(BinaryOperator* B); + void VisitDeclStmt(DeclStmt* DS); + void BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S); + void VisitUnaryOperator(UnaryOperator* U); + void Visit(Stmt *S); + void VisitTerminator(CFGBlock* B); + + void SetTopValue(LiveVariables::ValTy& V) { + V = AD.AlwaysLive; + } + +}; + +void TransferFuncs::Visit(Stmt *S) { + + if (S == getCurrentBlkStmt()) { + + if (AD.Observer) + AD.Observer->ObserveStmt(S,AD,LiveState); + + if (getCFG().isBlkExpr(S)) LiveState(S,AD) = Dead; + StmtVisitor<TransferFuncs,void>::Visit(S); + } + else if (!getCFG().isBlkExpr(S)) { + + if (AD.Observer) + AD.Observer->ObserveStmt(S,AD,LiveState); + + StmtVisitor<TransferFuncs,void>::Visit(S); + + } + else + // For block-level expressions, mark that they are live. + LiveState(S,AD) = Alive; +} + +void TransferFuncs::VisitTerminator(CFGBlock* B) { + + const Stmt* E = B->getTerminatorCondition(); + + if (!E) + return; + + assert (getCFG().isBlkExpr(E)); + LiveState(E, AD) = Alive; +} + +void TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) { + if (VarDecl* V = dyn_cast<VarDecl>(DR->getDecl())) + LiveState(V,AD) = Alive; +} + +void TransferFuncs::VisitBinaryOperator(BinaryOperator* B) { + if (B->isAssignmentOp()) VisitAssign(B); + else VisitStmt(B); +} + +void +TransferFuncs::BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S) { + + // This is a block-level expression. Its value is 'dead' before this point. + LiveState(S, AD) = Dead; + + // This represents a 'use' of the collection. + Visit(S->getCollection()); + + // This represents a 'kill' for the variable. + Stmt* Element = S->getElement(); + DeclRefExpr* DR = 0; + VarDecl* VD = 0; + + if (DeclStmt* DS = dyn_cast<DeclStmt>(Element)) + VD = cast<VarDecl>(DS->getSingleDecl()); + else { + Expr* ElemExpr = cast<Expr>(Element)->IgnoreParens(); + if ((DR = dyn_cast<DeclRefExpr>(ElemExpr))) + VD = cast<VarDecl>(DR->getDecl()); + else { + Visit(ElemExpr); + return; + } + } + + if (VD) { + LiveState(VD, AD) = Dead; + if (AD.Observer && DR) { AD.Observer->ObserverKill(DR); } + } +} + + +void TransferFuncs::VisitUnaryOperator(UnaryOperator* U) { + Expr *E = U->getSubExpr(); + + switch (U->getOpcode()) { + case UnaryOperator::PostInc: + case UnaryOperator::PostDec: + case UnaryOperator::PreInc: + case UnaryOperator::PreDec: + // Walk through the subexpressions, blasting through ParenExprs + // until we either find a DeclRefExpr or some non-DeclRefExpr + // expression. + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E->IgnoreParens())) + if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) { + // Treat the --/++ operator as a kill. + if (AD.Observer) { AD.Observer->ObserverKill(DR); } + LiveState(VD, AD) = Alive; + return VisitDeclRefExpr(DR); + } + + // Fall-through. + + default: + return Visit(E); + } +} + +void TransferFuncs::VisitAssign(BinaryOperator* B) { + Expr* LHS = B->getLHS(); + + // Assigning to a variable? + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParens())) { + + // Update liveness inforamtion. + unsigned bit = AD.getIdx(DR->getDecl()); + LiveState.getDeclBit(bit) = Dead | AD.AlwaysLive.getDeclBit(bit); + + if (AD.Observer) { AD.Observer->ObserverKill(DR); } + + // Handle things like +=, etc., which also generate "uses" + // of a variable. Do this just by visiting the subexpression. + if (B->getOpcode() != BinaryOperator::Assign) + VisitDeclRefExpr(DR); + } + else // Not assigning to a variable. Process LHS as usual. + Visit(LHS); + + Visit(B->getRHS()); +} + +void TransferFuncs::VisitDeclStmt(DeclStmt* DS) { + // Declarations effectively "kill" a variable since they cannot + // possibly be live before they are declared. + for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) + if (VarDecl* VD = dyn_cast<VarDecl>(*DI)) { + // The initializer is evaluated after the variable comes into scope. + // Since this is a reverse dataflow analysis, we must evaluate the + // transfer function for this expression first. + if (Expr* Init = VD->getInit()) + Visit(Init); + + if (const VariableArrayType* VT = + AD.getContext().getAsVariableArrayType(VD->getType())) { + StmtIterator I(const_cast<VariableArrayType*>(VT)); + StmtIterator E; + for (; I != E; ++I) Visit(*I); + } + + // Update liveness information by killing the VarDecl. + unsigned bit = AD.getIdx(VD); + LiveState.getDeclBit(bit) = Dead | AD.AlwaysLive.getDeclBit(bit); + } +} + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Merge operator: if something is live on any successor block, it is live +// in the current block (a set union). +//===----------------------------------------------------------------------===// + +namespace { + +struct Merge { + typedef StmtDeclBitVector_Types::ValTy ValTy; + + void operator()(ValTy& Dst, const ValTy& Src) { + Dst.OrDeclBits(Src); + Dst.OrBlkExprBits(Src); + } +}; + +typedef DataflowSolver<LiveVariables, TransferFuncs, Merge> Solver; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// External interface to run Liveness analysis. +//===----------------------------------------------------------------------===// + +void LiveVariables::runOnCFG(CFG& cfg) { + Solver S(*this); + S.runOnCFG(cfg); +} + +void LiveVariables::runOnAllBlocks(const CFG& cfg, + LiveVariables::ObserverTy* Obs, + bool recordStmtValues) { + Solver S(*this); + ObserverTy* OldObserver = getAnalysisData().Observer; + getAnalysisData().Observer = Obs; + S.runOnAllBlocks(cfg, recordStmtValues); + getAnalysisData().Observer = OldObserver; +} + +//===----------------------------------------------------------------------===// +// liveness queries +// + +bool LiveVariables::isLive(const CFGBlock* B, const VarDecl* D) const { + DeclBitVector_Types::Idx i = getAnalysisData().getIdx(D); + return i.isValid() ? getBlockData(B).getBit(i) : false; +} + +bool LiveVariables::isLive(const ValTy& Live, const VarDecl* D) const { + DeclBitVector_Types::Idx i = getAnalysisData().getIdx(D); + return i.isValid() ? Live.getBit(i) : false; +} + +bool LiveVariables::isLive(const Stmt* Loc, const Stmt* StmtVal) const { + return getStmtData(Loc)(StmtVal,getAnalysisData()); +} + +bool LiveVariables::isLive(const Stmt* Loc, const VarDecl* D) const { + return getStmtData(Loc)(D,getAnalysisData()); +} + +//===----------------------------------------------------------------------===// +// printing liveness state for debugging +// + +void LiveVariables::dumpLiveness(const ValTy& V, SourceManager& SM) const { + const AnalysisDataTy& AD = getAnalysisData(); + + for (AnalysisDataTy::decl_iterator I = AD.begin_decl(), + E = AD.end_decl(); I!=E; ++I) + if (V.getDeclBit(I->second)) { + fprintf(stderr, " %s <", I->first->getIdentifier()->getName()); + I->first->getLocation().dump(SM); + fprintf(stderr, ">\n"); + } +} + +void LiveVariables::dumpBlockLiveness(SourceManager& M) const { + for (BlockDataMapTy::iterator I = getBlockDataMap().begin(), + E = getBlockDataMap().end(); I!=E; ++I) { + fprintf(stderr, "\n[ B%d (live variables at block exit) ]\n", + I->first->getBlockID()); + + dumpLiveness(I->second,M); + } + + fprintf(stderr,"\n"); +} diff --git a/lib/Analysis/Makefile b/lib/Analysis/Makefile new file mode 100644 index 0000000..c597254 --- /dev/null +++ b/lib/Analysis/Makefile @@ -0,0 +1,22 @@ +##===- clang/lib/Analysis/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME := clangAnalysis +BUILD_ARCHIVE = 1 +CXXFLAGS = -fno-rtti + +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include + +include $(LEVEL)/Makefile.common + diff --git a/lib/Analysis/MemRegion.cpp b/lib/Analysis/MemRegion.cpp new file mode 100644 index 0000000..9f066f4 --- /dev/null +++ b/lib/Analysis/MemRegion.cpp @@ -0,0 +1,494 @@ +//== 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" + +using namespace clang; + + +MemRegion::~MemRegion() {} + +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; +} + +void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned)getKind()); +} + +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) { + ID.AddInteger((unsigned) AllocaRegionKind); + ID.AddPointer(Ex); + ID.AddInteger(cnt); +} + +void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ProfileRegion(ID, Ex, Cnt); +} + +void TypedViewRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, QualType T, + const MemRegion* superRegion) { + ID.AddInteger((unsigned) TypedViewRegionKind); + ID.Add(T); + ID.AddPointer(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 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 SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym) { + ID.AddInteger((unsigned) MemRegion::SymbolicRegionKind); + ID.Add(sym); +} + +void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { + SymbolicRegion::ProfileRegion(ID, sym); +} + +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 CodeTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const void* data, + QualType t) { + ID.AddInteger(MemRegion::CodeTextRegionKind); + ID.AddPointer(data); + ID.Add(t); +} + +void CodeTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + CodeTextRegion::ProfileRegion(ID, Data, LocationType); +} + +//===----------------------------------------------------------------------===// +// Region pretty-printing. +//===----------------------------------------------------------------------===// + +std::string MemRegion::getString() const { + std::string s; + llvm::raw_string_ostream os(s); + print(os); + return os.str(); +} + +void MemRegion::print(llvm::raw_ostream& os) const { + os << "<Unknown Region>"; +} + +void AllocaRegion::print(llvm::raw_ostream& os) const { + os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; +} + +void CodeTextRegion::print(llvm::raw_ostream& os) const { + os << "code{"; + if (isDeclared()) + os << getDecl()->getDeclName().getAsString(); + else + os << '$' << getSymbol(); + + os << '}'; +} + +void CompoundLiteralRegion::print(llvm::raw_ostream& os) const { + // FIXME: More elaborate pretty-printing. + os << "{ " << (void*) CL << " }"; +} + +void ElementRegion::print(llvm::raw_ostream& os) const { + superRegion->print(os); + os << '['; Index.print(os); os << ']'; +} + +void FieldRegion::print(llvm::raw_ostream& os) const { + superRegion->print(os); + os << "->" << getDecl()->getNameAsString(); +} + +void StringRegion::print(llvm::raw_ostream& os) const { + Str->printPretty(os); +} + +void SymbolicRegion::print(llvm::raw_ostream& os) const { + os << "SymRegion-" << sym; +} + +void TypedViewRegion::print(llvm::raw_ostream& os) const { + os << "typed_view{" << LValueType.getAsString() << ','; + getSuperRegion()->print(os); + os << '}'; +} + +void VarRegion::print(llvm::raw_ostream& os) const { + os << cast<VarDecl>(D)->getNameAsString(); +} + +//===----------------------------------------------------------------------===// +// MemRegionManager methods. +//===----------------------------------------------------------------------===// + +MemSpaceRegion* MemRegionManager::LazyAllocate(MemSpaceRegion*& region) { + + if (!region) { + region = (MemSpaceRegion*) A.Allocate<MemSpaceRegion>(); + new (region) MemSpaceRegion(); + } + + return region; +} + +MemSpaceRegion* MemRegionManager::getStackRegion() { + return LazyAllocate(stack); +} + +MemSpaceRegion* MemRegionManager::getGlobalsRegion() { + return LazyAllocate(globals); +} + +MemSpaceRegion* MemRegionManager::getHeapRegion() { + return LazyAllocate(heap); +} + +MemSpaceRegion* MemRegionManager::getUnknownRegion() { + return LazyAllocate(unknown); +} + +MemSpaceRegion* MemRegionManager::getCodeRegion() { + return LazyAllocate(code); +} + +bool MemRegionManager::onStack(const MemRegion* R) { + while (const SubRegion* SR = dyn_cast<SubRegion>(R)) + R = SR->getSuperRegion(); + + return (R != 0) && (R == stack); +} + +bool MemRegionManager::onHeap(const MemRegion* R) { + while (const SubRegion* SR = dyn_cast<SubRegion>(R)) + R = SR->getSuperRegion(); + + return (R != 0) && (R == heap); +} + +StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str) { + llvm::FoldingSetNodeID ID; + MemSpaceRegion* GlobalsR = getGlobalsRegion(); + + StringRegion::ProfileRegion(ID, Str, GlobalsR); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + StringRegion* R = cast_or_null<StringRegion>(data); + + if (!R) { + R = (StringRegion*) A.Allocate<StringRegion>(); + new (R) StringRegion(Str, GlobalsR); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +VarRegion* MemRegionManager::getVarRegion(const VarDecl* d) { + + const MemRegion* superRegion = d->hasLocalStorage() ? getStackRegion() + : getGlobalsRegion(); + + llvm::FoldingSetNodeID ID; + DeclRegion::ProfileRegion(ID, d, superRegion, MemRegion::VarRegionKind); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + VarRegion* R = cast_or_null<VarRegion>(data); + + if (!R) { + R = (VarRegion*) A.Allocate<VarRegion>(); + new (R) VarRegion(d, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +CompoundLiteralRegion* +MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL) { + // Is this compound literal allocated on the stack or is part of the + // global constant pool? + const MemRegion* superRegion = CL->isFileScope() ? + getGlobalsRegion() : getStackRegion(); + + // Profile the compound literal. + llvm::FoldingSetNodeID ID; + CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + CompoundLiteralRegion* R = cast_or_null<CompoundLiteralRegion>(data); + + if (!R) { + R = (CompoundLiteralRegion*) A.Allocate<CompoundLiteralRegion>(); + new (R) CompoundLiteralRegion(CL, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +ElementRegion* +MemRegionManager::getElementRegion(QualType elementType, SVal Idx, + const MemRegion* superRegion){ + + llvm::FoldingSetNodeID ID; + ElementRegion::ProfileRegion(ID, elementType, 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(elementType, Idx, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +CodeTextRegion* MemRegionManager::getCodeTextRegion(const FunctionDecl* fd, + QualType t) { + llvm::FoldingSetNodeID ID; + CodeTextRegion::ProfileRegion(ID, fd, t); + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + CodeTextRegion* R = cast_or_null<CodeTextRegion>(data); + + if (!R) { + R = (CodeTextRegion*) A.Allocate<CodeTextRegion>(); + new (R) CodeTextRegion(fd, t, getCodeRegion()); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +CodeTextRegion* MemRegionManager::getCodeTextRegion(SymbolRef sym, QualType t) { + llvm::FoldingSetNodeID ID; + CodeTextRegion::ProfileRegion(ID, sym, t); + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + CodeTextRegion* R = cast_or_null<CodeTextRegion>(data); + + if (!R) { + R = (CodeTextRegion*) A.Allocate<CodeTextRegion>(); + new (R) CodeTextRegion(sym, t, getCodeRegion()); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. +SymbolicRegion* MemRegionManager::getSymbolicRegion(SymbolRef sym) { + llvm::FoldingSetNodeID ID; + SymbolicRegion::ProfileRegion(ID, sym); + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + SymbolicRegion* R = cast_or_null<SymbolicRegion>(data); + + if (!R) { + R = (SymbolicRegion*) A.Allocate<SymbolicRegion>(); + // SymbolicRegion's storage class is usually unknown. + new (R) SymbolicRegion(sym, getUnknownRegion()); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +FieldRegion* MemRegionManager::getFieldRegion(const FieldDecl* d, + const MemRegion* superRegion) { + llvm::FoldingSetNodeID ID; + DeclRegion::ProfileRegion(ID, d, superRegion, MemRegion::FieldRegionKind); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + FieldRegion* R = cast_or_null<FieldRegion>(data); + + if (!R) { + R = (FieldRegion*) A.Allocate<FieldRegion>(); + new (R) FieldRegion(d, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +ObjCIvarRegion* +MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, + const MemRegion* superRegion) { + llvm::FoldingSetNodeID ID; + DeclRegion::ProfileRegion(ID, d, superRegion, MemRegion::ObjCIvarRegionKind); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + ObjCIvarRegion* R = cast_or_null<ObjCIvarRegion>(data); + + if (!R) { + R = (ObjCIvarRegion*) A.Allocate<ObjCIvarRegion>(); + new (R) ObjCIvarRegion(d, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +ObjCObjectRegion* +MemRegionManager::getObjCObjectRegion(const ObjCInterfaceDecl* d, + const MemRegion* superRegion) { + llvm::FoldingSetNodeID ID; + DeclRegion::ProfileRegion(ID, d, superRegion, + MemRegion::ObjCObjectRegionKind); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + ObjCObjectRegion* R = cast_or_null<ObjCObjectRegion>(data); + + if (!R) { + R = (ObjCObjectRegion*) A.Allocate<ObjCObjectRegion>(); + new (R) ObjCObjectRegion(d, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +TypedViewRegion* +MemRegionManager::getTypedViewRegion(QualType t, const MemRegion* superRegion) { + llvm::FoldingSetNodeID ID; + TypedViewRegion::ProfileRegion(ID, t, superRegion); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + TypedViewRegion* R = cast_or_null<TypedViewRegion>(data); + + if (!R) { + R = (TypedViewRegion*) A.Allocate<TypedViewRegion>(); + new (R) TypedViewRegion(t, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +AllocaRegion* MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt) { + llvm::FoldingSetNodeID ID; + AllocaRegion::ProfileRegion(ID, E, cnt); + + void* InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + AllocaRegion* R = cast_or_null<AllocaRegion>(data); + + if (!R) { + R = (AllocaRegion*) A.Allocate<AllocaRegion>(); + new (R) AllocaRegion(E, cnt, getStackRegion()); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +bool MemRegionManager::hasStackStorage(const MemRegion* R) { + + // Only subregions can have stack storage. + const SubRegion* SR = dyn_cast<SubRegion>(R); + + if (!SR) + return false; + + MemSpaceRegion* S = getStackRegion(); + + while (SR) { + R = SR->getSuperRegion(); + if (R == S) + return true; + + SR = dyn_cast<SubRegion>(R); + } + + return false; +} + + +//===----------------------------------------------------------------------===// +// View handling. +//===----------------------------------------------------------------------===// + +const MemRegion *TypedViewRegion::removeViews() const { + const SubRegion *SR = this; + const MemRegion *R = SR; + while (SR && isa<TypedViewRegion>(SR)) { + R = SR->getSuperRegion(); + SR = dyn_cast<SubRegion>(R); + } + return R; +} diff --git a/lib/Analysis/PathDiagnostic.cpp b/lib/Analysis/PathDiagnostic.cpp new file mode 100644 index 0000000..ec96329 --- /dev/null +++ b/lib/Analysis/PathDiagnostic.cpp @@ -0,0 +1,242 @@ +//===--- 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" +#include <sstream> +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 size_t GetNumCharsToLastNonPeriod(const char *s) { + const char *start = s; + const char *lastNonPeriod = 0; + + for ( ; *s != '\0' ; ++s) + if (*s != '.') lastNonPeriod = s; + + if (!lastNonPeriod) + return 0; + + return (lastNonPeriod - start) + 1; +} + +static inline size_t GetNumCharsToLastNonPeriod(const std::string &s) { + return s.empty () ? 0 : GetNumCharsToLastNonPeriod(&s[0]); +} + +PathDiagnosticPiece::PathDiagnosticPiece(const std::string& s, + Kind k, DisplayHint hint) + : str(s, 0, GetNumCharsToLastNonPeriod(s)), kind(k), Hint(hint) {} + +PathDiagnosticPiece::PathDiagnosticPiece(const char* s, Kind k, + DisplayHint hint) + : str(s, GetNumCharsToLastNonPeriod(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(const char* bugtype, const char* desc, + const char* category) + : Size(0), + BugType(bugtype, GetNumCharsToLastNonPeriod(bugtype)), + Desc(desc, GetNumCharsToLastNonPeriod(desc)), + Category(category, GetNumCharsToLastNonPeriod(category)) {} + +PathDiagnostic::PathDiagnostic(const std::string& bugtype, + const std::string& desc, + const std::string& category) + : Size(0), + BugType(bugtype, 0, GetNumCharsToLastNonPeriod(bugtype)), + Desc(desc, 0, GetNumCharsToLastNonPeriod(desc)), + Category(category, 0, GetNumCharsToLastNonPeriod(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(), + std::string(StrC.begin(), StrC.end())); + + 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->getBodyIfAvailable()) { + 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; + } +} + + diff --git a/lib/Analysis/RangeConstraintManager.cpp b/lib/Analysis/RangeConstraintManager.cpp new file mode 100644 index 0000000..f6ac2b9 --- /dev/null +++ b/lib/Analysis/RangeConstraintManager.cpp @@ -0,0 +1,363 @@ +//== 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/Frontend/ManagerRegistry.h" +#include "llvm/Support/Compiler.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 VISIBILITY_HIDDEN ConstraintRange {}; } +static int ConstraintRangeIndex = 0; + +/// A Range represents the closed range [from, to]. The caller must +/// guarantee that from <= to. Note that Range is immutable, so as not +/// to subvert RangeSet's immutability. +namespace { +class VISIBILITY_HIDDEN Range : public std::pair<const llvm::APSInt*, + 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 VISIBILITY_HIDDEN 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 VISIBILITY_HIDDEN 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(std::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 VISIBILITY_HIDDEN RangeConstraintManager : public SimpleConstraintManager{ + RangeSet GetRange(GRStateRef state, SymbolRef sym); +public: + RangeConstraintManager(GRStateManager& statemgr) + : SimpleConstraintManager(statemgr) {} + + const GRState* AssumeSymNE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymLT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymGT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymGE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + const GRState* AssumeSymLE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, bool& isFeasible); + + 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, std::ostream& Out, + const char* nl, const char *sep); + +private: + RangeSet::Factory F; +}; + +} // end anonymous namespace + +ConstraintManager* clang::CreateRangeConstraintManager(GRStateManager& StateMgr) +{ + return new RangeConstraintManager(StateMgr); +} + +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* St, + SymbolReaper& SymReaper) { + GRStateRef state(St, StateMgr); + + 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(GRStateRef 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* St, SymbolRef sym,\ + const llvm::APSInt& V, bool& isFeasible){\ + GRStateRef state(St, StateMgr);\ + const RangeSet& R = GetRange(state, sym).Add##OP(state.getBasicVals(), F, V);\ + isFeasible = !R.isEmpty();\ + return isFeasible ? state.set<ConstraintRange>(sym, R).getState() : 0;\ +} + +AssumeX(EQ) +AssumeX(NE) +AssumeX(LT) +AssumeX(GT) +AssumeX(LE) +AssumeX(GE) + +//===------------------------------------------------------------------------=== +// Pretty-printing. +//===------------------------------------------------------------------------===/ + +void RangeConstraintManager::print(const GRState* St, std::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 new file mode 100644 index 0000000..02d3d1f --- /dev/null +++ b/lib/Analysis/RegionStore.cpp @@ -0,0 +1,1304 @@ +//== 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/GRState.h" +#include "clang/Analysis/PathSensitive/GRStateTrait.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Basic/TargetInfo.h" + +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; + +// Actual Store type. +typedef llvm::ImmutableMap<const MemRegion*, SVal> RegionBindingsTy; + +//===----------------------------------------------------------------------===// +// Region "Views" +//===----------------------------------------------------------------------===// +// +// MemRegions can be layered on top of each other. This GDM entry tracks +// what are the MemRegions that layer a given MemRegion. +// +typedef llvm::ImmutableSet<const MemRegion*> RegionViews; +namespace { class VISIBILITY_HIDDEN RegionViewMap {}; } +static int RegionViewMapIndex = 0; +namespace clang { + template<> struct GRStateTrait<RegionViewMap> + : public GRStatePartialTrait<llvm::ImmutableMap<const MemRegion*, + RegionViews> > { + + static void* GDMIndex() { return &RegionViewMapIndex; } + }; +} + +// RegionCasts records the current cast type of a region. +namespace { class VISIBILITY_HIDDEN RegionCasts {}; } +static int RegionCastsIndex = 0; +namespace clang { + template<> struct GRStateTrait<RegionCasts> + : public GRStatePartialTrait<llvm::ImmutableMap<const MemRegion*, + QualType> > { + static void* GDMIndex() { return &RegionCastsIndex; } + }; +} + +//===----------------------------------------------------------------------===// +// 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 VISIBILITY_HIDDEN RegionExtents {}; } +static int RegionExtentsIndex = 0; +namespace clang { + template<> struct GRStateTrait<RegionExtents> + : public GRStatePartialTrait<llvm::ImmutableMap<const MemRegion*, SVal> > { + static void* GDMIndex() { return &RegionExtentsIndex; } + }; +} + +//===----------------------------------------------------------------------===// +// Region "killsets". +//===----------------------------------------------------------------------===// +// +// RegionStore lazily adds value bindings to regions when the analyzer handles +// assignment statements. Killsets track which default values have been +// killed, thus distinguishing between "unknown" values and default +// values. Regions are added to killset only when they are assigned "unknown" +// directly, otherwise we should have their value in the region bindings. +// +namespace { class VISIBILITY_HIDDEN RegionKills {}; } +static int RegionKillsIndex = 0; +namespace clang { + template<> struct GRStateTrait<RegionKills> + : public GRStatePartialTrait< llvm::ImmutableSet<const MemRegion*> > { + static void* GDMIndex() { return &RegionKillsIndex; } + }; +} + +//===----------------------------------------------------------------------===// +// Regions with default values. +//===----------------------------------------------------------------------===// +// +// This GDM entry tracks what regions have a default value if they have no bound +// value and have not been killed. +// +namespace { class VISIBILITY_HIDDEN RegionDefaultValue {}; } +static int RegionDefaultValueIndex = 0; +namespace clang { + template<> struct GRStateTrait<RegionDefaultValue> + : public GRStatePartialTrait<llvm::ImmutableMap<const MemRegion*, SVal> > { + static void* GDMIndex() { return &RegionDefaultValueIndex; } + }; +} + +//===----------------------------------------------------------------------===// +// Main RegionStore logic. +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN RegionStoreSubRegionMap : public SubRegionMap { + typedef llvm::DenseMap<const MemRegion*, + llvm::ImmutableSet<const MemRegion*> > Map; + + llvm::ImmutableSet<const MemRegion*>::Factory F; + Map M; + +public: + void add(const MemRegion* Parent, const MemRegion* SubRegion) { + Map::iterator I = M.find(Parent); + M.insert(std::make_pair(Parent, + F.Add(I == M.end() ? F.GetEmptySet() : I->second, SubRegion))); + } + + ~RegionStoreSubRegionMap() {} + + bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { + Map::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; + } +}; + +class VISIBILITY_HIDDEN RegionStoreManager : public StoreManager { + RegionBindingsTy::Factory RBFactory; + RegionViews::Factory RVFactory; + + const MemRegion* SelfRegion; + const ImplicitParamDecl *SelfDecl; + +public: + RegionStoreManager(GRStateManager& mgr) + : StoreManager(mgr), + RBFactory(mgr.getAllocator()), + RVFactory(mgr.getAllocator()), + SelfRegion(0), SelfDecl(0) { + if (const ObjCMethodDecl* MD = + dyn_cast<ObjCMethodDecl>(&StateMgr.getCodeDecl())) + SelfDecl = MD->getSelfDecl(); + } + + virtual ~RegionStoreManager() {} + + SubRegionMap* getSubRegionMap(const GRState *state); + + const GRState* BindCompoundLiteral(const GRState* St, + const CompoundLiteralExpr* CL, SVal V); + + /// 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 GRState* St, 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 GRState* St, 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 GRState* St, const VarDecl* VD); + + SVal getLValueIvar(const GRState* St, const ObjCIvarDecl* D, SVal Base); + + SVal getLValueField(const GRState* St, SVal Base, const FieldDecl* D); + + SVal getLValueFieldOrIvar(const GRState* St, SVal Base, const Decl* D); + + SVal getLValueElement(const GRState* St, QualType elementType, + SVal Base, SVal Offset); + + SVal getSizeInElements(const GRState* St, const MemRegion* R); + + /// 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); + + CastResult CastRegion(const GRState* state, const MemRegion* R, + QualType CastToTy); + + SVal EvalBinOp(const GRState *state,BinaryOperator::Opcode Op,Loc L,NonLoc R); + + /// 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 + SVal Retrieve(const GRState* state, Loc L, QualType T = QualType()); + + const GRState* Bind(const GRState* St, Loc LV, SVal V); + + Store Remove(Store store, Loc LV); + + Store getInitialStore() { return RBFactory.GetEmptyMap().getRoot(); } + + /// getSelfRegion - Returns the region for the 'self' (Objective-C) or + /// 'this' object (C++). When used when analyzing a normal function this + /// method returns NULL. + const MemRegion* getSelfRegion(Store) { + if (!SelfDecl) + return 0; + + if (!SelfRegion) { + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(&StateMgr.getCodeDecl()); + SelfRegion = MRMgr.getObjCObjectRegion(MD->getClassInterface(), + MRMgr.getHeapRegion()); + } + + return SelfRegion; + } + + /// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values. + /// It returns a new Store with these values removed, and populates LSymbols + // and DSymbols with the known set of live and dead symbols respectively. + Store RemoveDeadBindings(const GRState* state, Stmt* Loc, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); + + const GRState* BindDecl(const GRState* St, const VarDecl* VD, SVal InitVal); + + const GRState* BindDeclWithNoInit(const GRState* St, const VarDecl* VD) { + return St; + } + + const GRState* setExtent(const GRState* St, const MemRegion* R, SVal Extent); + const GRState* setCastType(const GRState* St, const MemRegion* R, QualType T); + + static inline RegionBindingsTy GetRegionBindings(Store store) { + return RegionBindingsTy(static_cast<const RegionBindingsTy::TreeTy*>(store)); + } + + void print(Store store, std::ostream& Out, const char* nl, const char *sep); + + void iterBindings(Store store, BindingsHandler& f) { + // FIXME: Implement. + } + const GRState* setDefaultValue(const GRState* St, const MemRegion* R, SVal V); +private: + const GRState* BindArray(const GRState* St, const TypedRegion* R, SVal V); + + /// 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); + + const GRState* BindStruct(const GRState* St, const TypedRegion* R, SVal V); + + /// KillStruct - Set the entire struct to unknown. + const GRState* KillStruct(const GRState* St, const TypedRegion* R); + + // Utility methods. + BasicValueFactory& getBasicVals() { return StateMgr.getBasicVals(); } + ASTContext& getContext() { return StateMgr.getContext(); } + + SymbolManager& getSymbolManager() { return StateMgr.getSymbolManager(); } + + const GRState* AddRegionView(const GRState* St, + const MemRegion* View, const MemRegion* Base); + const GRState* RemoveRegionView(const GRState* St, + const MemRegion* View, const MemRegion* Base); +}; + +} // end anonymous namespace + +StoreManager* clang::CreateRegionStoreManager(GRStateManager& StMgr) { + return new RegionStoreManager(StMgr); +} + +SubRegionMap* RegionStoreManager::getSubRegionMap(const GRState *state) { + RegionBindingsTy B = GetRegionBindings(state->getStore()); + RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); + + for (RegionBindingsTy::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + if (const SubRegion* R = dyn_cast<SubRegion>(I.getKey())) + M->add(R->getSuperRegion(), R); + } + + return M; +} + +/// 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 GRState* St, + 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 GRState* St, const VarDecl* VD) { + return loc::MemRegionVal(MRMgr.getVarRegion(VD)); +} + +/// 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 +RegionStoreManager::getLValueCompoundLiteral(const GRState* St, + const CompoundLiteralExpr* CL) { + return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL)); +} + +SVal RegionStoreManager::getLValueIvar(const GRState* St, const ObjCIvarDecl* D, + SVal Base) { + return getLValueFieldOrIvar(St, Base, D); +} + +SVal RegionStoreManager::getLValueField(const GRState* St, SVal Base, + const FieldDecl* D) { + return getLValueFieldOrIvar(St, Base, D); +} + +SVal RegionStoreManager::getLValueFieldOrIvar(const GRState* St, SVal Base, + const Decl* D) { + 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(const GRState* St, + QualType elementType, + SVal Base, SVal Offset) { + + // 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); + + 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. + // + + // Offset might be unsigned. We have to convert it to signed ConcreteInt. + if (nonloc::ConcreteInt* CI = dyn_cast<nonloc::ConcreteInt>(&Offset)) { + const llvm::APSInt& OffI = CI->getValue(); + if (OffI.isUnsigned()) { + llvm::APSInt Tmp = OffI; + Tmp.setIsSigned(true); + Offset = NonLoc::MakeVal(getBasicVals(), Tmp); + } + } + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, + BaseRegion)); + } + + 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()); + + // FIXME: This appears to be the assumption of this code. We should review + // whether or not BaseIdxI.getBitWidth() < OffI.getBitWidth(). If it + // can't we need to put a comment here. If it can, we should handle it. + assert(BaseIdxI.getBitWidth() >= OffI.getBitWidth()); + + const MemRegion *ArrayR = ElemR->getSuperRegion(); + SVal NewIdx; + + if (OffI.isUnsigned() || OffI.getBitWidth() < BaseIdxI.getBitWidth()) { + // 'Offset' might be unsigned. We have to convert it to signed and + // possibly extend it. + llvm::APSInt Tmp = OffI; + + if (OffI.getBitWidth() < BaseIdxI.getBitWidth()) + Tmp.extend(BaseIdxI.getBitWidth()); + + Tmp.setIsSigned(true); + Tmp += BaseIdxI; // Compute the new offset. + NewIdx = NonLoc::MakeVal(getBasicVals(), Tmp); + } + else + NewIdx = nonloc::ConcreteInt(getBasicVals().getValue(BaseIdxI + OffI)); + + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR)); +} + +SVal RegionStoreManager::getSizeInElements(const GRState* St, + const MemRegion* R) { + if (const VarRegion* VR = dyn_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 NonLoc::MakeVal(getBasicVals(), CAT->getSize(), false); + } + + GRStateRef state(St, StateMgr); + const QualType* CastTy = state.get<RegionCasts>(VR); + + // If the VarRegion is cast to other type, compute the size with respect to + // that type. + if (CastTy) { + QualType EleTy =cast<PointerType>(CastTy->getTypePtr())->getPointeeType(); + QualType VarTy = VR->getValueType(getContext()); + uint64_t EleSize = getContext().getTypeSize(EleTy); + uint64_t VarSize = getContext().getTypeSize(VarTy); + return NonLoc::MakeIntVal(getBasicVals(), VarSize / EleSize, false); + } + + // Clients can use ordinary variables as if they were arrays. These + // essentially are arrays of size 1. + return NonLoc::MakeIntVal(getBasicVals(), 1, false); + } + + if (const StringRegion* SR = dyn_cast<StringRegion>(R)) { + const StringLiteral* Str = SR->getStringLiteral(); + // We intentionally made the size value signed because it participates in + // operations with signed indices. + return NonLoc::MakeIntVal(getBasicVals(), Str->getByteLength()+1, false); + } + + if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) { + // FIXME: Unsupported yet. + FR = 0; + return UnknownVal(); + } + + if (isa<SymbolicRegion>(R)) { + return UnknownVal(); + } + + if (isa<AllocaRegion>(R)) { + return UnknownVal(); + } + + if (isa<ElementRegion>(R)) { + return UnknownVal(); + } + + assert(0 && "Other regions are not supported yet."); + return UnknownVal(); +} + +/// 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(); + + nonloc::ConcreteInt Idx(getBasicVals().getZeroWithPtrWidth(false)); + ElementRegion* ER = MRMgr.getElementRegion(T, Idx, ArrayR); + + return loc::MemRegionVal(ER); +} + +RegionStoreManager::CastResult +RegionStoreManager::CastRegion(const GRState* state, const MemRegion* R, + QualType CastToTy) { + + ASTContext& Ctx = StateMgr.getContext(); + + // We need to know the real type of CastToTy. + QualType ToTy = Ctx.getCanonicalType(CastToTy); + + // Check cast to ObjCQualifiedID type. + if (isa<ObjCQualifiedIdType>(ToTy)) { + // FIXME: Record the type information aside. + return CastResult(state, R); + } + + // CodeTextRegion should be cast to only function pointer type. + if (isa<CodeTextRegion>(R)) { + assert(CastToTy->isFunctionPointerType() || CastToTy->isBlockPointerType()); + return CastResult(state, R); + } + + // Now assume we are casting from pointer to pointer. Other cases should + // already be handled. + QualType PointeeTy = cast<PointerType>(ToTy.getTypePtr())->getPointeeType(); + + // Process region cast according to the kind of the region being cast. + + // FIXME: Need to handle arbitrary downcasts. + if (isa<SymbolicRegion>(R) || isa<AllocaRegion>(R)) { + state = setCastType(state, R, ToTy); + return CastResult(state, R); + } + + // VarRegion, ElementRegion, and FieldRegion has an inherent type. Normally + // they should not be cast. We only layer an ElementRegion when the cast-to + // pointee type is of smaller size. In other cases, we return the original + // VarRegion. + if (isa<VarRegion>(R) || isa<ElementRegion>(R) || isa<FieldRegion>(R) + || isa<ObjCIvarRegion>(R) || isa<CompoundLiteralRegion>(R)) { + // If the pointee type is incomplete, do not compute its size, and return + // the original region. + if (const RecordType *RT = dyn_cast<RecordType>(PointeeTy.getTypePtr())) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition(getContext())) + return CastResult(state, R); + } + + QualType ObjTy = cast<TypedRegion>(R)->getValueType(getContext()); + uint64_t PointeeTySize = getContext().getTypeSize(PointeeTy); + uint64_t ObjTySize = getContext().getTypeSize(ObjTy); + + if ((PointeeTySize > 0 && PointeeTySize < ObjTySize) || + (ObjTy->isAggregateType() && PointeeTy->isScalarType())) { + // Record the cast type of the region. + state = setCastType(state, R, ToTy); + + SVal Idx = ValMgr.makeZeroArrayIndex(); + ElementRegion* ER = MRMgr.getElementRegion(PointeeTy, Idx, R); + return CastResult(state, ER); + } else + return CastResult(state, R); + } + + if (isa<ObjCObjectRegion>(R)) { + return CastResult(state, R); + } + + assert(0 && "Unprocessed region."); + return 0; +} + +SVal RegionStoreManager::EvalBinOp(const GRState *state, + BinaryOperator::Opcode Op, Loc L, NonLoc R) { + // 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; + + // If the operand is a symbolic or alloca region, create the first element + // region on it. + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) { + // Get symbol's type. It should be a pointer type. + SymbolRef Sym = SR->getSymbol(); + QualType T = Sym->getType(getContext()); + QualType EleTy = cast<PointerType>(T.getTypePtr())->getPointeeType(); + + SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR); + } + else if (const AllocaRegion *AR = dyn_cast<AllocaRegion>(MR)) { + // Get the alloca region's current cast type. + GRStateRef StRef(state, StateMgr); + + GRStateTrait<RegionCasts>::lookup_type T = StRef.get<RegionCasts>(AR); + assert(T && "alloca region has no type."); + QualType EleTy = cast<PointerType>(T->getTypePtr())->getPointeeType(); + SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR); + } + else + ER = cast<ElementRegion>(MR); + + SVal Idx = ER->getIndex(); + + nonloc::ConcreteInt* Base = dyn_cast<nonloc::ConcreteInt>(&Idx); + nonloc::ConcreteInt* Offset = dyn_cast<nonloc::ConcreteInt>(&R); + + // Only support concrete integer indexes for now. + if (Base && Offset) { + // FIXME: For now, convert the signedness and bitwidth of offset in case + // they don't match. This can result from pointer arithmetic. In reality, + // we should figure out what are the proper semantics and implement them. + // + // This addresses the test case test/Analysis/ptr-arith.c + // + nonloc::ConcreteInt OffConverted(getBasicVals().Convert(Base->getValue(), + Offset->getValue())); + SVal NewIdx = Base->EvalBinOp(getBasicVals(), Op, OffConverted); + const MemRegion* NewER = + MRMgr.getElementRegion(ER->getElementType(), NewIdx,ER->getSuperRegion()); + return Loc::MakeVal(NewER); + + } + + return UnknownVal(); +} + +SVal RegionStoreManager::Retrieve(const GRState* St, 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 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<SymbolicRegion>(MR) || isa<AllocaRegion>(MR)) + return 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); + assert(R && "bad region"); + + // 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. + + QualType RTy = R->getValueType(getContext()); + + if (RTy->isStructureType()) + return RetrieveStruct(St, R); + + if (RTy->isArrayType()) + return RetrieveArray(St, R); + + // FIXME: handle Vector types. + if (RTy->isVectorType()) + return UnknownVal(); + + RegionBindingsTy B = GetRegionBindings(St->getStore()); + RegionBindingsTy::data_type* V = B.lookup(R); + + // Check if the region has a binding. + if (V) + return *V; + + GRStateRef state(St, StateMgr); + + // Check if the region is in killset. + if (state.contains<RegionKills>(R)) + return UnknownVal(); + + // Check if the region is an element region of a string literal. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + if (const StringRegion *StrR=dyn_cast<StringRegion>(ER->getSuperRegion())) { + const StringLiteral *Str = StrR->getStringLiteral(); + SVal Idx = ER->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { + int64_t i = CI->getValue().getSExtValue(); + char c; + if (i == Str->getByteLength()) + c = '\0'; + else + c = Str->getStrData()[i]; + const llvm::APSInt &V = getBasicVals().getValue(c, getContext().CharTy); + return nonloc::ConcreteInt(V); + } + } + } + + // If the region is an element or field, it may have a default value. + if (isa<ElementRegion>(R) || isa<FieldRegion>(R)) { + const MemRegion* SuperR = cast<SubRegion>(R)->getSuperRegion(); + GRStateTrait<RegionDefaultValue>::lookup_type D = + state.get<RegionDefaultValue>(SuperR); + if (D) { + // If the default value is symbolic, we need to create a new symbol. + if (D->hasConjuredSymbol()) + return ValMgr.getRegionValueSymbolVal(R); + else + return *D; + } + } + + if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { + const MemRegion *SR = IVR->getSuperRegion(); + + // If the super region is 'self' then return the symbol representing + // the value of the ivar upon entry to the method. + if (SR == SelfRegion) { + // FIXME: Do we need to handle the case where the super region + // has a view? We want to canonicalize the bindings. + return ValMgr.getRegionValueSymbolVal(R); + } + + // Otherwise, we need a new symbol. For now return Unknown. + return UnknownVal(); + } + + // 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'. + + // We treat function parameters as symbolic values. + if (const VarRegion* VR = dyn_cast<VarRegion>(R)) { + const VarDecl *VD = VR->getDecl(); + + if (VD == SelfDecl) + return loc::MemRegionVal(getSelfRegion(0)); + + if (isa<ParmVarDecl>(VD) || isa<ImplicitParamDecl>(VD) || + VD->hasGlobalStorage()) { + QualType VTy = VD->getType(); + if (Loc::IsLocType(VTy) || VTy->isIntegerType()) + return ValMgr.getRegionValueSymbolVal(VR); + else + return UnknownVal(); + } + } + + if (MRMgr.onStack(R) || MRMgr.onHeap(R)) { + // 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 UndefinedVal(); + } + + // All other integer values are symbolic. + if (Loc::IsLocType(RTy) || RTy->isIntegerType()) + return ValMgr.getRegionValueSymbolVal(R); + else + return UnknownVal(); +} + +SVal RegionStoreManager::RetrieveStruct(const GRState* St,const TypedRegion* R){ + QualType T = R->getValueType(getContext()); + assert(T->isStructureType()); + + const RecordType* RT = cast<RecordType>(T.getTypePtr()); + RecordDecl* RD = RT->getDecl(); + assert(RD->isDefinition()); + + llvm::ImmutableList<SVal> StructVal = getBasicVals().getEmptySValList(); + + std::vector<FieldDecl *> Fields(RD->field_begin(getContext()), + RD->field_end(getContext())); + + 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(St, loc::MemRegionVal(FR), FTy); + StructVal = getBasicVals().consVals(FieldValue, StructVal); + } + + return NonLoc::MakeCompoundVal(T, StructVal, getBasicVals()); +} + +SVal RegionStoreManager::RetrieveArray(const GRState* St, const TypedRegion* R){ + QualType T = R->getValueType(getContext()); + ConstantArrayType* CAT = cast<ConstantArrayType>(T.getTypePtr()); + + llvm::ImmutableList<SVal> ArrayVal = getBasicVals().getEmptySValList(); + llvm::APSInt Size(CAT->getSize(), false); + llvm::APSInt i = getBasicVals().getZeroWithPtrWidth(false); + + for (; i < Size; ++i) { + SVal Idx = NonLoc::MakeVal(getBasicVals(), i); + ElementRegion* ER = MRMgr.getElementRegion(CAT->getElementType(), Idx, R); + QualType ETy = ER->getElementType(); + SVal ElementVal = Retrieve(St, loc::MemRegionVal(ER), ETy); + ArrayVal = getBasicVals().consVals(ElementVal, ArrayVal); + } + + return NonLoc::MakeCompoundVal(T, ArrayVal, getBasicVals()); +} + +const GRState* RegionStoreManager::Bind(const GRState* St, Loc L, SVal V) { + // If we get here, the location should be a region. + const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion(); + assert(R); + + // Check if the region is a struct region. + if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) + if (TR->getValueType(getContext())->isStructureType()) + return BindStruct(St, TR, V); + + Store store = St->getStore(); + RegionBindingsTy B = GetRegionBindings(store); + + if (V.isUnknown()) { + // Remove the binding. + store = RBFactory.Remove(B, R).getRoot(); + + // Add the region to the killset. + GRStateRef state(St, StateMgr); + St = state.add<RegionKills>(R); + } + else + store = RBFactory.Add(B, R, V).getRoot(); + + return StateMgr.MakeStateWithStore(St, store); +} + +Store RegionStoreManager::Remove(Store store, Loc L) { + const MemRegion* R = 0; + + if (isa<loc::MemRegionVal>(L)) + R = cast<loc::MemRegionVal>(L).getRegion(); + + if (R) { + RegionBindingsTy B = GetRegionBindings(store); + return RBFactory.Remove(B, R).getRoot(); + } + + return store; +} + +const GRState* RegionStoreManager::BindDecl(const GRState* St, + const VarDecl* VD, SVal InitVal) { + + QualType T = VD->getType(); + VarRegion* VR = MRMgr.getVarRegion(VD); + + if (T->isArrayType()) + return BindArray(St, VR, InitVal); + if (T->isStructureType()) + return BindStruct(St, VR, InitVal); + + return Bind(St, Loc::MakeVal(VR), InitVal); +} + +// FIXME: this method should be merged into Bind(). +const GRState* +RegionStoreManager::BindCompoundLiteral(const GRState* St, + const CompoundLiteralExpr* CL, SVal V) { + CompoundLiteralRegion* R = MRMgr.getCompoundLiteralRegion(CL); + return Bind(St, loc::MemRegionVal(R), V); +} + +const GRState* RegionStoreManager::setExtent(const GRState* St, + const MemRegion* R, SVal Extent) { + GRStateRef state(St, StateMgr); + return state.set<RegionExtents>(R, Extent); +} + + +static void UpdateLiveSymbols(SVal X, SymbolReaper& SymReaper) { + if (loc::MemRegionVal *XR = dyn_cast<loc::MemRegionVal>(&X)) { + const MemRegion *R = XR->getRegion(); + + while (R) { + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + SymReaper.markLive(SR->getSymbol()); + return; + } + + if (const SubRegion *SR = dyn_cast<SubRegion>(R)) { + R = SR->getSuperRegion(); + continue; + } + + break; + } + + return; + } + + for (SVal::symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end();SI!=SE;++SI) + SymReaper.markLive(*SI); +} + +Store RegionStoreManager::RemoveDeadBindings(const GRState* state, Stmt* Loc, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) +{ + + Store store = state->getStore(); + RegionBindingsTy B = GetRegionBindings(store); + + // Lazily constructed backmap from MemRegions to SubRegions. + typedef llvm::ImmutableSet<const MemRegion*> SubRegionsTy; + typedef llvm::ImmutableMap<const MemRegion*, SubRegionsTy> SubRegionsMapTy; + + // FIXME: As a future optimization we can modifiy BumpPtrAllocator to have + // the ability to reuse memory. This way we can keep TmpAlloc around as + // an instance variable of RegionStoreManager (avoiding repeated malloc + // overhead). + llvm::BumpPtrAllocator TmpAlloc; + + // Factory objects. + SubRegionsMapTy::Factory SubRegMapF(TmpAlloc); + SubRegionsTy::Factory SubRegF(TmpAlloc); + + // The backmap from regions to subregions. + SubRegionsMapTy SubRegMap = SubRegMapF.GetEmptyMap(); + + // 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; + + for (RegionBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + IntermediateRoots.push_back(I.getKey()); + } + + while (!IntermediateRoots.empty()) { + const MemRegion* R = IntermediateRoots.back(); + IntermediateRoots.pop_back(); + + if (const VarRegion* VR = dyn_cast<VarRegion>(R)) { + if (SymReaper.isLive(Loc, VR->getDecl())) + RegionRoots.push_back(VR); // This is a live "root". + } + else if (const SymbolicRegion* SR = dyn_cast<SymbolicRegion>(R)) { + if (SymReaper.isLive(SR->getSymbol())) + RegionRoots.push_back(SR); + } + else { + // Get the super region for R. + const MemRegion* SuperR = cast<SubRegion>(R)->getSuperRegion(); + + // Get the current set of subregions for SuperR. + const SubRegionsTy* SRptr = SubRegMap.lookup(SuperR); + SubRegionsTy SRs = SRptr ? *SRptr : SubRegF.GetEmptySet(); + + // Add R to the subregions of SuperR. + SubRegMap = SubRegMapF.Add(SubRegMap, SuperR, SubRegF.Add(SRs, R)); + + // Super region may be VarRegion or subregion of another VarRegion. Add it + // to the work list. + if (isa<SubRegion>(SuperR)) + IntermediateRoots.push_back(SuperR); + } + } + + // Process the worklist of RegionRoots. This performs a "mark-and-sweep" + // of the store. We want to find all live symbols and dead regions. + llvm::SmallPtrSet<const MemRegion*, 10> Marked; + + while (!RegionRoots.empty()) { + // Dequeue the next region on the worklist. + const MemRegion* R = RegionRoots.back(); + RegionRoots.pop_back(); + + // Check if we have already processed this region. + if (Marked.count(R)) continue; + + // Mark this region as processed. This is needed for termination in case + // a region is referenced more than once. + Marked.insert(R); + + // 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()); + + // Get the data binding for R (if any). + RegionBindingsTy::data_type* Xptr = B.lookup(R); + if (Xptr) { + SVal X = *Xptr; + UpdateLiveSymbols(X, SymReaper); // Update the set of live symbols. + + // If X is a region, then add it the RegionRoots. + if (loc::MemRegionVal* RegionX = dyn_cast<loc::MemRegionVal>(&X)) + RegionRoots.push_back(RegionX->getRegion()); + } + + // Get the subregions of R. These are RegionRoots as well since they + // represent values that are also bound to R. + const SubRegionsTy* SRptr = SubRegMap.lookup(R); + if (!SRptr) continue; + SubRegionsTy SR = *SRptr; + + for (SubRegionsTy::iterator I=SR.begin(), E=SR.end(); I!=E; ++I) + RegionRoots.push_back(*I); + } + + // 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 (RegionBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const MemRegion* R = I.getKey(); + + // If this region live? Is so, none of its symbols are dead. + if (Marked.count(R)) + continue; + + // Remove this dead region from the store. + store = Remove(store, Loc::MakeVal(R)); + + // 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(); + SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) SymReaper.maybeDead(*SI); + } + + return store; +} + +void RegionStoreManager::print(Store store, std::ostream& Out, + const char* nl, const char *sep) { + llvm::raw_os_ostream OS(Out); + RegionBindingsTy B = GetRegionBindings(store); + OS << "Store:" << nl; + + for (RegionBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + OS << ' '; I.getKey()->print(OS); OS << " : "; + I.getData().print(OS); OS << nl; + } +} + +const GRState* RegionStoreManager::BindArray(const GRState* St, + const TypedRegion* R, SVal Init) { + QualType T = R->getValueType(getContext()); + assert(T->isArrayType()); + + // When we are binding the whole array, it always has default value 0. + GRStateRef state(St, StateMgr); + St = state.set<RegionDefaultValue>(R, NonLoc::MakeIntVal(getBasicVals(), 0, + false)); + + ConstantArrayType* CAT = cast<ConstantArrayType>(T.getTypePtr()); + + llvm::APSInt Size(CAT->getSize(), false); + llvm::APSInt i = getBasicVals().getValue(0, Size.getBitWidth(), + Size.isUnsigned()); + + // 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 (; i < Size; ++i, ++j) { + if (j >= len) + break; + + SVal Idx = NonLoc::MakeVal(getBasicVals(), i); + ElementRegion* ER = + MRMgr.getElementRegion(cast<ArrayType>(T)->getElementType(), + Idx, R); + + SVal V = NonLoc::MakeVal(getBasicVals(), str[j], sizeof(char)*8, true); + St = Bind(St, loc::MemRegionVal(ER), V); + } + + return St; + } + + nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(Init); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + + for (; i < Size; ++i, ++VI) { + // The init list might be shorter than the array decl. + if (VI == VE) + break; + + SVal Idx = NonLoc::MakeVal(getBasicVals(), i); + ElementRegion* ER = + MRMgr.getElementRegion(cast<ArrayType>(T)->getElementType(), + Idx, R); + + if (CAT->getElementType()->isStructureType()) + St = BindStruct(St, ER, *VI); + else + St = Bind(St, Loc::MakeVal(ER), *VI); + } + + return St; +} + +const GRState* +RegionStoreManager::BindStruct(const GRState* St, const TypedRegion* R, SVal V){ + QualType T = R->getValueType(getContext()); + assert(T->isStructureType()); + + const RecordType* RT = T->getAsRecordType(); + RecordDecl* RD = RT->getDecl(); + + if (!RD->isDefinition()) + return St; + + if (V.isUnknown()) + return KillStruct(St, R); + + nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + RecordDecl::field_iterator FI = RD->field_begin(getContext()), + FE = RD->field_end(getContext()); + + for (; FI != FE; ++FI, ++VI) { + + // There may be fewer values than fields only when we are initializing a + // struct decl. In this case, mark the region as having default value. + if (VI == VE) { + GRStateRef state(St, StateMgr); + const NonLoc& Idx = NonLoc::MakeIntVal(getBasicVals(), 0, false); + St = state.set<RegionDefaultValue>(R, Idx); + break; + } + + QualType FTy = (*FI)->getType(); + FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); + + if (Loc::IsLocType(FTy) || FTy->isIntegerType()) + St = Bind(St, Loc::MakeVal(FR), *VI); + + else if (FTy->isArrayType()) + St = BindArray(St, FR, *VI); + + else if (FTy->isStructureType()) + St = BindStruct(St, FR, *VI); + } + + return St; +} + +const GRState* RegionStoreManager::KillStruct(const GRState* St, + const TypedRegion* R){ + GRStateRef state(St, StateMgr); + + // Kill the struct region because it is assigned "unknown". + St = state.add<RegionKills>(R); + + // Set the default value of the struct region to "unknown". + St = state.set<RegionDefaultValue>(R, UnknownVal()); + + Store store = St->getStore(); + RegionBindingsTy B = GetRegionBindings(store); + + // Remove all bindings for the subregions of the struct. + for (RegionBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const MemRegion* r = I.getKey(); + if (const SubRegion* sr = dyn_cast<SubRegion>(r)) + if (sr->isSubRegionOf(R)) + store = Remove(store, Loc::MakeVal(sr)); + // FIXME: Maybe we should also remove the bindings for the "views" of the + // subregions. + } + + return StateMgr.MakeStateWithStore(St, store); +} + +const GRState* RegionStoreManager::AddRegionView(const GRState* St, + const MemRegion* View, + const MemRegion* Base) { + GRStateRef state(St, StateMgr); + + // First, retrieve the region view of the base region. + const RegionViews* d = state.get<RegionViewMap>(Base); + RegionViews L = d ? *d : RVFactory.GetEmptySet(); + + // Now add View to the region view. + L = RVFactory.Add(L, View); + + // Create a new state with the new region view. + return state.set<RegionViewMap>(Base, L); +} + +const GRState* RegionStoreManager::RemoveRegionView(const GRState* St, + const MemRegion* View, + const MemRegion* Base) { + GRStateRef state(St, StateMgr); + + // Retrieve the region view of the base region. + const RegionViews* d = state.get<RegionViewMap>(Base); + + // If the base region has no view, return. + if (!d) + return St; + + // Remove the view. + RegionViews V = *d; + V = RVFactory.Remove(V, View); + + return state.set<RegionViewMap>(Base, V); +} + +const GRState* RegionStoreManager::setCastType(const GRState* St, + const MemRegion* R, QualType T) { + GRStateRef state(St, StateMgr); + return state.set<RegionCasts>(R, T); +} + +const GRState* RegionStoreManager::setDefaultValue(const GRState* St, + const MemRegion* R, SVal V) { + GRStateRef state(St, StateMgr); + return state.set<RegionDefaultValue>(R, V); +} diff --git a/lib/Analysis/SVals.cpp b/lib/Analysis/SVals.cpp new file mode 100644 index 0000000..e19b168 --- /dev/null +++ b/lib/Analysis/SVals.cpp @@ -0,0 +1,513 @@ +//= 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" +#include "llvm/Support/Streams.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; + } else if (const CodeTextRegion *CTR = dyn_cast<CodeTextRegion>(R)) { + if (CTR->isSymbolic()) { + SymbolRef sym = CTR->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 CodeTextRegion* CTR = R->getAs<CodeTextRegion>()) { + if (CTR->isDeclared()) + return CTR->getDecl(); + } + } + + return 0; +} + +/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and +/// wraps a symbol, return that SymbolRef. Otherwise return 0. +// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +SymbolRef SVal::getAsLocSymbol() const { + if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) { + const MemRegion *R = X->getRegion(); + + while (R) { + // Blast through region views. + if (const TypedViewRegion *View = dyn_cast<TypedViewRegion>(R)) { + R = View->getSuperRegion(); + continue; + } + + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) + return SymR->getSymbol(); + + break; + } + } + + return 0; +} + +/// 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(); +} + +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"); +} + +//===----------------------------------------------------------------------===// +// 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::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(BasicValueFactory& BasicVals, + BinaryOperator::Opcode Op, + const nonloc::ConcreteInt& R) const { + + const llvm::APSInt* X = + BasicVals.EvaluateAPSInt(Op, getValue(), R.getValue()); + + if (X) + return nonloc::ConcreteInt(*X); + else + return UndefinedVal(); +} + + // Bitwise-Complement. + +nonloc::ConcreteInt +nonloc::ConcreteInt::EvalComplement(BasicValueFactory& BasicVals) const { + return BasicVals.getValue(~getValue()); +} + + // Unary Minus. + +nonloc::ConcreteInt +nonloc::ConcreteInt::EvalMinus(BasicValueFactory& BasicVals, UnaryOperator* U) const { + assert (U->getType() == U->getSubExpr()->getType()); + assert (U->getType()->isIntegerType()); + return BasicVals.getValue(-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(); +} + +//===----------------------------------------------------------------------===// +// Utility methods for constructing SVals. +//===----------------------------------------------------------------------===// + +SVal ValueManager::makeZeroVal(QualType T) { + if (Loc::IsLocType(T)) + return Loc::MakeNull(BasicVals); + + if (T->isIntegerType()) + return NonLoc::MakeVal(BasicVals, 0, T); + + // FIXME: Handle floats. + // FIXME: Handle structs. + return UnknownVal(); +} + +SVal ValueManager::makeZeroArrayIndex() { + return nonloc::ConcreteInt(BasicVals.getZeroWithPtrWidth(false)); +} + +//===----------------------------------------------------------------------===// +// Utility methods for constructing Non-Locs. +//===----------------------------------------------------------------------===// + +NonLoc ValueManager::makeNonLoc(SymbolRef sym) { + return nonloc::SymbolVal(sym); +} + +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)); +} + +NonLoc NonLoc::MakeIntVal(BasicValueFactory& BasicVals, uint64_t X, + bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getIntValue(X, isUnsigned)); +} + +NonLoc NonLoc::MakeVal(BasicValueFactory& BasicVals, uint64_t X, + unsigned BitWidth, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getValue(X, BitWidth, isUnsigned)); +} + +NonLoc NonLoc::MakeVal(BasicValueFactory& BasicVals, uint64_t X, QualType T) { + return nonloc::ConcreteInt(BasicVals.getValue(X, T)); +} + +NonLoc NonLoc::MakeVal(BasicValueFactory& BasicVals, IntegerLiteral* I) { + + return nonloc::ConcreteInt(BasicVals.getValue(APSInt(I->getValue(), + I->getType()->isUnsignedIntegerType()))); +} + +NonLoc NonLoc::MakeVal(BasicValueFactory& BasicVals, const llvm::APInt& I, + bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getValue(I, isUnsigned)); +} + +NonLoc NonLoc::MakeVal(BasicValueFactory& BasicVals, const llvm::APSInt& I) { + return nonloc::ConcreteInt(BasicVals.getValue(I)); +} + +NonLoc NonLoc::MakeIntTruthVal(BasicValueFactory& BasicVals, bool b) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b)); +} + +NonLoc ValueManager::makeTruthVal(bool b, QualType T) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b, T)); +} + +NonLoc NonLoc::MakeCompoundVal(QualType T, llvm::ImmutableList<SVal> Vals, + BasicValueFactory& BasicVals) { + return nonloc::CompoundVal(BasicVals.getCompoundValData(T, Vals)); +} + +SVal ValueManager::getRegionValueSymbolVal(const MemRegion* R) { + SymbolRef sym = SymMgr.getRegionValueSymbol(R); + + if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) { + QualType T = TR->getValueType(SymMgr.getContext()); + + // If T is of function pointer type, create a CodeTextRegion wrapping a + // symbol. + if (T->isFunctionPointerType()) { + return Loc::MakeVal(MemMgr.getCodeTextRegion(sym, T)); + } + + if (Loc::IsLocType(T)) + return Loc::MakeVal(MemMgr.getSymbolicRegion(sym)); + + // Only handle integers for now. + if (T->isIntegerType() && T->isScalarType()) + return makeNonLoc(sym); + } + + return UnknownVal(); +} + +SVal ValueManager::getConjuredSymbolVal(const Expr* E, unsigned Count) { + QualType T = E->getType(); + SymbolRef sym = SymMgr.getConjuredSymbol(E, Count); + + // If T is of function pointer type, create a CodeTextRegion wrapping a + // symbol. + if (T->isFunctionPointerType()) { + return Loc::MakeVal(MemMgr.getCodeTextRegion(sym, T)); + } + + if (Loc::IsLocType(T)) + return Loc::MakeVal(MemMgr.getSymbolicRegion(sym)); + + if (T->isIntegerType() && T->isScalarType()) + return makeNonLoc(sym); + + return UnknownVal(); +} + +SVal ValueManager::getConjuredSymbolVal(const Expr* E, QualType T, + unsigned Count) { + + SymbolRef sym = SymMgr.getConjuredSymbol(E, T, Count); + + // If T is of function pointer type, create a CodeTextRegion wrapping a + // symbol. + if (T->isFunctionPointerType()) { + return Loc::MakeVal(MemMgr.getCodeTextRegion(sym, T)); + } + + if (Loc::IsLocType(T)) + return Loc::MakeVal(MemMgr.getSymbolicRegion(sym)); + + if (T->isIntegerType() && T->isScalarType()) + return makeNonLoc(sym); + + return UnknownVal(); +} + +SVal ValueManager::getFunctionPointer(const FunctionDecl* FD) { + CodeTextRegion* R + = MemMgr.getCodeTextRegion(FD, Context.getPointerType(FD->getType())); + return loc::MemRegionVal(R); +} + +nonloc::LocAsInteger nonloc::LocAsInteger::Make(BasicValueFactory& Vals, Loc V, + unsigned Bits) { + return LocAsInteger(Vals.getPersistentSValWithData(V, Bits)); +} + +//===----------------------------------------------------------------------===// +// Utility methods for constructing Locs. +//===----------------------------------------------------------------------===// + +Loc Loc::MakeVal(const MemRegion* R) { return loc::MemRegionVal(R); } + +Loc Loc::MakeVal(AddrLabelExpr* E) { return loc::GotoLabel(E->getLabel()); } + +Loc Loc::MakeNull(BasicValueFactory &BasicVals) { + return loc::ConcreteInt(BasicVals.getZeroWithPtrWidth()); +} + +//===----------------------------------------------------------------------===// +// Pretty-Printing. +//===----------------------------------------------------------------------===// + +void SVal::printStdErr() const { print(llvm::errs()); } + +void SVal::print(std::ostream& Out) const { + llvm::raw_os_ostream out(Out); + print(out); +} + +void SVal::print(llvm::raw_ostream& Out) const { + + switch (getBaseKind()) { + + case UnknownKind: + Out << "Invalid"; break; + + case NonLocKind: + cast<NonLoc>(this)->print(Out); break; + + case LocKind: + cast<Loc>(this)->print(Out); break; + + case UndefinedKind: + Out << "Undefined"; break; + + default: + assert (false && "Invalid SVal."); + } +} + +void NonLoc::print(llvm::raw_ostream& Out) const { + + switch (getSubKind()) { + + case nonloc::ConcreteIntKind: + Out << cast<nonloc::ConcreteInt>(this)->getValue().getZExtValue(); + + if (cast<nonloc::ConcreteInt>(this)->getValue().isUnsigned()) + Out << 'U'; + + break; + + case nonloc::SymbolValKind: + Out << '$' << cast<nonloc::SymbolVal>(this)->getSymbol(); + break; + + case nonloc::SymExprValKind: { + const nonloc::SymExprVal& C = *cast<nonloc::SymExprVal>(this); + const SymExpr *SE = C.getSymbolicExpression(); + Out << SE; + break; + } + + case nonloc::LocAsIntegerKind: { + const nonloc::LocAsInteger& C = *cast<nonloc::LocAsInteger>(this); + C.getLoc().print(Out); + Out << " [as " << C.getNumBits() << " bit integer]"; + break; + } + + case nonloc::CompoundValKind: { + const nonloc::CompoundVal& C = *cast<nonloc::CompoundVal>(this); + Out << " {"; + bool first = true; + for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { + if (first) { Out << ' '; first = false; } + else Out << ", "; + (*I).print(Out); + } + Out << " }"; + break; + } + + default: + assert (false && "Pretty-printed not implemented for this NonLoc."); + break; + } +} + +void Loc::print(llvm::raw_ostream& Out) const { + + switch (getSubKind()) { + + case loc::ConcreteIntKind: + Out << cast<loc::ConcreteInt>(this)->getValue().getZExtValue() + << " (Loc)"; + break; + + case loc::GotoLabelKind: + Out << "&&" + << cast<loc::GotoLabel>(this)->getLabel()->getID()->getName(); + break; + + case loc::MemRegionKind: + Out << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString(); + break; + + default: + assert (false && "Pretty-printing not implemented for this Loc."); + break; + } +} diff --git a/lib/Analysis/SimpleConstraintManager.cpp b/lib/Analysis/SimpleConstraintManager.cpp new file mode 100644 index 0000000..f79dba0 --- /dev/null +++ b/lib/Analysis/SimpleConstraintManager.cpp @@ -0,0 +1,263 @@ +//== 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" + +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* St, SVal Cond, bool Assumption, + bool& isFeasible) { + if (Cond.isUnknown()) { + isFeasible = true; + return St; + } + + if (isa<NonLoc>(Cond)) + return Assume(St, cast<NonLoc>(Cond), Assumption, isFeasible); + else + return Assume(St, cast<Loc>(Cond), Assumption, isFeasible); +} + +const GRState* +SimpleConstraintManager::Assume(const GRState* St, Loc Cond, bool Assumption, + bool& isFeasible) { + St = AssumeAux(St, Cond, Assumption, isFeasible); + + if (!isFeasible) + return St; + + // EvalAssume is used to call into the GRTransferFunction object to perform + // any checker-specific update of the state based on this assumption being + // true or false. + return StateMgr.getTransferFuncs().EvalAssume(StateMgr, St, Cond, Assumption, + isFeasible); +} + +const GRState* +SimpleConstraintManager::AssumeAux(const GRState* St, Loc Cond, bool Assumption, + bool& isFeasible) { + BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + + switch (Cond.getSubKind()) { + default: + assert (false && "'Assume' not implemented for this Loc."); + return St; + + 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(St, SymR->getSymbol(), + BasicVals.getZeroWithPtrWidth(), isFeasible); + else + return AssumeSymEQ(St, SymR->getSymbol(), + BasicVals.getZeroWithPtrWidth(), isFeasible); + } + SubR = dyn_cast<SubRegion>(SubR->getSuperRegion()); + } + + // FALL-THROUGH. + } + + case loc::GotoLabelKind: + isFeasible = Assumption; + return St; + + case loc::ConcreteIntKind: { + bool b = cast<loc::ConcreteInt>(Cond).getValue() != 0; + isFeasible = b ? Assumption : !Assumption; + return St; + } + } // end switch +} + +const GRState* +SimpleConstraintManager::Assume(const GRState* St, NonLoc Cond, bool Assumption, + bool& isFeasible) { + St = AssumeAux(St, Cond, Assumption, isFeasible); + + if (!isFeasible) + return St; + + // EvalAssume is used to call into the GRTransferFunction object to perform + // any checker-specific update of the state based on this assumption being + // true or false. + return StateMgr.getTransferFuncs().EvalAssume(StateMgr, St, Cond, Assumption, + isFeasible); +} + +const GRState* +SimpleConstraintManager::AssumeAux(const GRState* St,NonLoc Cond, + bool Assumption, bool& isFeasible) { + // We cannot reason about SymIntExpr and SymSymExpr. + if (!canReasonAbout(Cond)) { + isFeasible = true; + return St; + } + + BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + SymbolManager& SymMgr = StateMgr.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); + + if (Assumption) + return AssumeSymNE(St, sym, BasicVals.getValue(0, T), isFeasible); + else + return AssumeSymEQ(St, sym, BasicVals.getValue(0, T), isFeasible); + } + + case nonloc::SymExprValKind: { + nonloc::SymExprVal V = cast<nonloc::SymExprVal>(Cond); + if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(V.getSymbolicExpression())) + return AssumeSymInt(St, Assumption, SE, isFeasible); + + isFeasible = true; + return St; + } + + case nonloc::ConcreteIntKind: { + bool b = cast<nonloc::ConcreteInt>(Cond).getValue() != 0; + isFeasible = b ? Assumption : !Assumption; + return St; + } + + case nonloc::LocAsIntegerKind: + return AssumeAux(St, cast<nonloc::LocAsInteger>(Cond).getLoc(), + Assumption, isFeasible); + } // end switch +} + +const GRState* +SimpleConstraintManager::AssumeSymInt(const GRState* St, bool Assumption, + const SymIntExpr *SE, bool& isFeasible) { + + + // 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. + isFeasible = true; + return St; + + case BinaryOperator::EQ: + return Assumption ? AssumeSymEQ(St, Sym, Int, isFeasible) + : AssumeSymNE(St, Sym, Int, isFeasible); + + case BinaryOperator::NE: + return Assumption ? AssumeSymNE(St, Sym, Int, isFeasible) + : AssumeSymEQ(St, Sym, Int, isFeasible); + + case BinaryOperator::GT: + return Assumption ? AssumeSymGT(St, Sym, Int, isFeasible) + : AssumeSymLE(St, Sym, Int, isFeasible); + + case BinaryOperator::GE: + return Assumption ? AssumeSymGE(St, Sym, Int, isFeasible) + : AssumeSymLT(St, Sym, Int, isFeasible); + + case BinaryOperator::LT: + return Assumption ? AssumeSymLT(St, Sym, Int, isFeasible) + : AssumeSymGE(St, Sym, Int, isFeasible); + + case BinaryOperator::LE: + return Assumption ? AssumeSymLE(St, Sym, Int, isFeasible) + : AssumeSymGT(St, Sym, Int, isFeasible); + } // end switch +} + +const GRState* +SimpleConstraintManager::AssumeInBound(const GRState* St, SVal Idx, + SVal UpperBound, bool Assumption, + bool& isFeasible) { + // Only support ConcreteInt for now. + if (!(isa<nonloc::ConcreteInt>(Idx) && isa<nonloc::ConcreteInt>(UpperBound))){ + isFeasible = true; + return St; + } + + const llvm::APSInt& Zero = 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); + + isFeasible = Assumption ? InBound : !InBound; + + return St; +} + +} // end of namespace clang diff --git a/lib/Analysis/SimpleConstraintManager.h b/lib/Analysis/SimpleConstraintManager.h new file mode 100644 index 0000000..fb41e2f --- /dev/null +++ b/lib/Analysis/SimpleConstraintManager.h @@ -0,0 +1,84 @@ +//== 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 { +protected: + GRStateManager& StateMgr; +public: + SimpleConstraintManager(GRStateManager& statemgr) + : StateMgr(statemgr) {} + virtual ~SimpleConstraintManager(); + + bool canReasonAbout(SVal X) const; + + virtual const GRState* Assume(const GRState* St, SVal Cond, bool Assumption, + bool& isFeasible); + + const GRState* Assume(const GRState* St, Loc Cond, bool Assumption, + bool& isFeasible); + + const GRState* AssumeAux(const GRState* St, Loc Cond,bool Assumption, + bool& isFeasible); + + const GRState* Assume(const GRState* St, NonLoc Cond, bool Assumption, + bool& isFeasible); + + const GRState* AssumeAux(const GRState* St, NonLoc Cond, bool Assumption, + bool& isFeasible); + + const GRState* AssumeSymInt(const GRState* St, bool Assumption, + const SymIntExpr *SE, bool& isFeasible); + + virtual const GRState* AssumeSymNE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, + bool& isFeasible) = 0; + + virtual const GRState* AssumeSymEQ(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, + bool& isFeasible) = 0; + + virtual const GRState* AssumeSymLT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, + bool& isFeasible) = 0; + + virtual const GRState* AssumeSymGT(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, + bool& isFeasible) = 0; + + virtual const GRState* AssumeSymLE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, + bool& isFeasible) = 0; + + virtual const GRState* AssumeSymGE(const GRState* St, SymbolRef sym, + const llvm::APSInt& V, + bool& isFeasible) = 0; + + const GRState* AssumeInBound(const GRState* St, SVal Idx, SVal UpperBound, + bool Assumption, bool& isFeasible); + +private: + BasicValueFactory& getBasicVals() { return StateMgr.getBasicVals(); } + SymbolManager& getSymbolManager() const { return StateMgr.getSymbolManager(); } +}; + +} // end clang namespace + +#endif diff --git a/lib/Analysis/Store.cpp b/lib/Analysis/Store.cpp new file mode 100644 index 0000000..13326ab --- /dev/null +++ b/lib/Analysis/Store.cpp @@ -0,0 +1,110 @@ +//== 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" + +using namespace clang; + +StoreManager::StoreManager(GRStateManager &stateMgr) + : ValMgr(stateMgr.getValueManager()), + StateMgr(stateMgr), + MRMgr(ValMgr.getRegionManager()) {} + +StoreManager::CastResult +StoreManager::CastRegion(const GRState* state, const MemRegion* R, + QualType CastToTy) { + + ASTContext& Ctx = StateMgr.getContext(); + + // We need to know the real type of CastToTy. + QualType ToTy = Ctx.getCanonicalType(CastToTy); + + // Return the same region if the region types are compatible. + if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) { + QualType Ta = Ctx.getCanonicalType(TR->getLocationType(Ctx)); + + if (Ta == ToTy) + return CastResult(state, R); + } + + if (const PointerType* PTy = dyn_cast<PointerType>(ToTy.getTypePtr())) { + // Check if we are casting to 'void*'. + // FIXME: Handle arbitrary upcasts. + QualType Pointee = PTy->getPointeeType(); + if (Pointee->isVoidType()) { + + do { + if (const TypedViewRegion *TR = dyn_cast<TypedViewRegion>(R)) { + // Casts to void* removes TypedViewRegion. This happens when: + // + // void foo(void*); + // ... + // void bar() { + // int x; + // foo(&x); + // } + // + R = TR->removeViews(); + continue; + } + else if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + // Casts to void* also removes ElementRegions. This happens when: + // + // void foo(void*); + // ... + // void bar() { + // int x; + // foo((char*)&x); + // } + // + R = ER->getSuperRegion(); + continue; + } + else + break; + } + while (0); + + return CastResult(state, R); + } + else if (Pointee->isIntegerType()) { + // FIXME: At some point, it stands to reason that this 'dyn_cast' should + // become a 'cast' and that 'R' will always be a TypedRegion. + if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { + // Check if we are casting to a region with an integer type. We now + // the types aren't the same, so we construct an ElementRegion. + SVal Idx = ValMgr.makeZeroArrayIndex(); + + // If the super region is an element region, strip it away. + // FIXME: Is this the right thing to do in all cases? + const TypedRegion *Base = isa<ElementRegion>(TR) ? + cast<TypedRegion>(TR->getSuperRegion()) : TR; + ElementRegion* ER = MRMgr.getElementRegion(Pointee, Idx, Base); + return CastResult(state, ER); + } + } + } + + // FIXME: Need to handle arbitrary downcasts. + // FIXME: Handle the case where a TypedViewRegion (layering a SymbolicRegion + // or an AllocaRegion is cast to another view, thus causing the memory + // to be re-used for a different purpose. + + if (isa<SymbolicRegion>(R) || isa<AllocaRegion>(R)) { + const MemRegion* ViewR = MRMgr.getTypedViewRegion(CastToTy, R); + return CastResult(AddRegionView(state, ViewR, R), ViewR); + } + + return CastResult(state, R); +} diff --git a/lib/Analysis/SymbolManager.cpp b/lib/Analysis/SymbolManager.cpp new file mode 100644 index 0000000..5c885cd --- /dev/null +++ b/lib/Analysis/SymbolManager.cpp @@ -0,0 +1,203 @@ +//== 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; + +static void print(llvm::raw_ostream& os, const SymExpr *SE); + +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; + } +} + +static void print(llvm::raw_ostream& os, const SymIntExpr *SE) { + os << '('; + print(os, SE->getLHS()); + os << ") "; + print(os, SE->getOpcode()); + os << ' ' << SE->getRHS().getZExtValue(); + if (SE->getRHS().isUnsigned()) os << 'U'; +} + +static void print(llvm::raw_ostream& os, const SymSymExpr *SE) { + os << '('; + print(os, SE->getLHS()); + os << ") "; + os << '('; + print(os, SE->getRHS()); + os << ')'; +} + +static void print(llvm::raw_ostream& os, const SymExpr *SE) { + switch (SE->getKind()) { + case SymExpr::BEGIN_SYMBOLS: + case SymExpr::RegionValueKind: + case SymExpr::ConjuredKind: + case SymExpr::END_SYMBOLS: + os << '$' << cast<SymbolData>(SE)->getSymbolID(); + return; + case SymExpr::SymIntKind: + print(os, cast<SymIntExpr>(SE)); + return; + case SymExpr::SymSymKind: + print(os, cast<SymSymExpr>(SE)); + return; + } +} + + +llvm::raw_ostream& llvm::operator<<(llvm::raw_ostream& os, const SymExpr *SE) { + print(os, SE); + return os; +} + +std::ostream& std::operator<<(std::ostream& os, const SymExpr *SE) { + llvm::raw_os_ostream O(os); + print(O, SE); + return os; +} + +const SymbolRegionValue* +SymbolManager::getRegionValueSymbol(const MemRegion* R) { + llvm::FoldingSetNodeID profile; + SymbolRegionValue::Profile(profile, R); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>(); + new (SD) SymbolRegionValue(SymbolCounter, R); + 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 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 SymbolRegionValue::getType(ASTContext& C) const { + 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(); +} + +void SymbolReaper::markLive(SymbolRef sym) { + TheLiving = F.Add(TheLiving, sym); + TheDead = F.Remove(TheDead, sym); +} + +bool SymbolReaper::maybeDead(SymbolRef sym) { + if (isLive(sym)) + return false; + + TheDead = F.Add(TheDead, sym); + return true; +} + +bool SymbolReaper::isLive(SymbolRef sym) { + if (TheLiving.contains(sym)) + return true; + + // Interogate the symbol. It may derive from an input value to + // the analyzed function/method. + return isa<SymbolRegionValue>(sym); +} + +SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp new file mode 100644 index 0000000..014ea82 --- /dev/null +++ b/lib/Analysis/UninitializedValues.cpp @@ -0,0 +1,312 @@ +//==- UninitializedValues.cpp - Find Uninitialized 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 Uninitialized Values analysis for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#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" +#include "llvm/Support/Compiler.h" + +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Dataflow initialization logic. +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN RegisterDecls + : public CFGRecStmtDeclVisitor<RegisterDecls> { + + UninitializedValues::AnalysisDataTy& AD; +public: + RegisterDecls(UninitializedValues::AnalysisDataTy& ad) : AD(ad) {} + + void VisitVarDecl(VarDecl* VD) { AD.Register(VD); } + CFG& getCFG() { return AD.getCFG(); } +}; + +} // end anonymous namespace + +void UninitializedValues::InitializeValues(const CFG& cfg) { + RegisterDecls R(getAnalysisData()); + cfg.VisitBlockStmts(R); +} + +//===----------------------------------------------------------------------===// +// Transfer functions. +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN TransferFuncs + : public CFGStmtVisitor<TransferFuncs,bool> { + + UninitializedValues::ValTy V; + UninitializedValues::AnalysisDataTy& AD; +public: + TransferFuncs(UninitializedValues::AnalysisDataTy& ad) : AD(ad) {} + + UninitializedValues::ValTy& getVal() { return V; } + CFG& getCFG() { return AD.getCFG(); } + + void SetTopValue(UninitializedValues::ValTy& X) { + X.setDeclValues(AD); + X.resetBlkExprValues(AD); + } + + bool VisitDeclRefExpr(DeclRefExpr* DR); + bool VisitBinaryOperator(BinaryOperator* B); + bool VisitUnaryOperator(UnaryOperator* U); + bool VisitStmt(Stmt* S); + bool VisitCallExpr(CallExpr* C); + bool VisitDeclStmt(DeclStmt* D); + bool VisitConditionalOperator(ConditionalOperator* C); + bool BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S); + + bool Visit(Stmt *S); + bool BlockStmt_VisitExpr(Expr* E); + + void VisitTerminator(CFGBlock* B) { } +}; + +static const bool Initialized = false; +static const bool Uninitialized = true; + +bool TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) { + + if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) + if (VD->isBlockVarDecl()) { + + if (AD.Observer) + AD.Observer->ObserveDeclRefExpr(V, AD, DR, VD); + + // Pseudo-hack to prevent cascade of warnings. If an accessed variable + // is uninitialized, then we are already going to flag a warning for + // this variable, which a "source" of uninitialized values. + // We can otherwise do a full "taint" of uninitialized values. The + // client has both options by toggling AD.FullUninitTaint. + + if (AD.FullUninitTaint) + return V(VD,AD); + } + + return Initialized; +} + +static VarDecl* FindBlockVarDecl(Expr* E) { + + // Blast through casts and parentheses to find any DeclRefExprs that + // refer to a block VarDecl. + + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) + if (VD->isBlockVarDecl()) return VD; + + return NULL; +} + +bool TransferFuncs::VisitBinaryOperator(BinaryOperator* B) { + + if (VarDecl* VD = FindBlockVarDecl(B->getLHS())) + if (B->isAssignmentOp()) { + if (B->getOpcode() == BinaryOperator::Assign) + return V(VD,AD) = Visit(B->getRHS()); + else // Handle +=, -=, *=, etc. We do want '&', not '&&'. + return V(VD,AD) = Visit(B->getLHS()) & Visit(B->getRHS()); + } + + return VisitStmt(B); +} + +bool TransferFuncs::VisitDeclStmt(DeclStmt* S) { + for (DeclStmt::decl_iterator I=S->decl_begin(), E=S->decl_end(); I!=E; ++I) { + VarDecl *VD = dyn_cast<VarDecl>(*I); + if (VD && VD->isBlockVarDecl()) { + if (Stmt* I = VD->getInit()) + V(VD,AD) = AD.FullUninitTaint ? V(cast<Expr>(I),AD) : Initialized; + else { + // Special case for declarations of array types. For things like: + // + // char x[10]; + // + // we should treat "x" as being initialized, because the variable + // "x" really refers to the memory block. Clearly x[1] is + // uninitialized, but expressions like "(char *) x" really do refer to + // an initialized value. This simple dataflow analysis does not reason + // about the contents of arrays, although it could be potentially + // extended to do so if the array were of constant size. + if (VD->getType()->isArrayType()) + V(VD,AD) = Initialized; + else + V(VD,AD) = Uninitialized; + } + } + } + return Uninitialized; // Value is never consumed. +} + +bool TransferFuncs::VisitCallExpr(CallExpr* C) { + VisitChildren(C); + return Initialized; +} + +bool TransferFuncs::VisitUnaryOperator(UnaryOperator* U) { + switch (U->getOpcode()) { + case UnaryOperator::AddrOf: { + VarDecl* VD = FindBlockVarDecl(U->getSubExpr()); + if (VD && VD->isBlockVarDecl()) + return V(VD,AD) = Initialized; + break; + } + + default: + break; + } + + return Visit(U->getSubExpr()); +} + +bool +TransferFuncs::BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S) { + // This represents a use of the 'collection' + bool x = Visit(S->getCollection()); + + if (x == Uninitialized) + return Uninitialized; + + // This represents an initialization of the 'element' value. + Stmt* Element = S->getElement(); + VarDecl* VD = 0; + + if (DeclStmt* DS = dyn_cast<DeclStmt>(Element)) + VD = cast<VarDecl>(DS->getSingleDecl()); + else { + Expr* ElemExpr = cast<Expr>(Element)->IgnoreParens(); + + // Initialize the value of the reference variable. + if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(ElemExpr)) + VD = cast<VarDecl>(DR->getDecl()); + else + return Visit(ElemExpr); + } + + V(VD,AD) = Initialized; + return Initialized; +} + + +bool TransferFuncs::VisitConditionalOperator(ConditionalOperator* C) { + Visit(C->getCond()); + + bool rhsResult = Visit(C->getRHS()); + // Handle the GNU extension for missing LHS. + if (Expr *lhs = C->getLHS()) + return Visit(lhs) & rhsResult; // Yes: we want &, not &&. + else + return rhsResult; +} + +bool TransferFuncs::VisitStmt(Stmt* S) { + bool x = Initialized; + + // We don't stop at the first subexpression that is Uninitialized because + // evaluating some subexpressions may result in propogating "Uninitialized" + // or "Initialized" to variables referenced in the other subexpressions. + for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I) + if (*I && Visit(*I) == Uninitialized) x = Uninitialized; + + return x; +} + +bool TransferFuncs::Visit(Stmt *S) { + if (AD.isTracked(static_cast<Expr*>(S))) return V(static_cast<Expr*>(S),AD); + else return static_cast<CFGStmtVisitor<TransferFuncs,bool>*>(this)->Visit(S); +} + +bool TransferFuncs::BlockStmt_VisitExpr(Expr* E) { + bool x = static_cast<CFGStmtVisitor<TransferFuncs,bool>*>(this)->Visit(E); + if (AD.isTracked(E)) V(E,AD) = x; + return x; +} + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Merge operator. +// +// In our transfer functions we take the approach that any +// combination of uninitialized values, e.g. +// Uninitialized + ___ = Uninitialized. +// +// Merges take the same approach, preferring soundness. At a confluence point, +// if any predecessor has a variable marked uninitialized, the value is +// uninitialized at the confluence point. +//===----------------------------------------------------------------------===// + +namespace { + typedef StmtDeclBitVector_Types::Union Merge; + typedef DataflowSolver<UninitializedValues,TransferFuncs,Merge> Solver; +} + +//===----------------------------------------------------------------------===// +// Uninitialized values checker. Scan an AST and flag variable uses +//===----------------------------------------------------------------------===// + +UninitializedValues_ValueTypes::ObserverTy::~ObserverTy() {} + +namespace { +class VISIBILITY_HIDDEN UninitializedValuesChecker + : public UninitializedValues::ObserverTy { + + ASTContext &Ctx; + Diagnostic &Diags; + llvm::SmallPtrSet<VarDecl*,10> AlreadyWarned; + +public: + UninitializedValuesChecker(ASTContext &ctx, Diagnostic &diags) + : Ctx(ctx), Diags(diags) {} + + virtual void ObserveDeclRefExpr(UninitializedValues::ValTy& V, + UninitializedValues::AnalysisDataTy& AD, + DeclRefExpr* DR, VarDecl* VD) { + + assert ( AD.isTracked(VD) && "Unknown VarDecl."); + + if (V(VD,AD) == Uninitialized) + if (AlreadyWarned.insert(VD)) + Diags.Report(Ctx.getFullLoc(DR->getSourceRange().getBegin()), + diag::warn_uninit_val); + } +}; +} // end anonymous namespace + +namespace clang { +void CheckUninitializedValues(CFG& cfg, ASTContext &Ctx, Diagnostic &Diags, + bool FullUninitTaint) { + + // Compute the uninitialized values information. + UninitializedValues U(cfg); + U.getAnalysisData().FullUninitTaint = FullUninitTaint; + Solver S(U); + S.runOnCFG(cfg); + + // Scan for DeclRefExprs that use uninitialized values. + UninitializedValuesChecker Observer(Ctx,Diags); + U.getAnalysisData().Observer = &Observer; + S.runOnAllBlocks(cfg); +} +} // end namespace clang |