123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- //==--- RetainCountChecker.h - Checks for leaks and other issues -*- C++ -*--//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // 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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/Analysis/PathDiagnostic.h"
- #include "clang/Analysis/RetainSummaryManager.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/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 "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 <cstdarg>
- #include <utility>
- 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 or OSObject), if known.
- ///
- /// See the 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, ObjKind o, unsigned cnt, unsigned acnt, QualType t,
- IvarAccessHistory IvarAccess)
- : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)),
- RawObjectKind(static_cast<unsigned>(o)),
- RawIvarAccessHistory(static_cast<unsigned>(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<Kind>(RawKind); }
- ObjKind getObjKind() const {
- return static_cast<ObjKind>(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<IvarAccessHistory>(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(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(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::BeginFunction,
- check::EndFunction,
- check::PostStmt<BlockExpr>,
- check::PostStmt<CastExpr>,
- check::PostStmt<ObjCArrayLiteral>,
- check::PostStmt<ObjCDictionaryLiteral>,
- check::PostStmt<ObjCBoxedExpr>,
- check::PostStmt<ObjCIvarRefExpr>,
- check::PostCall,
- check::RegionChanges,
- eval::Assume,
- eval::Call > {
- public:
- std::unique_ptr<RefCountBug> UseAfterRelease;
- std::unique_ptr<RefCountBug> ReleaseNotOwned;
- std::unique_ptr<RefCountBug> DeallocNotOwned;
- std::unique_ptr<RefCountBug> FreeNotOwned;
- std::unique_ptr<RefCountBug> OverAutorelease;
- std::unique_ptr<RefCountBug> ReturnNotOwnedForOwned;
- std::unique_ptr<RefCountBug> LeakWithinFunction;
- std::unique_ptr<RefCountBug> LeakAtReturn;
- mutable std::unique_ptr<RetainSummaryManager> Summaries;
- static std::unique_ptr<CheckerProgramPointTag> DeallocSentTag;
- static std::unique_ptr<CheckerProgramPointTag> CastFailTag;
- /// Track Objective-C and CoreFoundation objects.
- bool TrackObjCAndCFObjects = false;
- /// Track sublcasses of OSObject.
- bool TrackOSObjects = false;
- /// Track initial parameters (for the entry point) for NS/CF objects.
- bool TrackNSCFStartParam = false;
- RetainCountChecker() {};
- RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const {
- if (!Summaries)
- Summaries.reset(
- new RetainSummaryManager(Ctx, TrackObjCAndCFObjects, TrackOSObjects));
- 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 CallEvent &Call, CheckerContext &C) const;
- ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
- bool Assumption) const;
- ProgramStateRef
- checkRegionChanges(ProgramStateRef state,
- const InvalidatedSymbols *invalidated,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const LocationContext* LCtx,
- const CallEvent *Call) const;
- ExplodedNode* 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;
- const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind,
- SymbolRef Sym) const;
- void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
- RefVal::Kind ErrorKind, SymbolRef Sym,
- CheckerContext &C) const;
- void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;
- ProgramStateRef handleSymbolDeath(ProgramStateRef state,
- SymbolRef sid, RefVal V,
- SmallVectorImpl<SymbolRef> &Leaked) const;
- ProgramStateRef
- handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred,
- const ProgramPointTag *Tag, CheckerContext &Ctx,
- SymbolRef Sym,
- RefVal V,
- const ReturnStmt *S=nullptr) const;
- ExplodedNode *processLeaks(ProgramStateRef state,
- SmallVectorImpl<SymbolRef> &Leaked,
- CheckerContext &Ctx,
- ExplodedNode *Pred = nullptr) const;
- static const CheckerProgramPointTag &getDeallocSentTag() {
- return *DeallocSentTag;
- }
- static const CheckerProgramPointTag &getCastFailTag() { return *CastFailTag; }
- private:
- /// Perform the necessary checks and state adjustments at the end of the
- /// function.
- /// \p S Return statement, may be null.
- ExplodedNode * processReturn(const ReturnStmt *S, CheckerContext &C) const;
- };
- //===----------------------------------------------------------------------===//
- // RefBindings - State used to track object reference counts.
- //===----------------------------------------------------------------------===//
- const RefVal *getRefBinding(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<ObjCMethodDecl>(SFC->getDecl());
- if (!Method || !Method->isPropertyAccessor())
- return false;
- return SFC->getAnalysisDeclContext()->isBodyAutosynthesized();
- }
- } // end namespace retaincountchecker
- } // end namespace ento
- } // end namespace clang
- #endif
|