summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDevin Coughlin <dcoughlin@apple.com>2016-02-25 23:36:52 +0000
committerDevin Coughlin <dcoughlin@apple.com>2016-02-25 23:36:52 +0000
commit591991c86fb5820dec76b0e30871212c3e17969a (patch)
tree704310d40410d57e9b4092319a0ca7e309029b9b /clang/lib/StaticAnalyzer
parent7bd3499d05de028251b36a373dfcc4ba56a97a41 (diff)
downloadbcm5719-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.cpp143
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) {
OpenPOWER on IntegriCloud