ObjCSuperDeallocChecker.cpp 9.3 KB

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