Skip to content

Commit

Permalink
[analyzer] Detect duplicate [super dealloc] calls
Browse files Browse the repository at this point in the history
Add an alpha path checker that warns about duplicate calls to [super dealloc].
This will form the foundation of a checker that will detect uses of
'self' after calling [super dealloc].

Part of rdar://problem/6953275.

Based on a patch by David Kilzer!

Differential Revision: http://reviews.llvm.org/D5238

llvm-svn: 261545
  • Loading branch information
devincoughlin committed Feb 22, 2016
1 parent 7bf9187 commit eb6673c
Show file tree
Hide file tree
Showing 4 changed files with 500 additions and 0 deletions.
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ add_clang_library(clangStaticAnalyzerCheckers
ObjCContainersChecker.cpp
ObjCMissingSuperCallChecker.cpp
ObjCSelfInitChecker.cpp
ObjCSuperDeallocChecker.cpp
ObjCUnusedIVarsChecker.cpp
PaddingChecker.cpp
PointerArithChecker.cpp
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,10 @@ def ObjCDeallocChecker : Checker<"Dealloc">,
HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">,
DescFile<"CheckObjCDealloc.cpp">;

def ObjCSuperDeallocChecker : Checker<"SuperDealloc">,
HelpText<"Warn about improper use of '[super dealloc]' in Objective-C">,
DescFile<"ObjCSuperDeallocChecker.cpp">;

def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">,
HelpText<"Check that the invalidatable instance variables are invalidated in the methods annotated with objc_instance_variable_invalidator">,
DescFile<"IvarInvalidationChecker.cpp">;
Expand Down
197 changes: 197 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines ObjCSuperDeallocChecker, a builtin check that warns when
// [super dealloc] is called twice on the same instance in MRR mode.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"

using namespace clang;
using namespace ento;

namespace {
class ObjCSuperDeallocChecker
: public Checker<check::PostObjCMessage, check::PreObjCMessage> {

mutable IdentifierInfo *IIdealloc, *IINSObject;
mutable Selector SELdealloc;

std::unique_ptr<BugType> DoubleSuperDeallocBugType;

void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;

bool isSuperDeallocMessage(const ObjCMethodCall &M) const;

public:
ObjCSuperDeallocChecker();
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
};

} // End anonymous namespace.

// Remember whether [super dealloc] has previously been called on the
// a SymbolRef for the receiver.
REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)

class SuperDeallocBRVisitor final
: public BugReporterVisitorImpl<SuperDeallocBRVisitor> {

SymbolRef ReceiverSymbol;
bool Satisfied;

public:
SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)
: ReceiverSymbol(ReceiverSymbol),
Satisfied(false) {}

PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ,
const ExplodedNode *Pred,
BugReporterContext &BRC,
BugReport &BR) override;

void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.Add(ReceiverSymbol);
}
};

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]?");

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;

// 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));

return;
}

void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
CheckerContext &C) const {
// Check for [super dealloc] method call.
if (!isSuperDeallocMessage(M))
return;

ProgramStateRef State = C.getState();
SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");

// We add this transition in checkPostObjCMessage to avoid warning when
// we inline a call to [super dealloc] where the inlined call itself
// calls [super dealloc].
State = State->add<CalledSuperDealloc>(ReceiverSymbol);
C.addTransition(State);
}

ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
: IIdealloc(nullptr), IINSObject(nullptr) {

DoubleSuperDeallocBugType.reset(
new BugType(this, "[super dealloc] should not be called more than once",
categories::CoreFoundationObjectiveC));
}

void
ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
if (IIdealloc)
return;

IIdealloc = &Ctx.Idents.get("dealloc");
IINSObject = &Ctx.Idents.get("NSObject");

SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);
}

bool
ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
return false;

ASTContext &Ctx = M.getState()->getStateManager().getContext();
initIdentifierInfoAndSelectors(Ctx);

return M.getSelector() == SELdealloc;
}

PathDiagnosticPiece *SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
const ExplodedNode *Pred,
BugReporterContext &BRC,
BugReport &BR) {
if (Satisfied)
return nullptr;

ProgramStateRef State = Succ->getState();

bool CalledNow =
Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
bool CalledBefore =
Pred->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);

// Is Succ the node on which the analyzer noted that [super dealloc] was
// called on ReceiverSymbol?
if (CalledNow && !CalledBefore) {
Satisfied = true;

ProgramPoint P = Succ->getLocation();
PathDiagnosticLocation L =
PathDiagnosticLocation::create(P, BRC.getSourceManager());

if (!L.isValid() || !L.asLocation().isValid())
return nullptr;

return new PathDiagnosticEventPiece(
L, "[super dealloc] called here");
}

return nullptr;
}

//===----------------------------------------------------------------------===//
// Checker Registration.
//===----------------------------------------------------------------------===//

void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
const LangOptions &LangOpts = Mgr.getLangOpts();
if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
return;
Mgr.registerChecker<ObjCSuperDeallocChecker>();
}

0 comments on commit eb6673c

Please sign in to comment.