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), Satisfied(
false) {}
73 BugReporterContext &BRC,
74 PathSensitiveBugReport &BR)
override;
76 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
77 ID.Add(ReceiverSymbol);
82 void ObjCSuperDeallocChecker::checkPreObjCMessage(
const ObjCMethodCall &M,
83 CheckerContext &C)
const {
87 if (!ReceiverSymbol) {
88 diagnoseCallArguments(M, C);
92 bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
98 if (isSuperDeallocMessage(M)) {
99 Desc =
"[super dealloc] should not be called multiple times";
104 reportUseAfterDealloc(ReceiverSymbol, Desc, M.
getOriginExpr(), C);
107 void ObjCSuperDeallocChecker::checkPreCall(
const CallEvent &Call,
108 CheckerContext &C)
const {
109 diagnoseCallArguments(Call, C);
112 void ObjCSuperDeallocChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
113 CheckerContext &C)
const {
115 if (!isSuperDeallocMessage(M))
120 assert(ReceiverSymbol &&
"No receiver symbol at call to [super dealloc]?");
125 State = State->add<CalledSuperDealloc>(ReceiverSymbol);
126 C.addTransition(State);
129 void ObjCSuperDeallocChecker::checkLocation(SVal L,
bool IsLoad,
const Stmt *S,
130 CheckerContext &C)
const {
131 SymbolRef BaseSym = L.getLocSymbolInBase();
137 if (!State->contains<CalledSuperDealloc>(BaseSym))
140 const MemRegion *R = L.getAsRegion();
146 const MemRegion *PriorSubRegion =
nullptr;
147 while (
const SubRegion *SR = dyn_cast<SubRegion>(R)) {
148 if (
const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) {
149 BaseSym = SymR->getSymbol();
152 R = SR->getSuperRegion();
157 StringRef Desc = StringRef();
158 auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);
161 llvm::raw_string_ostream
OS(Buf);
163 OS <<
"Use of instance variable '" << *IvarRegion->getDecl() <<
164 "' after 'self' has been deallocated";
168 reportUseAfterDealloc(BaseSym, Desc, S, C);
174 void ObjCSuperDeallocChecker::reportUseAfterDealloc(
SymbolRef Sym,
177 CheckerContext &C)
const {
181 ExplodedNode *ErrNode = C.generateErrorNode();
187 Desc =
"Use of 'self' after it has been deallocated";
190 auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType,
193 BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym));
194 C.emitReport(std::move(BR));
199 void ObjCSuperDeallocChecker::diagnoseCallArguments(
const CallEvent &CE,
200 CheckerContext &C)
const {
202 unsigned ArgCount = CE.getNumArgs();
203 for (
unsigned I = 0; I < ArgCount; I++) {
204 SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();
208 if (State->contains<CalledSuperDealloc>(Sym)) {
209 reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);
215 ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
216 : IIdealloc(nullptr), IINSObject(nullptr) {
218 DoubleSuperDeallocBugType.reset(
219 new BugType(
this,
"[super dealloc] should not be called more than once",
224 ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const {
229 IINSObject = &Ctx.
Idents.
get(
"NSObject");
235 ObjCSuperDeallocChecker::isSuperDeallocMessage(
const ObjCMethodCall &M)
const {
239 ASTContext &Ctx = M.getState()->getStateManager().getContext();
240 initIdentifierInfoAndSelectors(Ctx);
246 SuperDeallocBRVisitor::VisitNode(
const ExplodedNode *Succ,
247 BugReporterContext &BRC,
248 PathSensitiveBugReport &) {
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.
Stmt - This represents one statement.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
One of these records is kept for each identifier that is lexed.
const SymExpr * SymbolRef
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...
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
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...