//==--- RetainCountChecker.h - Checks for leaks and other issues -*- 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 RetainCountChecker, which implements // a reference count checker for Core Foundation and Cocoa on (Mac OS X). // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H #include "../ClangSACheckers.h" #include "../AllocationDiagnostics.h" #include "RetainCountDiagnostics.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Analysis/SelectorExtras.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include #include using llvm::StrInStrNoCase; namespace clang { namespace ento { namespace retaincountchecker { /// Metadata on reference. class RefVal { public: enum Kind { Owned = 0, // Owning reference. NotOwned, // Reference is not owned by still valid (not freed). Released, // Object has been released. ReturnedOwned, // Returned object passes ownership to caller. ReturnedNotOwned, // Return object does not pass ownership to caller. ERROR_START, ErrorDeallocNotOwned, // -dealloc called on non-owned object. 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. ErrorOverAutorelease, ErrorReturnedNotOwned }; /// Tracks how an object referenced by an ivar has been used. /// /// This accounts for us not knowing if an arbitrary ivar is supposed to be /// stored at +0 or +1. enum class IvarAccessHistory { None, AccessedDirectly, ReleasedAfterDirectAccess }; private: /// The number of outstanding retains. unsigned Cnt; /// The number of outstanding autoreleases. unsigned ACnt; /// The (static) type of the object at the time we started tracking it. QualType T; /// The current state of the object. /// /// See the RefVal::Kind enum for possible values. unsigned RawKind : 5; /// The kind of object being tracked (CF or ObjC), if known. /// /// See the RetEffect::ObjKind enum for possible values. unsigned RawObjectKind : 3; /// True if the current state and/or retain count may turn out to not be the /// best possible approximation of the reference counting state. /// /// If true, the checker may decide to throw away ("override") this state /// in favor of something else when it sees the object being used in new ways. /// /// This setting should not be propagated to state derived from this state. /// Once we start deriving new states, it would be inconsistent to override /// them. unsigned RawIvarAccessHistory : 2; RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t, IvarAccessHistory IvarAccess) : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast(k)), RawObjectKind(static_cast(o)), RawIvarAccessHistory(static_cast(IvarAccess)) { assert(getKind() == k && "not enough bits for the kind"); assert(getObjKind() == o && "not enough bits for the object kind"); assert(getIvarAccessHistory() == IvarAccess && "not enough bits"); } public: Kind getKind() const { return static_cast(RawKind); } RetEffect::ObjKind getObjKind() const { return static_cast(RawObjectKind); } 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; } /// Returns what the analyzer knows about direct accesses to a particular /// instance variable. /// /// If the object with this refcount wasn't originally from an Objective-C /// ivar region, this should always return IvarAccessHistory::None. IvarAccessHistory getIvarAccessHistory() const { return static_cast(RawIvarAccessHistory); } bool isOwned() const { return getKind() == Owned; } bool isNotOwned() const { return getKind() == NotOwned; } bool isReturnedOwned() const { return getKind() == ReturnedOwned; } bool isReturnedNotOwned() const { return getKind() == ReturnedNotOwned; } /// Create a state for an object whose lifetime is the responsibility of the /// current function, at least partially. /// /// Most commonly, this is an owned object with a retain count of +1. static RefVal makeOwned(RetEffect::ObjKind o, QualType t) { return RefVal(Owned, o, /*Count=*/1, 0, t, IvarAccessHistory::None); } /// Create a state for an object whose lifetime is not the responsibility of /// the current function. /// /// Most commonly, this is an unowned object with a retain count of +0. static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t) { return RefVal(NotOwned, o, /*Count=*/0, 0, t, IvarAccessHistory::None); } RefVal operator-(size_t i) const { return RefVal(getKind(), getObjKind(), getCount() - i, getAutoreleaseCount(), getType(), getIvarAccessHistory()); } RefVal operator+(size_t i) const { return RefVal(getKind(), getObjKind(), getCount() + i, getAutoreleaseCount(), getType(), getIvarAccessHistory()); } RefVal operator^(Kind k) const { return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), getType(), getIvarAccessHistory()); } RefVal autorelease() const { return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, getType(), getIvarAccessHistory()); } RefVal withIvarAccess() const { assert(getIvarAccessHistory() == IvarAccessHistory::None); return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(), getType(), IvarAccessHistory::AccessedDirectly); } RefVal releaseViaIvar() const { assert(getIvarAccessHistory() == IvarAccessHistory::AccessedDirectly); return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(), getType(), IvarAccessHistory::ReleasedAfterDirectAccess); } // Comparison, profiling, and pretty-printing. bool hasSameState(const RefVal &X) const { return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt && getIvarAccessHistory() == X.getIvarAccessHistory(); } bool operator==(const RefVal& X) const { return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind(); } void Profile(llvm::FoldingSetNodeID& ID) const { ID.Add(T); ID.AddInteger(RawKind); ID.AddInteger(Cnt); ID.AddInteger(ACnt); ID.AddInteger(RawObjectKind); ID.AddInteger(RawIvarAccessHistory); } void print(raw_ostream &Out) const; }; class RetainCountChecker : public Checker< check::Bind, check::DeadSymbols, check::EndAnalysis, check::BeginFunction, check::EndFunction, check::PostStmt, check::PostStmt, check::PostStmt, check::PostStmt, check::PostStmt, check::PostStmt, check::PostCall, check::PreStmt, check::RegionChanges, eval::Assume, eval::Call > { mutable std::unique_ptr useAfterRelease, releaseNotOwned; mutable std::unique_ptr deallocNotOwned; mutable std::unique_ptr overAutorelease, returnNotOwnedForOwned; mutable std::unique_ptr leakWithinFunction, leakAtReturn; typedef llvm::DenseMap SymbolTagMap; // This map is only used to ensure proper deletion of any allocated tags. mutable SymbolTagMap DeadSymbolTags; mutable std::unique_ptr Summaries; mutable SummaryLogTy SummaryLog; AnalyzerOptions &Options; mutable bool ShouldResetSummaryLog; /// Optional setting to indicate if leak reports should include /// the allocation line. mutable bool IncludeAllocationLine; public: RetainCountChecker(AnalyzerOptions &Options) : Options(Options), ShouldResetSummaryLog(false), IncludeAllocationLine( shouldIncludeAllocationSiteInLeakDiagnostics(Options)) {} ~RetainCountChecker() override { DeleteContainerSeconds(DeadSymbolTags); } bool shouldCheckOSObjectRetainCount() const { return Options.getBooleanOption("CheckOSObject", false, this); } void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { // FIXME: This is a hack to make sure the summary log gets cleared between // analyses of different code bodies. // // Why is this necessary? Because a checker's lifetime is tied to a // translation unit, but an ExplodedGraph's lifetime is just a code body. // Once in a blue moon, a new ExplodedNode will have the same address as an // old one with an associated summary, and the bug report visitor gets very // confused. (To make things worse, the summary lifetime is currently also // tied to a code body, so we get a crash instead of incorrect results.) // // Why is this a bad solution? Because if the lifetime of the ExplodedGraph // changes, things will start going wrong again. Really the lifetime of this // log needs to be tied to either the specific nodes in it or the entire // ExplodedGraph, not to a specific part of the code being analyzed. // // (Also, having stateful local data means that the same checker can't be // used from multiple threads, but a lot of checkers have incorrect // assumptions about that anyway. So that wasn't a priority at the time of // this fix.) // // This happens at the end of analysis, but bug reports are emitted /after/ // this point. So we can't just clear the summary log now. Instead, we mark // that the next time we access the summary log, it should be cleared. // If we never reset the summary log during /this/ code body analysis, // there were no new summaries. There might still have been summaries from // the /last/ analysis, so clear them out to make sure the bug report // visitors don't get confused. if (ShouldResetSummaryLog) SummaryLog.clear(); ShouldResetSummaryLog = !SummaryLog.empty(); } CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const { if (!leakWithinFunction) leakWithinFunction.reset(new Leak(this, "Leak")); return leakWithinFunction.get(); } CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts) const { if (!leakAtReturn) leakAtReturn.reset(new Leak(this, "Leak of returned object")); return leakAtReturn.get(); } RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const { // FIXME: We don't support ARC being turned on and off during one analysis. // (nor, for that matter, do we support changing ASTContexts) bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; if (!Summaries) { Summaries.reset(new RetainSummaryManager( Ctx, ARCEnabled, shouldCheckOSObjectRetainCount())); } else { assert(Summaries->isARCEnabled() == ARCEnabled); } return *Summaries; } RetainSummaryManager &getSummaryManager(CheckerContext &C) const { return getSummaryManager(C.getASTContext()); } void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const; void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkSummary(const RetainSummary &Summ, const CallEvent &Call, CheckerContext &C) const; void processSummaryOfInlined(const RetainSummary &Summ, const CallEvent &Call, CheckerContext &C) const; bool evalCall(const CallExpr *CE, CheckerContext &C) const; ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const; ProgramStateRef checkRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext* LCtx, const CallEvent *Call) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C, ExplodedNode *Pred, RetEffect RE, RefVal X, SymbolRef Sym, ProgramStateRef state) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; void checkBeginFunction(CheckerContext &C) const; void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym, RefVal V, ArgEffect E, RefVal::Kind &hasErr, CheckerContext &C) const; void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; void processObjCLiterals(CheckerContext &C, const Expr *Ex) const; const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; ProgramStateRef handleSymbolDeath(ProgramStateRef state, SymbolRef sid, RefVal V, SmallVectorImpl &Leaked) const; ProgramStateRef handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred, const ProgramPointTag *Tag, CheckerContext &Ctx, SymbolRef Sym, RefVal V) const; ExplodedNode *processLeaks(ProgramStateRef state, SmallVectorImpl &Leaked, CheckerContext &Ctx, ExplodedNode *Pred = nullptr) const; }; //===----------------------------------------------------------------------===// // RefBindings - State used to track object reference counts. //===----------------------------------------------------------------------===// const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym); ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, RefVal Val); ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym); /// Returns true if this stack frame is for an Objective-C method that is a /// property getter or setter whose body has been synthesized by the analyzer. inline bool isSynthesizedAccessor(const StackFrameContext *SFC) { auto Method = dyn_cast_or_null(SFC->getDecl()); if (!Method || !Method->isPropertyAccessor()) return false; return SFC->getAnalysisDeclContext()->isBodyAutosynthesized(); } } // end namespace retaincountchecker } // end namespace ento } // end namespace clang #endif