diff options
| author | Devin Coughlin <dcoughlin@apple.com> | 2016-02-25 23:36:52 +0000 |
|---|---|---|
| committer | Devin Coughlin <dcoughlin@apple.com> | 2016-02-25 23:36:52 +0000 |
| commit | 591991c86fb5820dec76b0e30871212c3e17969a (patch) | |
| tree | 704310d40410d57e9b4092319a0ca7e309029b9b /clang/lib/StaticAnalyzer | |
| parent | 7bd3499d05de028251b36a373dfcc4ba56a97a41 (diff) | |
| download | bcm5719-llvm-591991c86fb5820dec76b0e30871212c3e17969a.tar.gz bcm5719-llvm-591991c86fb5820dec76b0e30871212c3e17969a.zip | |
[analyzer] Warn on use of 'self' after call to to [super dealloc].
Referring to 'self' after a call to [super dealloc] is a use-after-free in
Objective-C because NSObject's -dealloc frees the memory pointed to by self.
This patch extends the ObjCSuperDeallocChecker to catch this error.
rdar://problem/6953275
Differential Revision: http://reviews.llvm.org/D17528
llvm-svn: 261935
Diffstat (limited to 'clang/lib/StaticAnalyzer')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp | 143 |
1 files changed, 119 insertions, 24 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index d1d6ef388db..596ad70c423 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // // This defines ObjCSuperDeallocChecker, a builtin check that warns when -// [super dealloc] is called twice on the same instance in MRR mode. +// self is used after a call to [super dealloc] in MRR mode. // //===----------------------------------------------------------------------===// @@ -25,7 +25,8 @@ using namespace ento; namespace { class ObjCSuperDeallocChecker - : public Checker<check::PostObjCMessage, check::PreObjCMessage> { + : public Checker<check::PostObjCMessage, check::PreObjCMessage, + check::PreCall, check::Location> { mutable IdentifierInfo *IIdealloc, *IINSObject; mutable Selector SELdealloc; @@ -40,12 +41,24 @@ public: ObjCSuperDeallocChecker(); void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + +private: + + void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const; + + void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S, + CheckerContext &C) const; }; } // End anonymous namespace. // Remember whether [super dealloc] has previously been called on the -// a SymbolRef for the receiver. +// SymbolRef for the receiver. REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) class SuperDeallocBRVisitor final @@ -71,40 +84,36 @@ public: void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { - if (!isSuperDeallocMessage(M)) - return; ProgramStateRef State = C.getState(); SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); - assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); + if (!ReceiverSymbol) { + diagnoseCallArguments(M, C); + return; + } bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol); - - // If [super dealloc] has not been called, there is nothing to do. We'll - // note the fact that [super dealloc] was called in checkPostObjCMessage. if (!AlreadyCalled) return; - // We have a duplicate [super dealloc] method call. - // This likely causes a crash, so stop exploring the - // path by generating a sink. - ExplodedNode *ErrNode = C.generateErrorNode(); - // If we've already reached this node on another path, return. - if (!ErrNode) - return; + StringRef Desc; - // Generate the report. - std::unique_ptr<BugReport> BR( - new BugReport(*DoubleSuperDeallocBugType, - "[super dealloc] should not be called multiple times", - ErrNode)); - BR->addRange(M.getOriginExpr()->getSourceRange()); - BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(ReceiverSymbol)); - C.emitReport(std::move(BR)); + if (isSuperDeallocMessage(M)) { + Desc = "[super dealloc] should not be called multiple times"; + } else { + Desc = StringRef(); + } + + reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C); return; } +void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + diagnoseCallArguments(Call, C); +} + void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { // Check for [super dealloc] method call. @@ -122,6 +131,92 @@ void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, C.addTransition(State); } +void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + SymbolRef BaseSym = L.getLocSymbolInBase(); + if (!BaseSym) + return; + + ProgramStateRef State = C.getState(); + + if (!State->contains<CalledSuperDealloc>(BaseSym)) + return; + + const MemRegion *R = L.getAsRegion(); + if (!R) + return; + + // Climb the super regions to find the base symbol while recording + // the second-to-last region for error reporting. + const MemRegion *PriorSubRegion = nullptr; + while (const SubRegion *SR = dyn_cast<SubRegion>(R)) { + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) { + BaseSym = SymR->getSymbol(); + break; + } else { + R = SR->getSuperRegion(); + PriorSubRegion = SR; + } + } + + StringRef Desc = StringRef(); + auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion); + + if (IvarRegion) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "use of instance variable '" << *IvarRegion->getDecl() << + "' after the instance has been freed with call to [super dealloc]"; + Desc = OS.str(); + } + + reportUseAfterDealloc(BaseSym, Desc, S, C); +} + +/// Report a use-after-dealloc on \param Sym. If not empty, +/// \param Desc will be used to describe the error; otherwise, +/// a default warning will be used. +void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, + StringRef Desc, + const Stmt *S, + CheckerContext &C) const { + // We have a use of self after free. + // This likely causes a crash, so stop exploring the + // path by generating a sink. + ExplodedNode *ErrNode = C.generateErrorNode(); + // If we've already reached this node on another path, return. + if (!ErrNode) + return; + + if (Desc.empty()) + Desc = "use of 'self' after it has been freed with call to [super dealloc]"; + + // Generate the report. + std::unique_ptr<BugReport> BR( + new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode)); + BR->addRange(S->getSourceRange()); + BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym)); + C.emitReport(std::move(BR)); +} + +/// Diagnose if any of the arguments to \param CE have already been +/// dealloc'd. +void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + unsigned ArgCount = CE.getNumArgs(); + for (unsigned I = 0; I < ArgCount; I++) { + SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); + if (!Sym) + continue; + + if (State->contains<CalledSuperDealloc>(Sym)) { + reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); + return; + } + } +} + ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() : IIdealloc(nullptr), IINSObject(nullptr) { |

