brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.2 KiB · 227cbfa Raw
284 lines · cpp
1//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//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 defines ObjCSuperDeallocChecker, a builtin check that warns when10// self is used after a call to [super dealloc] in MRR mode.11//12//===----------------------------------------------------------------------===//13 14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"21 22using namespace clang;23using namespace ento;24 25namespace {26class ObjCSuperDeallocChecker27    : public Checker<check::PostObjCMessage, check::PreObjCMessage,28                     check::PreCall, check::Location> {29  mutable const IdentifierInfo *IIdealloc = nullptr;30  mutable const IdentifierInfo *IINSObject = nullptr;31  mutable Selector SELdealloc;32 33  const BugType DoubleSuperDeallocBugType{34      this, "[super dealloc] should not be called more than once",35      categories::CoreFoundationObjectiveC};36 37  void initIdentifierInfoAndSelectors(const ASTContext &Ctx) const;38 39  bool isSuperDeallocMessage(const ObjCMethodCall &M) const;40 41public:42  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;43  void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;44 45  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;46 47  void checkLocation(SVal l, bool isLoad, const Stmt *S,48                     CheckerContext &C) const;49 50private:51 52  void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const;53 54  void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S,55                             CheckerContext &C) const;56};57 58} // End anonymous namespace.59 60// Remember whether [super dealloc] has previously been called on the61// SymbolRef for the receiver.62REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)63 64namespace {65class SuperDeallocBRVisitor final : public BugReporterVisitor {66  SymbolRef ReceiverSymbol;67  bool Satisfied;68 69public:70  SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)71      : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {}72 73  PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,74                                   BugReporterContext &BRC,75                                   PathSensitiveBugReport &BR) override;76 77  void Profile(llvm::FoldingSetNodeID &ID) const override {78    ID.Add(ReceiverSymbol);79  }80};81} // End anonymous namespace.82 83void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,84                                                  CheckerContext &C) const {85 86  ProgramStateRef State = C.getState();87  SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();88  if (!ReceiverSymbol) {89    diagnoseCallArguments(M, C);90    return;91  }92 93  bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);94  if (!AlreadyCalled)95    return;96 97  StringRef Desc;98 99  if (isSuperDeallocMessage(M)) {100    Desc = "[super dealloc] should not be called multiple times";101  } else {102    Desc = StringRef();103  }104 105  reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C);106}107 108void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call,109                                           CheckerContext &C) const {110  diagnoseCallArguments(Call, C);111}112 113void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,114                                                   CheckerContext &C) const {115  // Check for [super dealloc] method call.116  if (!isSuperDeallocMessage(M))117    return;118 119  ProgramStateRef State = C.getState();120  const LocationContext *LC = C.getLocationContext();121  SymbolRef SelfSymbol = State->getSelfSVal(LC).getAsSymbol();122  assert(SelfSymbol && "No receiver symbol at call to [super dealloc]?");123 124  // We add this transition in checkPostObjCMessage to avoid warning when125  // we inline a call to [super dealloc] where the inlined call itself126  // calls [super dealloc].127  State = State->add<CalledSuperDealloc>(SelfSymbol);128  C.addTransition(State);129}130 131void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S,132                                  CheckerContext &C) const {133  SymbolRef BaseSym = L.getLocSymbolInBase();134  if (!BaseSym)135    return;136 137  ProgramStateRef State = C.getState();138 139  if (!State->contains<CalledSuperDealloc>(BaseSym))140    return;141 142  const MemRegion *R = L.getAsRegion();143  if (!R)144    return;145 146  // Climb the super regions to find the base symbol while recording147  // the second-to-last region for error reporting.148  const MemRegion *PriorSubRegion = nullptr;149  while (const SubRegion *SR = dyn_cast<SubRegion>(R)) {150    if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) {151      BaseSym = SymR->getSymbol();152      break;153    } else {154      R = SR->getSuperRegion();155      PriorSubRegion = SR;156    }157  }158 159  StringRef Desc = StringRef();160  auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);161 162  std::string Buf;163  llvm::raw_string_ostream OS(Buf);164  if (IvarRegion) {165    OS << "Use of instance variable '" << *IvarRegion->getDecl() <<166          "' after 'self' has been deallocated";167    Desc = Buf;168  }169 170  reportUseAfterDealloc(BaseSym, Desc, S, C);171}172 173/// Report a use-after-dealloc on Sym. If not empty,174/// Desc will be used to describe the error; otherwise,175/// a default warning will be used.176void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym,177                                                    StringRef Desc,178                                                    const Stmt *S,179                                                    CheckerContext &C) const {180  // We have a use of self after free.181  // This likely causes a crash, so stop exploring the182  // path by generating a sink.183  ExplodedNode *ErrNode = C.generateErrorNode();184  // If we've already reached this node on another path, return.185  if (!ErrNode)186    return;187 188  if (Desc.empty())189    Desc = "Use of 'self' after it has been deallocated";190 191  // Generate the report.192  auto BR = std::make_unique<PathSensitiveBugReport>(DoubleSuperDeallocBugType,193                                                     Desc, ErrNode);194  BR->addRange(S->getSourceRange());195  BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym));196  C.emitReport(std::move(BR));197}198 199/// Diagnose if any of the arguments to CE have already been200/// dealloc'd.201void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE,202                                                    CheckerContext &C) const {203  ProgramStateRef State = C.getState();204  unsigned ArgCount = CE.getNumArgs();205  for (unsigned I = 0; I < ArgCount; I++) {206    SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();207    if (!Sym)208      continue;209 210    if (State->contains<CalledSuperDealloc>(Sym)) {211      reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);212      return;213    }214  }215}216 217void ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(218    const ASTContext &Ctx) const {219  if (IIdealloc)220    return;221 222  IIdealloc = &Ctx.Idents.get("dealloc");223  IINSObject = &Ctx.Idents.get("NSObject");224 225  SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);226}227 228bool229ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {230  if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)231    return false;232 233  const ASTContext &Ctx = M.getASTContext();234  initIdentifierInfoAndSelectors(Ctx);235 236  return M.getSelector() == SELdealloc;237}238 239PathDiagnosticPieceRef240SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,241                                 BugReporterContext &BRC,242                                 PathSensitiveBugReport &) {243  if (Satisfied)244    return nullptr;245 246  ProgramStateRef State = Succ->getState();247 248  bool CalledNow =249      Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);250  bool CalledBefore =251      Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(252          ReceiverSymbol);253 254  // Is Succ the node on which the analyzer noted that [super dealloc] was255  // called on ReceiverSymbol?256  if (CalledNow && !CalledBefore) {257    Satisfied = true;258 259    ProgramPoint P = Succ->getLocation();260    PathDiagnosticLocation L =261        PathDiagnosticLocation::create(P, BRC.getSourceManager());262 263    if (!L.isValid() || !L.asLocation().isValid())264      return nullptr;265 266    return std::make_shared<PathDiagnosticEventPiece>(267        L, "[super dealloc] called here");268  }269 270  return nullptr;271}272 273//===----------------------------------------------------------------------===//274// Checker Registration.275//===----------------------------------------------------------------------===//276 277void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {278  Mgr.registerChecker<ObjCSuperDeallocChecker>();279}280 281bool ento::shouldRegisterObjCSuperDeallocChecker(const CheckerManager &mgr) {282  return true;283}284