brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.4 KiB · dc8bad6 Raw
388 lines · c
1//==--- RetainCountChecker.h - Checks for leaks and other issues -*- C++ -*--//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9//  This file defines the methods for RetainCountChecker, which implements10//  a reference count checker for Core Foundation and Cocoa on (Mac OS X).11//12//===----------------------------------------------------------------------===//13 14#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H15#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H16 17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "RetainCountDiagnostics.h"19#include "clang/AST/Attr.h"20#include "clang/AST/DeclCXX.h"21#include "clang/AST/DeclObjC.h"22#include "clang/AST/ParentMap.h"23#include "clang/Analysis/DomainSpecific/CocoaConventions.h"24#include "clang/Analysis/PathDiagnostic.h"25#include "clang/Analysis/RetainSummaryManager.h"26#include "clang/Basic/LangOptions.h"27#include "clang/Basic/SourceManager.h"28#include "clang/Analysis/SelectorExtras.h"29#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"30#include "clang/StaticAnalyzer/Core/Checker.h"31#include "clang/StaticAnalyzer/Core/CheckerManager.h"32#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"33#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"34#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"35#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"36#include "llvm/ADT/DenseMap.h"37#include "llvm/ADT/FoldingSet.h"38#include "llvm/ADT/ImmutableList.h"39#include "llvm/ADT/STLExtras.h"40#include "llvm/ADT/SmallString.h"41#include "llvm/ADT/StringExtras.h"42#include <cstdarg>43#include <utility>44 45namespace clang {46namespace ento {47namespace retaincountchecker {48 49/// Metadata on reference.50class RefVal {51public:52  enum Kind {53    Owned = 0, // Owning reference.54    NotOwned,  // Reference is not owned by still valid (not freed).55    Released,  // Object has been released.56    ReturnedOwned, // Returned object passes ownership to caller.57    ReturnedNotOwned, // Return object does not pass ownership to caller.58    ERROR_START,59    ErrorDeallocNotOwned, // -dealloc called on non-owned object.60    ErrorUseAfterRelease, // Object used after released.61    ErrorReleaseNotOwned, // Release of an object that was not owned.62    ERROR_LEAK_START,63    ErrorLeak,  // A memory leak due to excessive reference counts.64    ErrorLeakReturned, // A memory leak due to the returning method not having65                       // the correct naming conventions.66    ErrorOverAutorelease,67    ErrorReturnedNotOwned68  };69 70  /// Tracks how an object referenced by an ivar has been used.71  ///72  /// This accounts for us not knowing if an arbitrary ivar is supposed to be73  /// stored at +0 or +1.74  enum class IvarAccessHistory {75    None,76    AccessedDirectly,77    ReleasedAfterDirectAccess78  };79 80private:81  /// The number of outstanding retains.82  unsigned Cnt;83  /// The number of outstanding autoreleases.84  unsigned ACnt;85  /// The (static) type of the object at the time we started tracking it.86  QualType T;87 88  /// The current state of the object.89  ///90  /// See the RefVal::Kind enum for possible values.91  unsigned RawKind : 5;92 93  /// The kind of object being tracked (CF or ObjC or OSObject), if known.94  ///95  /// See the ObjKind enum for possible values.96  unsigned RawObjectKind : 3;97 98  /// True if the current state and/or retain count may turn out to not be the99  /// best possible approximation of the reference counting state.100  ///101  /// If true, the checker may decide to throw away ("override") this state102  /// in favor of something else when it sees the object being used in new ways.103  ///104  /// This setting should not be propagated to state derived from this state.105  /// Once we start deriving new states, it would be inconsistent to override106  /// them.107  unsigned RawIvarAccessHistory : 2;108 109  RefVal(Kind k, ObjKind o, unsigned cnt, unsigned acnt, QualType t,110         IvarAccessHistory IvarAccess)111    : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)),112      RawObjectKind(static_cast<unsigned>(o)),113      RawIvarAccessHistory(static_cast<unsigned>(IvarAccess)) {114    assert(getKind() == k && "not enough bits for the kind");115    assert(getObjKind() == o && "not enough bits for the object kind");116    assert(getIvarAccessHistory() == IvarAccess && "not enough bits");117  }118 119public:120  Kind getKind() const { return static_cast<Kind>(RawKind); }121 122  ObjKind getObjKind() const {123    return static_cast<ObjKind>(RawObjectKind);124  }125 126  unsigned getCount() const { return Cnt; }127  unsigned getAutoreleaseCount() const { return ACnt; }128  unsigned getCombinedCounts() const { return Cnt + ACnt; }129  void clearCounts() {130    Cnt = 0;131    ACnt = 0;132  }133  void setCount(unsigned i) {134    Cnt = i;135  }136  void setAutoreleaseCount(unsigned i) {137    ACnt = i;138  }139 140  QualType getType() const { return T; }141 142  /// Returns what the analyzer knows about direct accesses to a particular143  /// instance variable.144  ///145  /// If the object with this refcount wasn't originally from an Objective-C146  /// ivar region, this should always return IvarAccessHistory::None.147  IvarAccessHistory getIvarAccessHistory() const {148    return static_cast<IvarAccessHistory>(RawIvarAccessHistory);149  }150 151  bool isOwned() const {152    return getKind() == Owned;153  }154 155  bool isNotOwned() const {156    return getKind() == NotOwned;157  }158 159  bool isReturnedOwned() const {160    return getKind() == ReturnedOwned;161  }162 163  bool isReturnedNotOwned() const {164    return getKind() == ReturnedNotOwned;165  }166 167  /// Create a state for an object whose lifetime is the responsibility of the168  /// current function, at least partially.169  ///170  /// Most commonly, this is an owned object with a retain count of +1.171  static RefVal makeOwned(ObjKind o, QualType t) {172    return RefVal(Owned, o, /*Count=*/1, 0, t, IvarAccessHistory::None);173  }174 175  /// Create a state for an object whose lifetime is not the responsibility of176  /// the current function.177  ///178  /// Most commonly, this is an unowned object with a retain count of +0.179  static RefVal makeNotOwned(ObjKind o, QualType t) {180    return RefVal(NotOwned, o, /*Count=*/0, 0, t, IvarAccessHistory::None);181  }182 183  RefVal operator-(size_t i) const {184    return RefVal(getKind(), getObjKind(), getCount() - i,185                  getAutoreleaseCount(), getType(), getIvarAccessHistory());186  }187 188  RefVal operator+(size_t i) const {189    return RefVal(getKind(), getObjKind(), getCount() + i,190                  getAutoreleaseCount(), getType(), getIvarAccessHistory());191  }192 193  RefVal operator^(Kind k) const {194    return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(),195                  getType(), getIvarAccessHistory());196  }197 198  RefVal autorelease() const {199    return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1,200                  getType(), getIvarAccessHistory());201  }202 203  RefVal withIvarAccess() const {204    assert(getIvarAccessHistory() == IvarAccessHistory::None);205    return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(),206                  getType(), IvarAccessHistory::AccessedDirectly);207  }208 209  RefVal releaseViaIvar() const {210    assert(getIvarAccessHistory() == IvarAccessHistory::AccessedDirectly);211    return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(),212                  getType(), IvarAccessHistory::ReleasedAfterDirectAccess);213  }214 215  // Comparison, profiling, and pretty-printing.216  bool hasSameState(const RefVal &X) const {217    return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt &&218           getIvarAccessHistory() == X.getIvarAccessHistory();219  }220 221  bool operator==(const RefVal& X) const {222    return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind();223  }224 225  void Profile(llvm::FoldingSetNodeID& ID) const {226    ID.Add(T);227    ID.AddInteger(RawKind);228    ID.AddInteger(Cnt);229    ID.AddInteger(ACnt);230    ID.AddInteger(RawObjectKind);231    ID.AddInteger(RawIvarAccessHistory);232  }233 234  void print(raw_ostream &Out) const;235};236 237class RetainCountChecker238    : public CheckerFamily<239          check::Bind, check::DeadSymbols, check::BeginFunction,240          check::EndFunction, check::PostStmt<BlockExpr>,241          check::PostStmt<CastExpr>, check::PostStmt<ObjCArrayLiteral>,242          check::PostStmt<ObjCDictionaryLiteral>,243          check::PostStmt<ObjCBoxedExpr>, check::PostStmt<ObjCIvarRefExpr>,244          check::PostCall, check::RegionChanges, eval::Assume, eval::Call> {245 246public:247  RefCountFrontend RetainCount;248  RefCountFrontend OSObjectRetainCount;249 250  mutable std::unique_ptr<RetainSummaryManager> Summaries;251 252  static std::unique_ptr<SimpleProgramPointTag> DeallocSentTag;253  static std::unique_ptr<SimpleProgramPointTag> CastFailTag;254 255  /// Track initial parameters (for the entry point) for NS/CF objects.256  bool TrackNSCFStartParam = false;257 258  StringRef getDebugTag() const override { return "RetainCountChecker"; }259 260  RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const {261    if (!Summaries)262      Summaries = std::make_unique<RetainSummaryManager>(263          Ctx, RetainCount.isEnabled(), OSObjectRetainCount.isEnabled());264    return *Summaries;265  }266 267  RetainSummaryManager &getSummaryManager(CheckerContext &C) const {268    return getSummaryManager(C.getASTContext());269  }270 271  const RefCountFrontend &getPreferredFrontend() const {272    // FIXME: The two frontends of this checker family are in an unusual273    // relationship: if they are both enabled, then all bug reports are274    // reported by RetainCount (i.e. `osx.cocoa.RetainCount`), even the bugs275    // that "belong to" OSObjectRetainCount (i.e. `osx.OSObjectRetainCount`).276    // This is counter-intuitive and should be fixed to avoid confusion.277    return RetainCount.isEnabled() ? RetainCount : OSObjectRetainCount;278  }279 280  void printState(raw_ostream &Out, ProgramStateRef State,281                  const char *NL, const char *Sep) const override;282 283  void checkBind(SVal loc, SVal val, const Stmt *S, bool AtDeclInit,284                 CheckerContext &C) const;285  void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;286  void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;287 288  void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;289  void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;290  void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;291 292  void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const;293 294  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;295 296  void checkSummary(const RetainSummary &Summ, const CallEvent &Call,297                    CheckerContext &C) const;298 299  void processSummaryOfInlined(const RetainSummary &Summ,300                               const CallEvent &Call,301                               CheckerContext &C) const;302 303  bool evalCall(const CallEvent &Call, CheckerContext &C) const;304 305  ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,306                                 bool Assumption) const;307 308  ProgramStateRef309  checkRegionChanges(ProgramStateRef state,310                     const InvalidatedSymbols *invalidated,311                     ArrayRef<const MemRegion *> ExplicitRegions,312                     ArrayRef<const MemRegion *> Regions,313                     const LocationContext* LCtx,314                     const CallEvent *Call) const;315 316  ExplodedNode* checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,317                                ExplodedNode *Pred, RetEffect RE, RefVal X,318                                SymbolRef Sym, ProgramStateRef state) const;319 320  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;321  void checkBeginFunction(CheckerContext &C) const;322  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;323 324  ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym,325                               RefVal V, ArgEffect E, RefVal::Kind &hasErr,326                               CheckerContext &C) const;327 328  const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind,329                                        SymbolRef Sym) const;330 331  bool isReleaseUnownedError(RefVal::Kind ErrorKind) const;332 333  void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,334                           RefVal::Kind ErrorKind, SymbolRef Sym,335                           CheckerContext &C) const;336 337  void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;338 339  ProgramStateRef handleSymbolDeath(ProgramStateRef state,340                                    SymbolRef sid, RefVal V,341                                    SmallVectorImpl<SymbolRef> &Leaked) const;342 343  ProgramStateRef handleAutoreleaseCounts(ProgramStateRef state,344                                          ExplodedNode *Pred,345                                          CheckerContext &Ctx, SymbolRef Sym,346                                          RefVal V,347                                          const ReturnStmt *S = nullptr) const;348 349  ExplodedNode *processLeaks(ProgramStateRef state,350                             SmallVectorImpl<SymbolRef> &Leaked,351                             CheckerContext &Ctx,352                             ExplodedNode *Pred = nullptr) const;353 354  static const SimpleProgramPointTag &getDeallocSentTag() {355    return *DeallocSentTag;356  }357 358  static const SimpleProgramPointTag &getCastFailTag() { return *CastFailTag; }359 360private:361  /// Perform the necessary checks and state adjustments at the end of the362  /// function.363  /// \p S Return statement, may be null.364  ExplodedNode * processReturn(const ReturnStmt *S, CheckerContext &C) const;365};366 367//===----------------------------------------------------------------------===//368// RefBindings - State used to track object reference counts.369//===----------------------------------------------------------------------===//370 371const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym);372 373/// Returns true if this stack frame is for an Objective-C method that is a374/// property getter or setter whose body has been synthesized by the analyzer.375inline bool isSynthesizedAccessor(const StackFrameContext *SFC) {376  auto Method = dyn_cast_or_null<ObjCMethodDecl>(SFC->getDecl());377  if (!Method || !Method->isPropertyAccessor())378    return false;379 380  return SFC->getAnalysisDeclContext()->isBodyAutosynthesized();381}382 383} // end namespace retaincountchecker384} // end namespace ento385} // end namespace clang386 387#endif388