// 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/Analysis/PathSensitive/ValueState.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/LocalCheckers.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/PathSensitive/BugReporter.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/Support/Compiler.h" #include #include using namespace clang; //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { IdentifierInfo* II = &Ctx.Idents.get(name); return Ctx.Selectors.getSelector(0, &II); } //===----------------------------------------------------------------------===// // Symbolic Evaluation of Reference Counting Logic //===----------------------------------------------------------------------===// namespace { enum ArgEffect { IncRef, DecRef, DoNothing }; typedef std::vector > ArgEffects; } namespace llvm { template <> struct FoldingSetTrait { static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) { for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I) { ID.AddInteger(I->first); ID.AddInteger((unsigned) I->second); } } }; } // end llvm namespace namespace { class RetEffect { public: enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2, NotOwnedSymbol = 0x3 }; private: unsigned Data; RetEffect(Kind k, unsigned D) { Data = (D << 2) | (unsigned) k; } public: Kind getKind() const { return (Kind) (Data & 0x3); } unsigned getValue() const { assert(getKind() == Alias); return Data >> 2; } static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); } static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); } static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); } static RetEffect MakeNoRet() { return RetEffect(NoRet, 0); } operator Kind() const { return getKind(); } void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); } }; class CFRefSummary : public llvm::FoldingSetNode { ArgEffects* Args; RetEffect Ret; public: CFRefSummary(ArgEffects* A, RetEffect R) : Args(A), Ret(R) {} unsigned getNumArgs() const { return Args->size(); } ArgEffect getArg(unsigned idx) const { if (!Args) return DoNothing; // If Args is present, it is likely to contain only 1 element. // Just do a linear search. Do it from the back because functions with // large numbers of arguments will be tail heavy with respect to which // argument they actually modify with respect to the reference count. for (ArgEffects::reverse_iterator I=Args->rbegin(), E=Args->rend(); I!=E; ++I) { if (idx > I->first) return DoNothing; if (idx == I->first) return I->second; } return DoNothing; } RetEffect getRet() const { return Ret; } typedef ArgEffects::const_iterator arg_iterator; arg_iterator begin_args() const { return Args->begin(); } arg_iterator end_args() const { return Args->end(); } static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A, RetEffect R) { ID.AddPointer(A); ID.Add(R); } void Profile(llvm::FoldingSetNodeID& ID) const { Profile(ID, Args, Ret); } }; class CFRefSummaryManager { typedef llvm::FoldingSet > AESetTy; typedef llvm::FoldingSet SummarySetTy; typedef llvm::DenseMap SummaryMapTy; ASTContext& Ctx; const bool GCEnabled; SummarySetTy SummarySet; SummaryMapTy SummaryMap; AESetTy AESet; llvm::BumpPtrAllocator BPAlloc; ArgEffects ScratchArgs; ArgEffects* getArgEffects(); enum CFUnaryFunc { cfretain, cfrelease, cfmakecollectable }; CFRefSummary* getUnaryCFSummary(FunctionTypeProto* FT, CFUnaryFunc func); CFRefSummary* getCFSummary(FunctionDecl* FD, const char* FName); CFRefSummary* getCFSummaryCreateRule(FunctionTypeProto* FT); CFRefSummary* getCFSummaryGetRule(FunctionTypeProto* FT); CFRefSummary* getPersistentSummary(ArgEffects* AE, RetEffect RE); public: CFRefSummaryManager(ASTContext& ctx, bool gcenabled) : Ctx(ctx), GCEnabled(gcenabled) {} ~CFRefSummaryManager(); CFRefSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx); }; } // end anonymous namespace //===----------------------------------------------------------------------===// // Implementation of checker data structures. //===----------------------------------------------------------------------===// CFRefSummaryManager::~CFRefSummaryManager() { // FIXME: The ArgEffects could eventually be allocated from BPAlloc, // mitigating the need to do explicit cleanup of the // Argument-Effect summaries. for (AESetTy::iterator I = AESet.begin(), E = AESet.end(); I!=E; ++I) I->getValue().~ArgEffects(); } ArgEffects* CFRefSummaryManager::getArgEffects() { if (ScratchArgs.empty()) return NULL; // Compute a profile for a non-empty ScratchArgs. llvm::FoldingSetNodeID profile; profile.Add(ScratchArgs); void* InsertPos; // Look up the uniqued copy, or create a new one. llvm::FoldingSetNodeWrapper* E = AESet.FindNodeOrInsertPos(profile, InsertPos); if (E) { ScratchArgs.clear(); return &E->getValue(); } E = (llvm::FoldingSetNodeWrapper*) BPAlloc.Allocate >(); new (E) llvm::FoldingSetNodeWrapper(ScratchArgs); AESet.InsertNode(E, InsertPos); ScratchArgs.clear(); return &E->getValue(); } CFRefSummary* CFRefSummaryManager::getPersistentSummary(ArgEffects* AE, RetEffect RE) { // Generate a profile for the summary. llvm::FoldingSetNodeID profile; CFRefSummary::Profile(profile, AE, RE); // Look up the uniqued summary, or create one if it doesn't exist. void* InsertPos; CFRefSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos); if (Summ) return Summ; // Create the summary and return it. Summ = (CFRefSummary*) BPAlloc.Allocate(); new (Summ) CFRefSummary(AE, RE); SummarySet.InsertNode(Summ, InsertPos); return Summ; } CFRefSummary* CFRefSummaryManager::getSummary(FunctionDecl* FD, ASTContext& Ctx) { SourceLocation Loc = FD->getLocation(); if (!Loc.isFileID()) return NULL; // Look up a summary in our cache of FunctionDecls -> Summaries. SummaryMapTy::iterator I = SummaryMap.find(FD); if (I != SummaryMap.end()) return I->second; // No summary. Generate one. const char* FName = FD->getIdentifier()->getName(); if (FName[0] == 'C' && FName[1] == 'F') { CFRefSummary* S = getCFSummary(FD, FName); SummaryMap[FD] = S; return S; } // Function has no ref-count effects. Return the NULL summary. return NULL; } CFRefSummary* CFRefSummaryManager::getCFSummary(FunctionDecl* FD, const char* FName) { // For now, only generate summaries for functions that have a prototype. FunctionTypeProto* FT = dyn_cast(FD->getType().getTypePtr()); if (!FT) return NULL; FName += 2; if (strcmp(FName, "Retain") == 0) return getUnaryCFSummary(FT, cfretain); if (strcmp(FName, "Release") == 0) return getUnaryCFSummary(FT, cfrelease); if (strcmp(FName, "MakeCollectable") == 0) return getUnaryCFSummary(FT, cfmakecollectable); assert (ScratchArgs.empty()); bool usesCreateRule = false; if (strstr(FName, "Create")) usesCreateRule = true; if (!usesCreateRule && strstr(FName, "Copy")) usesCreateRule = true; if (usesCreateRule) return getCFSummaryCreateRule(FT); if (strstr(FName, "Get")) return getCFSummaryGetRule(FT); return NULL; } CFRefSummary* CFRefSummaryManager::getUnaryCFSummary(FunctionTypeProto* FT, CFUnaryFunc func) { if (FT->getNumArgs() != 1) return NULL; TypedefType* ArgT = dyn_cast(FT->getArgType(0).getTypePtr()); if (!ArgT) return NULL; // For CFRetain/CFRelease, the first (and only) argument is of type // "CFTypeRef". const char* TDName = ArgT->getDecl()->getIdentifier()->getName(); assert (TDName); if (strcmp("CFTypeRef", TDName) != 0) return NULL; if (!ArgT->isPointerType()) return NULL; QualType RetTy = FT->getResultType(); switch (func) { case cfretain: { // CFRetain: the return type should also be "CFTypeRef". if (RetTy.getTypePtr() != ArgT) return NULL; // The function's interface checks out. Generate a canned summary. assert (ScratchArgs.empty()); ScratchArgs.push_back(std::make_pair(0, IncRef)); return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0)); } case cfrelease: { // CFRelease: the return type should be void. if (RetTy != Ctx.VoidTy) return NULL; assert (ScratchArgs.empty()); ScratchArgs.push_back(std::make_pair(0, DecRef)); return getPersistentSummary(getArgEffects(), RetEffect::MakeNoRet()); } case cfmakecollectable: { // CFRetain: the return type should also be "CFTypeRef". if (RetTy.getTypePtr() != ArgT) return NULL; // The function's interface checks out. Generate a canned summary. assert (ScratchArgs.empty()); if (GCEnabled) ScratchArgs.push_back(std::make_pair(0, DecRef)); return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0)); } default: assert (false && "Not a support unary function."); } } static bool isCFRefType(QualType T) { if (!T->isPointerType()) return false; // Check the typedef for the name "CF" and the substring "Ref". TypedefType* TD = dyn_cast(T.getTypePtr()); if (!TD) return false; const char* TDName = TD->getDecl()->getIdentifier()->getName(); assert (TDName); if (TDName[0] != 'C' || TDName[1] != 'F') return false; if (strstr(TDName, "Ref") == 0) return false; return true; } CFRefSummary* CFRefSummaryManager::getCFSummaryCreateRule(FunctionTypeProto* FT) { if (!isCFRefType(FT->getResultType())) return NULL; // FIXME: Add special-cases for functions that retain/release. For now // just handle the default case. assert (ScratchArgs.empty()); return getPersistentSummary(getArgEffects(), RetEffect::MakeOwned()); } CFRefSummary* CFRefSummaryManager::getCFSummaryGetRule(FunctionTypeProto* FT) { QualType RetTy = FT->getResultType(); // FIXME: For now we assume that all pointer types returned are referenced // counted. Since this is the "Get" rule, we assume non-ownership, which // works fine for things that are not reference counted. We do this because // some generic data structures return "void*". We need something better // in the future. if (!isCFRefType(RetTy) && !RetTy->isPointerType()) return NULL; // FIXME: Add special-cases for functions that retain/release. For now // just handle the default case. assert (ScratchArgs.empty()); return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned()); } //===----------------------------------------------------------------------===// // 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. ErrorUseAfterRelease, // Object used after released. ErrorReleaseNotOwned, // Release of an object that was not owned. ErrorLeak // A memory leak due to excessive reference counts. }; private: Kind kind; unsigned Cnt; RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {} RefVal(Kind k) : kind(k), Cnt(0) {} public: Kind getKind() const { return kind; } unsigned getCount() const { return Cnt; } // Useful predicates. static bool isError(Kind k) { return k >= ErrorUseAfterRelease; } static bool isLeak(Kind k) { return k == ErrorLeak; } 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); } // State creation: normal state. static RefVal makeOwned(unsigned Count = 0) { return RefVal(Owned, Count); } static RefVal makeNotOwned(unsigned Count = 0) { return RefVal(NotOwned, Count); } static RefVal makeReturnedOwned(unsigned Count) { return RefVal(ReturnedOwned, Count); } static RefVal makeReturnedNotOwned() { return RefVal(ReturnedNotOwned); } // State creation: errors. static RefVal makeLeak() { return RefVal(ErrorLeak); } static RefVal makeReleased() { return RefVal(Released); } static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); } static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); } // Comparison, profiling, and pretty-printing. bool operator==(const RefVal& X) const { return kind == X.kind && Cnt == X.Cnt; } void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger((unsigned) kind); ID.AddInteger(Cnt); } void print(std::ostream& Out) const; }; void RefVal::print(std::ostream& Out) const { 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 ErrorLeak: Out << "Leaked"; break; case ErrorUseAfterRelease: Out << "Use-After-Release [ERROR]"; break; case ErrorReleaseNotOwned: Out << "Release of Not-Owned [ERROR]"; break; } } //===----------------------------------------------------------------------===// // Transfer functions. //===----------------------------------------------------------------------===// class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals { public: // Type definitions. typedef llvm::ImmutableMap RefBindings; typedef RefBindings::Factory RefBFactoryTy; typedef llvm::DenseMap > ReleasesNotOwnedTy; typedef ReleasesNotOwnedTy UseAfterReleasesTy; typedef llvm::DenseMap*> LeaksTy; class BindingsPrinter : public ValueState::CheckerStatePrinter { public: virtual void PrintCheckerState(std::ostream& Out, void* State, const char* nl, const char* sep); }; private: // Instance variables. CFRefSummaryManager Summaries; const bool GCEnabled; const LangOptions& LOpts; RefBFactoryTy RefBFactory; UseAfterReleasesTy UseAfterReleases; ReleasesNotOwnedTy ReleasesNotOwned; LeaksTy Leaks; BindingsPrinter Printer; Selector RetainSelector; Selector ReleaseSelector; Selector AutoreleaseSelector; public: static RefBindings GetRefBindings(ValueState& StImpl) { return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState); } private: static void SetRefBindings(ValueState& StImpl, RefBindings B) { StImpl.CheckerState = B.getRoot(); } RefBindings Remove(RefBindings B, SymbolID sym) { return RefBFactory.Remove(B, sym); } RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E, RefVal::Kind& hasErr); void ProcessNonLeakError(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, Expr* NodeExpr, Expr* ErrorExpr, ExplodedNode* Pred, ValueState* St, RefVal::Kind hasErr, SymbolID Sym); ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St, SymbolID sid, RefVal V, bool& hasLeak); ValueState* NukeBinding(ValueStateManager& VMgr, ValueState* St, SymbolID sid); public: CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) : Summaries(Ctx, gcenabled), GCEnabled(gcenabled), LOpts(lopts), RetainSelector(GetNullarySelector("retain", Ctx)), ReleaseSelector(GetNullarySelector("release", Ctx)), AutoreleaseSelector(GetNullarySelector("autorelease", Ctx)) {} virtual ~CFRefCount() { for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I) delete I->second; } virtual void RegisterChecks(GRExprEngine& Eng); virtual ValueState::CheckerStatePrinter* getCheckerStatePrinter() { return &Printer; } bool isGCEnabled() const { return GCEnabled; } const LangOptions& getLangOptions() const { return LOpts; } // Calls. virtual void EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, CallExpr* CE, RVal L, ExplodedNode* Pred); virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, ExplodedNode* Pred); bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, ExplodedNode* Pred); // Stores. virtual void EvalStore(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, Expr* E, ExplodedNode* Pred, ValueState* St, RVal TargetLV, RVal Val); // End-of-path. virtual void EvalEndPath(GRExprEngine& Engine, GREndPathNodeBuilder& Builder); virtual void EvalDeadSymbols(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, ExplodedNode* Pred, Stmt* S, ValueState* St, const ValueStateManager::DeadSymbolsTy& Dead); // Return statements. virtual void EvalReturn(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, ReturnStmt* S, ExplodedNode* Pred); // Assumptions. virtual ValueState* EvalAssume(GRExprEngine& Engine, ValueState* St, RVal Cond, bool Assumption, bool& isFeasible); // Error iterators. typedef UseAfterReleasesTy::iterator use_after_iterator; typedef ReleasesNotOwnedTy::iterator bad_release_iterator; typedef LeaksTy::iterator leaks_iterator; use_after_iterator use_after_begin() { return UseAfterReleases.begin(); } use_after_iterator use_after_end() { return UseAfterReleases.end(); } bad_release_iterator bad_release_begin() { return ReleasesNotOwned.begin(); } bad_release_iterator bad_release_end() { return ReleasesNotOwned.end(); } leaks_iterator leaks_begin() { return Leaks.begin(); } leaks_iterator leaks_end() { return Leaks.end(); } }; } // end anonymous namespace void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out, void* State, const char* nl, const char* sep) { RefBindings B((RefBindings::TreeTy*) State); if (State) Out << sep << nl; for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { Out << (*I).first << " : "; (*I).second.print(Out); Out << nl; } } static inline ArgEffect GetArgE(CFRefSummary* Summ, unsigned idx) { return Summ ? Summ->getArg(idx) : DoNothing; } static inline RetEffect GetRetE(CFRefSummary* Summ) { return Summ ? Summ->getRet() : RetEffect::MakeNoRet(); } void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, Expr* NodeExpr, Expr* ErrorExpr, ExplodedNode* Pred, ValueState* St, RefVal::Kind hasErr, SymbolID Sym) { Builder.BuildSinks = true; GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St); if (!N) return; switch (hasErr) { default: assert(false); case RefVal::ErrorUseAfterRelease: UseAfterReleases[N] = std::make_pair(ErrorExpr, Sym); break; case RefVal::ErrorReleaseNotOwned: ReleasesNotOwned[N] = std::make_pair(ErrorExpr, Sym); break; } } void CFRefCount::EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, CallExpr* CE, RVal L, ExplodedNode* Pred) { ValueStateManager& StateMgr = Eng.getStateManager(); CFRefSummary* Summ = NULL; // Get the summary. if (isa(L)) { lval::FuncVal FV = cast(L); FunctionDecl* FD = FV.getDecl(); Summ = Summaries.getSummary(FD, Eng.getContext()); } // Get the state. ValueState* St = Builder.GetState(Pred); // Evaluate the effects of the call. ValueState StVals = *St; RefVal::Kind hasErr = (RefVal::Kind) 0; // This function has a summary. Evaluate the effect of the arguments. unsigned idx = 0; Expr* ErrorExpr = NULL; SymbolID ErrorSym = 0; for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I, ++idx) { RVal V = StateMgr.GetRVal(St, *I); if (isa(V)) { SymbolID Sym = cast(V).getSymbol(); RefBindings B = GetRefBindings(StVals); if (RefBindings::TreeTy* T = B.SlimFind(Sym)) { B = Update(B, Sym, T->getValue().second, GetArgE(Summ, idx), hasErr); SetRefBindings(StVals, B); if (hasErr) { ErrorExpr = *I; ErrorSym = T->getValue().first; break; } } } else if (isa(V)) { // Nuke all arguments passed by reference. // FIXME: This is basically copy-and-paste from GRSimpleVals. We // should compose behavior, not copy it. StateMgr.Unbind(StVals, cast(V)); } else if (isa(V)) StateMgr.Unbind(StVals, cast(V).getLVal()); } St = StateMgr.getPersistentState(StVals); if (hasErr) { ProcessNonLeakError(Dst, Builder, CE, ErrorExpr, Pred, St, hasErr, ErrorSym); return; } // Finally, consult the summary for the return value. RetEffect RE = GetRetE(Summ); 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. if (CE->getType() != Eng.getContext().VoidTy) { unsigned Count = Builder.getCurrentBlockCount(); SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count); RVal X = CE->getType()->isPointerType() ? cast(lval::SymbolVal(Sym)) : cast(nonlval::SymbolVal(Sym)); St = StateMgr.SetRVal(St, CE, X, Eng.getCFG().isBlkExpr(CE), false); } break; case RetEffect::Alias: { unsigned idx = RE.getValue(); assert (idx < CE->getNumArgs()); RVal V = StateMgr.GetRVal(St, CE->getArg(idx)); St = StateMgr.SetRVal(St, CE, V, Eng.getCFG().isBlkExpr(CE), false); break; } case RetEffect::OwnedSymbol: { unsigned Count = Builder.getCurrentBlockCount(); SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count); ValueState StImpl = *St; RefBindings B = GetRefBindings(StImpl); SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned())); St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl), CE, lval::SymbolVal(Sym), Eng.getCFG().isBlkExpr(CE), false); break; } case RetEffect::NotOwnedSymbol: { unsigned Count = Builder.getCurrentBlockCount(); SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count); ValueState StImpl = *St; RefBindings B = GetRefBindings(StImpl); SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeNotOwned())); St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl), CE, lval::SymbolVal(Sym), Eng.getCFG().isBlkExpr(CE), false); break; } } Builder.MakeNode(Dst, CE, Pred, St); } void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, ExplodedNode* Pred) { if (!EvalObjCMessageExprAux(Dst, Eng, Builder, ME, Pred)) return; // The basic transfer function logic for message expressions does nothing. // We just invalidate all arguments passed in by references. ValueStateManager& StateMgr = Eng.getStateManager(); ValueState* St = Builder.GetState(Pred); RefBindings B = GetRefBindings(*St); for (ObjCMessageExpr::arg_iterator I = ME->arg_begin(), E = ME->arg_end(); I != E; ++I) { RVal V = StateMgr.GetRVal(St, *I); if (isa(V)) { LVal lv = cast(V); // Did the lval bind to a symbol? RVal X = StateMgr.GetRVal(St, lv); if (isa(X)) { SymbolID Sym = cast(V).getSymbol(); B = Remove(B, Sym); // Create a new state with the updated bindings. ValueState StVals = *St; SetRefBindings(StVals, B); St = StateMgr.getPersistentState(StVals); } St = StateMgr.SetRVal(St, cast(V), UnknownVal()); } } Builder.MakeNode(Dst, ME, Pred, St); } bool CFRefCount::EvalObjCMessageExprAux(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, ObjCMessageExpr* ME, ExplodedNode* Pred) { if (GCEnabled) return true; // Handle "toll-free bridging" of calls to "Release" and "Retain". // FIXME: track the underlying object type associated so that we can // flag illegal uses of toll-free bridging (or at least handle it // at casts). Selector S = ME->getSelector(); if (!S.isUnarySelector()) return true; Expr* Receiver = ME->getReceiver(); if (!Receiver) return true; // Check if we are calling "autorelease". enum { IsRelease, IsRetain, IsAutorelease, IsNone } mode = IsNone; if (S == AutoreleaseSelector) mode = IsAutorelease; else if (S == RetainSelector) mode = IsRetain; else if (S == ReleaseSelector) mode = IsRelease; if (mode == IsNone) return true; // We have "retain", "release", or "autorelease". ValueStateManager& StateMgr = Eng.getStateManager(); ValueState* St = Builder.GetState(Pred); RVal V = StateMgr.GetRVal(St, Receiver); // Was the argument something we are not tracking? if (!isa(V)) return true; // Get the bindings. SymbolID Sym = cast(V).getSymbol(); RefBindings B = GetRefBindings(*St); // Find the tracked value. RefBindings::TreeTy* T = B.SlimFind(Sym); if (!T) return true; RefVal::Kind hasErr = (RefVal::Kind) 0; // Update the bindings. switch (mode) { case IsNone: assert(false); case IsRelease: B = Update(B, Sym, T->getValue().second, DecRef, hasErr); break; case IsRetain: B = Update(B, Sym, T->getValue().second, IncRef, hasErr); break; case IsAutorelease: // For now we just stop tracking a value if we see // it sent "autorelease." In the future we can potentially // track the associated pool. B = Remove(B, Sym); break; } // Create a new state with the updated bindings. ValueState StVals = *St; SetRefBindings(StVals, B); St = Eng.SetRVal(StateMgr.getPersistentState(StVals), ME, V); // Create an error node if it exists. if (hasErr) ProcessNonLeakError(Dst, Builder, ME, Receiver, Pred, St, hasErr, Sym); else Builder.MakeNode(Dst, ME, Pred, St); return false; } // Stores. void CFRefCount::EvalStore(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, Expr* E, ExplodedNode* Pred, ValueState* St, RVal TargetLV, RVal Val) { // Check if we have a binding for "Val" and if we are storing it to something // we don't understand or otherwise the value "escapes" the function. if (!isa(Val)) return; // Are we storing to something that causes the value to "escape"? bool escapes = false; if (!isa(TargetLV)) escapes = true; else escapes = cast(TargetLV).getDecl()->hasGlobalStorage(); if (!escapes) return; SymbolID Sym = cast(Val).getSymbol(); RefBindings B = GetRefBindings(*St); RefBindings::TreeTy* T = B.SlimFind(Sym); if (!T) return; // Nuke the binding. St = NukeBinding(Eng.getStateManager(), St, Sym); // Hand of the remaining logic to the parent implementation. GRSimpleVals::EvalStore(Dst, Eng, Builder, E, Pred, St, TargetLV, Val); } ValueState* CFRefCount::NukeBinding(ValueStateManager& VMgr, ValueState* St, SymbolID sid) { ValueState StImpl = *St; RefBindings B = GetRefBindings(StImpl); StImpl.CheckerState = RefBFactory.Remove(B, sid).getRoot(); return VMgr.getPersistentState(StImpl); } // End-of-path. ValueState* CFRefCount::HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St, SymbolID sid, RefVal V, bool& hasLeak) { hasLeak = V.isOwned() || ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0); if (!hasLeak) return NukeBinding(VMgr, St, sid); RefBindings B = GetRefBindings(*St); ValueState StImpl = *St; StImpl.CheckerState = RefBFactory.Add(B, sid, RefVal::makeLeak()).getRoot(); return VMgr.getPersistentState(StImpl); } void CFRefCount::EvalEndPath(GRExprEngine& Eng, GREndPathNodeBuilder& Builder) { ValueState* St = Builder.getState(); RefBindings B = GetRefBindings(*St); llvm::SmallVector Leaked; for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { bool hasLeak = false; St = HandleSymbolDeath(Eng.getStateManager(), St, (*I).first, (*I).second, hasLeak); if (hasLeak) Leaked.push_back((*I).first); } if (Leaked.empty()) return; ExplodedNode* N = Builder.MakeNode(St); if (!N) return; std::vector*& LeaksAtNode = Leaks[N]; assert (!LeaksAtNode); LeaksAtNode = new std::vector(); for (llvm::SmallVector::iterator I=Leaked.begin(), E = Leaked.end(); I != E; ++I) (*LeaksAtNode).push_back(*I); } // Dead symbols. void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, ExplodedNode* Pred, Stmt* S, ValueState* St, const ValueStateManager::DeadSymbolsTy& Dead) { // FIXME: a lot of copy-and-paste from EvalEndPath. Refactor. RefBindings B = GetRefBindings(*St); llvm::SmallVector Leaked; for (ValueStateManager::DeadSymbolsTy::const_iterator I=Dead.begin(), E=Dead.end(); I!=E; ++I) { RefBindings::TreeTy* T = B.SlimFind(*I); if (!T) continue; bool hasLeak = false; St = HandleSymbolDeath(Eng.getStateManager(), St, *I, T->getValue().second, hasLeak); if (hasLeak) Leaked.push_back(*I); } if (Leaked.empty()) return; ExplodedNode* N = Builder.MakeNode(Dst, S, Pred, St); if (!N) return; std::vector*& LeaksAtNode = Leaks[N]; assert (!LeaksAtNode); LeaksAtNode = new std::vector(); for (llvm::SmallVector::iterator I=Leaked.begin(), E = Leaked.end(); I != E; ++I) (*LeaksAtNode).push_back(*I); } // Return statements. void CFRefCount::EvalReturn(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, ReturnStmt* S, ExplodedNode* Pred) { Expr* RetE = S->getRetValue(); if (!RetE) return; ValueStateManager& StateMgr = Eng.getStateManager(); ValueState* St = Builder.GetState(Pred); RVal V = StateMgr.GetRVal(St, RetE); if (!isa(V)) return; // Get the reference count binding (if any). SymbolID Sym = cast(V).getSymbol(); RefBindings B = GetRefBindings(*St); RefBindings::TreeTy* T = B.SlimFind(Sym); if (!T) return; // Change the reference count. RefVal X = T->getValue().second; switch (X.getKind()) { case RefVal::Owned: { unsigned cnt = X.getCount(); X = RefVal::makeReturnedOwned(cnt); break; } case RefVal::NotOwned: { unsigned cnt = X.getCount(); X = cnt ? RefVal::makeReturnedOwned(cnt - 1) : RefVal::makeReturnedNotOwned(); break; } default: // None of the error states should be possible at this point. // A symbol could not have been leaked (yet) if we are returning it // (and thus it is still live), and the other errors are hard errors. assert(false); return; } // Update the binding. ValueState StImpl = *St; StImpl.CheckerState = RefBFactory.Add(B, Sym, X).getRoot(); Builder.MakeNode(Dst, S, Pred, StateMgr.getPersistentState(StImpl)); } // Assumptions. ValueState* CFRefCount::EvalAssume(GRExprEngine& Eng, ValueState* St, RVal 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 = GetRefBindings(*St); if (B.isEmpty()) return St; bool changed = false; 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 (St->getSymVal(I.getKey())) { changed = true; B = RefBFactory.Remove(B, I.getKey()); } } if (!changed) return St; ValueState StImpl = *St; StImpl.CheckerState = B.getRoot(); return Eng.getStateManager().getPersistentState(StImpl); } CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E, RefVal::Kind& hasErr) { // FIXME: This dispatch can potentially be sped up by unifiying it into // a single switch statement. Opt for simplicity for now. switch (E) { default: assert (false && "Unhandled CFRef transition."); case DoNothing: if (!GCEnabled && V.getKind() == RefVal::Released) { V = RefVal::makeUseAfterRelease(); hasErr = V.getKind(); break; } return B; case IncRef: switch (V.getKind()) { default: assert(false); case RefVal::Owned: V = RefVal::makeOwned(V.getCount()+1); break; case RefVal::NotOwned: V = RefVal::makeNotOwned(V.getCount()+1); break; case RefVal::Released: if (GCEnabled) V = RefVal::makeOwned(); else { V = RefVal::makeUseAfterRelease(); hasErr = V.getKind(); } break; } break; case DecRef: switch (V.getKind()) { default: assert (false); case RefVal::Owned: { unsigned Count = V.getCount(); V = Count > 0 ? RefVal::makeOwned(Count - 1) : RefVal::makeReleased(); break; } case RefVal::NotOwned: { unsigned Count = V.getCount(); if (Count > 0) V = RefVal::makeNotOwned(Count - 1); else { V = RefVal::makeReleaseNotOwned(); hasErr = V.getKind(); } break; } case RefVal::Released: V = RefVal::makeUseAfterRelease(); hasErr = V.getKind(); break; } break; } return RefBFactory.Add(B, sym, V); } //===----------------------------------------------------------------------===// // Error reporting. //===----------------------------------------------------------------------===// namespace { //===-------------===// // Bug Descriptions. // //===-------------===// class VISIBILITY_HIDDEN CFRefBug : public BugTypeCacheLocation { protected: CFRefCount& TF; public: CFRefBug(CFRefCount& tf) : TF(tf) {} CFRefCount& getTF() { return TF; } virtual bool ReportRanges() const { return true; } }; class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug { public: UseAfterRelease(CFRefCount& tf) : CFRefBug(tf) {} virtual const char* getName() const { return "Core Foundation: Use-After-Release"; } virtual const char* getDescription() const { return "Reference-counted object is used" " after it is released."; } virtual void EmitWarnings(BugReporter& BR); }; class VISIBILITY_HIDDEN BadRelease : public CFRefBug { public: BadRelease(CFRefCount& tf) : CFRefBug(tf) {} virtual const char* getName() const { return "Core Foundation: Release of non-owned object"; } virtual const char* getDescription() const { return "Incorrect decrement of the reference count of a " "CoreFoundation object: " "The object is not owned at this point by the caller."; } virtual void EmitWarnings(BugReporter& BR); }; class VISIBILITY_HIDDEN Leak : public CFRefBug { public: Leak(CFRefCount& tf) : CFRefBug(tf) {} virtual const char* getName() const { return "Core Foundation: Memory Leak"; } virtual const char* getDescription() const { return "Object leaked."; } virtual void EmitWarnings(BugReporter& BR); virtual void GetErrorNodes(std::vector*>& Nodes); virtual bool ReportRanges() const { return false; } }; //===---------===// // Bug Reports. // //===---------===// class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport { SymbolID Sym; public: CFRefReport(CFRefBug& D, ExplodedNode *n, SymbolID sym) : RangedBugReport(D, n), Sym(sym) {} 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().ReportRanges()) RangedBugReport::getRanges(BR, beg, end); else { beg = 0; end = 0; } } virtual std::pair getExtraDescriptiveText(); virtual PathDiagnosticPiece* VisitNode(ExplodedNode* N, ExplodedNode* PrevN, ExplodedGraph& G, BugReporter& BR); }; } // end anonymous namespace void CFRefCount::RegisterChecks(GRExprEngine& Eng) { GRSimpleVals::RegisterChecks(Eng); Eng.Register(new UseAfterRelease(*this)); Eng.Register(new BadRelease(*this)); Eng.Register(new Leak(*this)); } static const char* Msgs[] = { "Code is compiled in garbage collection only mode" // GC only " (the bug occurs with garbage collection enabled).", "Code is compiled without garbage collection.", // No GC. "Code is compiled for use with and without garbage collection (GC)." " The bug occurs with GC enabled.", // Hybrid, with GC. "Code is compiled for use with and without garbage collection (GC)." " The bug occurs in non-GC mode." // Hyrbird, without GC/ }; std::pair CFRefReport::getExtraDescriptiveText() { CFRefCount& TF = static_cast(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); } } PathDiagnosticPiece* CFRefReport::VisitNode(ExplodedNode* N, ExplodedNode* PrevN, ExplodedGraph& G, BugReporter& BR) { // Check if the type state has changed. ValueState* PrevSt = PrevN->getState(); ValueState* CurrSt = N->getState(); CFRefCount::RefBindings PrevB = CFRefCount::GetRefBindings(*PrevSt); CFRefCount::RefBindings CurrB = CFRefCount::GetRefBindings(*CurrSt); CFRefCount::RefBindings::TreeTy* PrevT = PrevB.SlimFind(Sym); CFRefCount::RefBindings::TreeTy* CurrT = CurrB.SlimFind(Sym); if (!CurrT) return NULL; const char* Msg = NULL; RefVal CurrV = CurrB.SlimFind(Sym)->getValue().second; if (!PrevT) { // Check for the point where we start tracking the value. if (CurrV.isOwned()) Msg = "Function call returns 'Owned' Core Foundation object."; else { assert (CurrV.isNotOwned()); Msg = "Function call returns 'Non-Owned' Core Foundation object."; } Stmt* S = cast(N->getLocation()).getStmt(); FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager()); PathDiagnosticPiece* P = new PathDiagnosticPiece(Pos, Msg); if (Expr* Exp = dyn_cast(S)) P->addRange(Exp->getSourceRange()); return P; } // Determine if the typestate has changed. RefVal PrevV = PrevB.SlimFind(Sym)->getValue().second; if (PrevV == CurrV) return NULL; // The typestate has changed. std::ostringstream os; switch (CurrV.getKind()) { case RefVal::Owned: case RefVal::NotOwned: assert (PrevV.getKind() == CurrV.getKind()); if (PrevV.getCount() > CurrV.getCount()) os << "Reference count decremented."; else os << "Reference count incremented."; if (CurrV.getCount()) { os << " Object has +" << CurrV.getCount(); if (CurrV.getCount() > 1) os << " reference counts."; else os << " reference count."; } Msg = os.str().c_str(); break; case RefVal::Released: Msg = "Object released."; break; case RefVal::ReturnedOwned: Msg = "Object returned to caller. " "Caller gets ownership of object."; break; case RefVal::ReturnedNotOwned: Msg = "Object returned to caller. " "Caller does not get ownership of object."; break; default: return NULL; } Stmt* S = cast(N->getLocation()).getStmt(); FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager()); PathDiagnosticPiece* P = new PathDiagnosticPiece(Pos, Msg); // Add the range by scanning the children of the statement for any bindings // to Sym. ValueStateManager& VSM = BR.getEngine().getStateManager(); for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) if (Expr* Exp = dyn_cast_or_null(*I)) { RVal X = VSM.GetRVal(CurrSt, Exp); if (lval::SymbolVal* SV = dyn_cast(&X)) if (SV->getSymbol() == Sym) { P->addRange(Exp->getSourceRange()); break; } } return P; } void UseAfterRelease::EmitWarnings(BugReporter& BR) { for (CFRefCount::use_after_iterator I = TF.use_after_begin(), E = TF.use_after_end(); I != E; ++I) { CFRefReport report(*this, I->first, I->second.second); report.addRange(I->second.first->getSourceRange()); BR.EmitWarning(report); } } void BadRelease::EmitWarnings(BugReporter& BR) { for (CFRefCount::bad_release_iterator I = TF.bad_release_begin(), E = TF.bad_release_end(); I != E; ++I) { CFRefReport report(*this, I->first, I->second.second); report.addRange(I->second.first->getSourceRange()); BR.EmitWarning(report); } } void Leak::EmitWarnings(BugReporter& BR) { for (CFRefCount::leaks_iterator I = TF.leaks_begin(), E = TF.leaks_end(); I != E; ++I) { std::vector& SymV = *(I->second); unsigned n = SymV.size(); for (unsigned i = 0; i < n; ++i) { CFRefReport report(*this, I->first, SymV[i]); BR.EmitWarning(report); } } } void Leak::GetErrorNodes(std::vector*>& Nodes) { for (CFRefCount::leaks_iterator I=TF.leaks_begin(), E=TF.leaks_end(); I!=E; ++I) Nodes.push_back(I->first); } //===----------------------------------------------------------------------===// // Transfer function creation for external clients. //===----------------------------------------------------------------------===// GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, const LangOptions& lopts) { return new CFRefCount(Ctx, GCEnabled, lopts); }