22 using namespace clang;
26 class ObjCSuperDeallocChecker
27 :
public Checker<check::PostObjCMessage, check::PreObjCMessage,
28 check::PreCall, check::Location> {
33 std::unique_ptr<BugType> DoubleSuperDeallocBugType;
35 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
40 ObjCSuperDeallocChecker();
41 void checkPostObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
42 void checkPreObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
44 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
46 void checkLocation(SVal l,
bool isLoad,
const Stmt *S,
47 CheckerContext &C)
const;
51 void diagnoseCallArguments(
const CallEvent &CE, CheckerContext &C)
const;
53 void reportUseAfterDealloc(
SymbolRef Sym, StringRef Desc,
const Stmt *S,
54 CheckerContext &C)
const;
69 SuperDeallocBRVisitor(
SymbolRef ReceiverSymbol)
70 : ReceiverSymbol(ReceiverSymbol),
73 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *Succ,
74 BugReporterContext &BRC,
75 BugReport &BR)
override;
77 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
78 ID.Add(ReceiverSymbol);
83 void ObjCSuperDeallocChecker::checkPreObjCMessage(
const ObjCMethodCall &M,
84 CheckerContext &C)
const {
88 if (!ReceiverSymbol) {
89 diagnoseCallArguments(M, C);
93 bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
99 if (isSuperDeallocMessage(M)) {
100 Desc =
"[super dealloc] should not be called multiple times";
105 reportUseAfterDealloc(ReceiverSymbol, Desc, M.
getOriginExpr(), C);
108 void ObjCSuperDeallocChecker::checkPreCall(
const CallEvent &Call,
109 CheckerContext &C)
const {
110 diagnoseCallArguments(Call, C);
113 void ObjCSuperDeallocChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
114 CheckerContext &C)
const {
116 if (!isSuperDeallocMessage(M))
121 assert(ReceiverSymbol &&
"No receiver symbol at call to [super dealloc]?");
126 State = State->add<CalledSuperDealloc>(ReceiverSymbol);
127 C.addTransition(State);
130 void ObjCSuperDeallocChecker::checkLocation(SVal L,
bool IsLoad,
const Stmt *S,
131 CheckerContext &C)
const {
132 SymbolRef BaseSym = L.getLocSymbolInBase();
138 if (!State->contains<CalledSuperDealloc>(BaseSym))
141 const MemRegion *R = L.getAsRegion();
147 const MemRegion *PriorSubRegion =
nullptr;
148 while (
const SubRegion *SR = dyn_cast<SubRegion>(R)) {
149 if (
const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) {
150 BaseSym = SymR->getSymbol();
153 R = SR->getSuperRegion();
158 StringRef Desc = StringRef();
159 auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);
162 llvm::raw_string_ostream
OS(Buf);
164 OS <<
"Use of instance variable '" << *IvarRegion->getDecl() <<
165 "' after 'self' has been deallocated";
169 reportUseAfterDealloc(BaseSym, Desc, S, C);
175 void ObjCSuperDeallocChecker::reportUseAfterDealloc(
SymbolRef Sym,
178 CheckerContext &C)
const {
182 ExplodedNode *ErrNode = C.generateErrorNode();
188 Desc =
"Use of 'self' after it has been deallocated";
191 std::unique_ptr<BugReport> BR(
192 new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode));
194 BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym));
195 C.emitReport(std::move(BR));
200 void ObjCSuperDeallocChecker::diagnoseCallArguments(
const CallEvent &CE,
201 CheckerContext &C)
const {
203 unsigned ArgCount = CE.getNumArgs();
204 for (
unsigned I = 0; I < ArgCount; I++) {
205 SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();
209 if (State->contains<CalledSuperDealloc>(Sym)) {
210 reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);
216 ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
217 : IIdealloc(nullptr), IINSObject(nullptr) {
219 DoubleSuperDeallocBugType.reset(
220 new BugType(
this,
"[super dealloc] should not be called more than once",
225 ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const {
230 IINSObject = &Ctx.
Idents.
get(
"NSObject");
236 ObjCSuperDeallocChecker::isSuperDeallocMessage(
const ObjCMethodCall &M)
const {
240 ASTContext &Ctx = M.getState()->getStateManager().getContext();
241 initIdentifierInfoAndSelectors(Ctx);
246 std::shared_ptr<PathDiagnosticPiece>
247 SuperDeallocBRVisitor::VisitNode(
const ExplodedNode *Succ,
248 BugReporterContext &BRC, BugReport &) {
255 Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
257 Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(
262 if (CalledNow && !CalledBefore) {
266 PathDiagnosticLocation L =
269 if (!L.isValid() || !L.asLocation().isValid())
272 return std::make_shared<PathDiagnosticEventPiece>(
273 L,
"[super dealloc] called here");
283 void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
284 Mgr.registerChecker<ObjCSuperDeallocChecker>();
287 bool ento::shouldRegisterObjCSuperDeallocChecker(
const LangOptions &LO) {
SVal getSelfSVal() const
Return the value of 'self' if available.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const char *const CoreFoundationObjectiveC
Smart pointer class that efficiently represents Objective-C method names.
const SymExpr * SymbolRef
Stmt - This represents one statement.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Represents any expression that calls an Objective-C method.
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
SelectorTable & Selectors
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
virtual const ObjCMessageExpr * getOriginExpr() const
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Selector getSelector() const
Dataflow Directional Tag Classes.
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV)
Can create any sort of selector.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...