clang 24.0.0git
CheckObjCDealloc.cpp
Go to the documentation of this file.
1//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This checker analyzes Objective-C -dealloc methods and their callees
10// to warn about improper releasing of instance variables that back synthesized
11// properties. It warns about missing releases in the following cases:
12// - When a class has a synthesized instance variable for a 'retain' or 'copy'
13// property and lacks a -dealloc method in its implementation.
14// - When a class has a synthesized instance variable for a 'retain'/'copy'
15// property but the ivar is not released in -dealloc by either -release
16// or by nilling out the property.
17//
18// It warns about extra releases in -dealloc (but not in callees) when a
19// synthesized instance variable is released in the following cases:
20// - When the property is 'assign' and is not 'readonly'.
21// - When the property is 'weak'.
22//
23// This checker only warns for instance variables synthesized to back
24// properties. Handling the more general case would require inferring whether
25// an instance variable is stored retained or not. For synthesized properties,
26// this is specified in the property declaration itself.
27//
28//===----------------------------------------------------------------------===//
29
30#include "clang/AST/Attr.h"
31#include "clang/AST/DeclObjC.h"
32#include "clang/AST/Expr.h"
33#include "clang/AST/ExprObjC.h"
47#include "llvm/ADT/STLExtras.h"
48#include "llvm/Support/raw_ostream.h"
49#include <optional>
50
51using namespace clang;
52using namespace ento;
53
54/// Indicates whether an instance variable is required to be released in
55/// -dealloc.
57 /// The instance variable must be released, either by calling
58 /// -release on it directly or by nilling it out with a property setter.
60
61 /// The instance variable must not be directly released with -release.
63
64 /// The requirement for the instance variable could not be determined.
66};
67
68/// Returns true if the property implementation is synthesized and the
69/// type of the property is retainable.
71 const ObjCIvarDecl **ID,
72 const ObjCPropertyDecl **PD) {
73
75 return false;
76
77 (*ID) = I->getPropertyIvarDecl();
78 if (!(*ID))
79 return false;
80
81 QualType T = (*ID)->getType();
82 if (!T->isObjCRetainableType())
83 return false;
84
85 (*PD) = I->getPropertyDecl();
86 // Shouldn't be able to synthesize a property that doesn't exist.
87 assert(*PD);
88
89 return true;
90}
91
92namespace {
93
94class ObjCDeallocChecker
95 : public Checker<check::ASTDecl<ObjCImplementationDecl>,
96 check::PreObjCMessage, check::PostObjCMessage,
97 check::PreCall,
98 check::BeginFunction, check::EndFunction,
99 eval::Assume,
100 check::PointerEscape,
101 check::PreStmt<ReturnStmt>> {
102
103 mutable const IdentifierInfo *NSObjectII = nullptr;
104 mutable const IdentifierInfo *SenTestCaseII = nullptr;
105 mutable const IdentifierInfo *XCTestCaseII = nullptr;
106 mutable const IdentifierInfo *Block_releaseII = nullptr;
107 mutable const IdentifierInfo *CIFilterII = nullptr;
108
109 mutable Selector DeallocSel;
110 mutable Selector ReleaseSel;
111
112 const BugType MissingReleaseBugType{this, "Missing ivar release (leak)",
114 const BugType ExtraReleaseBugType{this, "Extra ivar release",
116 const BugType MistakenDeallocBugType{this, "Mistaken dealloc",
118
119public:
120 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
121 BugReporter &BR) const;
122 void checkBeginFunction(CheckerContext &Ctx) const;
123 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
124 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
125 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
126
127 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
128 bool Assumption) const;
129
130 ProgramStateRef checkPointerEscape(ProgramStateRef State,
131 const InvalidatedSymbols &Escaped,
132 const CallEvent *Call,
133 PointerEscapeKind Kind) const;
134 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
135 void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
136
137private:
138 void diagnoseMissingReleases(CheckerContext &C) const;
139
140 bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
141 CheckerContext &C) const;
142
143 bool diagnoseMistakenDealloc(SymbolRef DeallocedValue,
144 const ObjCMethodCall &M,
145 CheckerContext &C) const;
146
147 SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
148 CheckerContext &C) const;
149
150 const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const;
151 SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
152
153 const ObjCPropertyImplDecl*
154 findPropertyOnDeallocatingInstance(SymbolRef IvarSym,
155 CheckerContext &C) const;
156
158 getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
159
160 bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const;
161 bool isInInstanceDealloc(const CheckerContext &C, const StackFrame *SF,
162 SVal &SelfValOut) const;
163 bool instanceDeallocIsOnStack(const CheckerContext &C,
164 SVal &InstanceValOut) const;
165
166 bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
167
168 const ObjCImplDecl *getContainingObjCImpl(const StackFrame *SF) const;
169
170 const ObjCPropertyDecl *
171 findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const;
172
173 void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const;
174 ProgramStateRef removeValueRequiringRelease(ProgramStateRef State,
175 SymbolRef InstanceSym,
176 SymbolRef ValueSym) const;
177
178 void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
179
180 bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
181
182 bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const;
183 bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const;
184};
185} // End anonymous namespace.
186
187
188/// Maps from the symbol for a class instance to the set of
189/// symbols remaining that must be released in -dealloc.
192
193
194/// An AST check that diagnose when the class requires a -dealloc method and
195/// is missing one.
196void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D,
197 AnalysisManager &Mgr,
198 BugReporter &BR) const {
199 assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly);
200 assert(!Mgr.getLangOpts().ObjCAutoRefCount);
201 initIdentifierInfoAndSelectors(Mgr.getASTContext());
202
203 const ObjCInterfaceDecl *ID = D->getClassInterface();
204 // If the class is known to have a lifecycle with a separate teardown method
205 // then it may not require a -dealloc method.
206 if (classHasSeparateTeardown(ID))
207 return;
208
209 // Does the class contain any synthesized properties that are retainable?
210 // If not, skip the check entirely.
211 const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr;
212 bool HasOthers = false;
213 for (const auto *I : D->property_impls()) {
214 if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) {
215 if (!PropImplRequiringRelease)
216 PropImplRequiringRelease = I;
217 else {
218 HasOthers = true;
219 break;
220 }
221 }
222 }
223
224 if (!PropImplRequiringRelease)
225 return;
226
227 const ObjCMethodDecl *MD = nullptr;
228
229 // Scan the instance methods for "dealloc".
230 for (const auto *I : D->instance_methods()) {
231 if (I->getSelector() == DeallocSel) {
232 MD = I;
233 break;
234 }
235 }
236
237 if (!MD) { // No dealloc found.
238 const char* Name = "Missing -dealloc";
239
240 std::string Buf;
241 llvm::raw_string_ostream OS(Buf);
242 OS << "'" << *D << "' lacks a 'dealloc' instance method but "
243 << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl()
244 << "'";
245
246 if (HasOthers)
247 OS << " and others";
248 PathDiagnosticLocation DLoc =
250
252 DLoc);
253 return;
254 }
255}
256
257/// If this is the beginning of -dealloc, mark the values initially stored in
258/// instance variables that must be released by the end of -dealloc
259/// as unreleased in the state.
260void ObjCDeallocChecker::checkBeginFunction(
261 CheckerContext &C) const {
262 initIdentifierInfoAndSelectors(C.getASTContext());
263
264 // Only do this if the current method is -dealloc.
265 SVal SelfVal;
266 if (!isInInstanceDealloc(C, SelfVal))
267 return;
268
269 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
270
271 const StackFrame *SF = C.getStackFrame();
272 ProgramStateRef InitialState = C.getState();
273
274 ProgramStateRef State = InitialState;
275
276 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
277
278 // Symbols that must be released by the end of the -dealloc;
279 SymbolSet RequiredReleases = F.getEmptySet();
280
281 // If we're an inlined -dealloc, we should add our symbols to the existing
282 // set from our subclass.
283 if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
284 RequiredReleases = *CurrSet;
285
286 for (auto *PropImpl : getContainingObjCImpl(SF)->property_impls()) {
287 ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl);
288 if (Requirement != ReleaseRequirement::MustRelease)
289 continue;
290
291 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
292 std::optional<Loc> LValLoc = LVal.getAs<Loc>();
293 if (!LValLoc)
294 continue;
295
296 SVal InitialVal = State->getSVal(*LValLoc);
297 SymbolRef Symbol = InitialVal.getAsSymbol();
298 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
299 continue;
300
301 // Mark the value as requiring a release.
302 RequiredReleases = F.add(RequiredReleases, Symbol);
303 }
304
305 if (!RequiredReleases.isEmpty()) {
306 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
307 }
308
309 if (State != InitialState) {
310 C.addTransition(State);
311 }
312}
313
314/// Given a symbol for an ivar, return the ivar region it was loaded from.
315/// Returns nullptr if the instance symbol cannot be found.
316const ObjCIvarRegion *
317ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const {
318 return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion());
319}
320
321/// Given a symbol for an ivar, return a symbol for the instance containing
322/// the ivar. Returns nullptr if the instance symbol cannot be found.
324ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
325
326 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
327 if (!IvarRegion)
328 return nullptr;
329
330 const SymbolicRegion *SR = IvarRegion->getSymbolicBase();
331 assert(SR && "Symbolic base should not be nullptr");
332 return SR->getSymbol();
333}
334
335/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
336/// a release or a nilling-out property setter.
337void ObjCDeallocChecker::checkPreObjCMessage(
338 const ObjCMethodCall &M, CheckerContext &C) const {
339 // Only run if -dealloc is on the stack.
340 SVal DeallocedInstance;
341 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
342 return;
343
344 SymbolRef ReleasedValue = nullptr;
345
346 if (M.getSelector() == ReleaseSel) {
347 ReleasedValue = M.getReceiverSVal().getAsSymbol();
348 } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) {
349 if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C))
350 return;
351 }
352
353 if (ReleasedValue) {
354 // An instance variable symbol was released with -release:
355 // [_property release];
356 if (diagnoseExtraRelease(ReleasedValue,M, C))
357 return;
358 } else {
359 // An instance variable symbol was released nilling out its property:
360 // self.property = nil;
361 ReleasedValue = getValueReleasedByNillingOut(M, C);
362 }
363
364 if (!ReleasedValue)
365 return;
366
367 transitionToReleaseValue(C, ReleasedValue);
368}
369
370/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
371/// call to Block_release().
372void ObjCDeallocChecker::checkPreCall(const CallEvent &Call,
373 CheckerContext &C) const {
374 const IdentifierInfo *II = Call.getCalleeIdentifier();
375 if (II != Block_releaseII)
376 return;
377
378 if (Call.getNumArgs() != 1)
379 return;
380
381 SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol();
382 if (!ReleasedValue)
383 return;
384
385 transitionToReleaseValue(C, ReleasedValue);
386}
387/// If the message was a call to '[super dealloc]', diagnose any missing
388/// releases.
389void ObjCDeallocChecker::checkPostObjCMessage(
390 const ObjCMethodCall &M, CheckerContext &C) const {
391 // We perform this check post-message so that if the super -dealloc
392 // calls a helper method and that this class overrides, any ivars released in
393 // the helper method will be recorded before checking.
394 if (isSuperDeallocMessage(M))
395 diagnoseMissingReleases(C);
396}
397
398/// Check for missing releases even when -dealloc does not call
399/// '[super dealloc]'.
400void ObjCDeallocChecker::checkEndFunction(
401 const ReturnStmt *RS, CheckerContext &C) const {
402 diagnoseMissingReleases(C);
403}
404
405/// Check for missing releases on early return.
406void ObjCDeallocChecker::checkPreStmt(
407 const ReturnStmt *RS, CheckerContext &C) const {
408 diagnoseMissingReleases(C);
409}
410
411/// When a symbol is assumed to be nil, remove it from the set of symbols
412/// require to be nil.
413ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond,
414 bool Assumption) const {
415 if (State->get<UnreleasedIvarMap>().isEmpty())
416 return State;
417
418 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
419 if (!CondBSE)
420 return State;
421
422 BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
423 if (Assumption) {
424 if (OpCode != BO_EQ)
425 return State;
426 } else {
427 if (OpCode != BO_NE)
428 return State;
429 }
430
431 SymbolRef NullSymbol = nullptr;
432 if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
433 const llvm::APInt &RHS = SIE->getRHS();
434 if (RHS != 0)
435 return State;
436 NullSymbol = SIE->getLHS();
437 } else if (auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
438 const llvm::APInt &LHS = SIE->getLHS();
439 if (LHS != 0)
440 return State;
441 NullSymbol = SIE->getRHS();
442 } else {
443 return State;
444 }
445
446 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
447 if (!InstanceSymbol)
448 return State;
449
450 State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
451
452 return State;
453}
454
455/// If a symbol escapes conservatively assume unseen code released it.
456ProgramStateRef ObjCDeallocChecker::checkPointerEscape(
457 ProgramStateRef State, const InvalidatedSymbols &Escaped,
458 const CallEvent *Call, PointerEscapeKind Kind) const {
459
460 if (State->get<UnreleasedIvarMap>().isEmpty())
461 return State;
462
463 // Don't treat calls to '[super dealloc]' as escaping for the purposes
464 // of this checker. Because the checker diagnoses missing releases in the
465 // post-message handler for '[super dealloc], escaping here would cause
466 // the checker to never warn.
467 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
468 if (OMC && isSuperDeallocMessage(*OMC))
469 return State;
470
471 for (const auto &Sym : Escaped) {
472 if (!Call || (Call && !Call->isInSystemHeader())) {
473 // If Sym is a symbol for an object with instance variables that
474 // must be released, remove these obligations when the object escapes
475 // unless via a call to a system function. System functions are
476 // very unlikely to release instance variables on objects passed to them,
477 // and are frequently called on 'self' in -dealloc (e.g., to remove
478 // observers) -- we want to avoid false negatives from escaping on
479 // them.
480 State = State->remove<UnreleasedIvarMap>(Sym);
481 }
482
483
484 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
485 if (!InstanceSymbol)
486 continue;
487
488 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
489 }
490
491 return State;
492}
493
494/// Report any unreleased instance variables for the current instance being
495/// dealloced.
496void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
497 ProgramStateRef State = C.getState();
498
499 SVal SelfVal;
500 if (!isInInstanceDealloc(C, SelfVal))
501 return;
502
503 const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
504 const StackFrame *SF = C.getStackFrame();
505
506 ExplodedNode *ErrNode = nullptr;
507
508 SymbolRef SelfSym = SelfVal.getAsSymbol();
509 if (!SelfSym)
510 return;
511
512 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
513 if (!OldUnreleased)
514 return;
515
516 SymbolSet NewUnreleased = *OldUnreleased;
517 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
518
519 ProgramStateRef InitialState = State;
520
521 for (auto *IvarSymbol : *OldUnreleased) {
522 const TypedValueRegion *TVR =
523 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
524 const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
525
526 // Don't warn if the ivar is not for this instance.
527 if (SelfRegion != IvarRegion->getSuperRegion())
528 continue;
529
530 const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
531 // Prevent an inlined call to -dealloc in a super class from warning
532 // about the values the subclass's -dealloc should release.
533 if (IvarDecl->getContainingInterface() !=
534 cast<ObjCMethodDecl>(SF->getDecl())->getClassInterface())
535 continue;
536
537 // Prevents diagnosing multiple times for the same instance variable
538 // at, for example, both a return and at the end of the function.
539 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
540
541 if (State->getStateManager()
542 .getConstraintManager()
543 .isNull(State, IvarSymbol)
544 .isConstrainedTrue()) {
545 continue;
546 }
547
548 // A missing release manifests as a leak, so treat as a non-fatal error.
549 if (!ErrNode)
550 ErrNode = C.generateNonFatalErrorNode();
551 // If we've already reached this node on another path, return without
552 // diagnosing.
553 if (!ErrNode)
554 return;
555
556 std::string Buf;
557 llvm::raw_string_ostream OS(Buf);
558
559 const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface();
560 // If the class is known to have a lifecycle with teardown that is
561 // separate from -dealloc, do not warn about missing releases. We
562 // suppress here (rather than not tracking for instance variables in
563 // such classes) because these classes are rare.
564 if (classHasSeparateTeardown(Interface))
565 return;
566
567 ObjCImplDecl *ImplDecl = Interface->getImplementation();
568
569 const ObjCPropertyImplDecl *PropImpl =
570 ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
571
572 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
573
574 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy ||
576
577 OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl
578 << "' was ";
579
580 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain)
581 OS << "retained";
582 else
583 OS << "copied";
584
585 OS << " by a synthesized property but not released"
586 " before '[super dealloc]'";
587
588 auto BR = std::make_unique<PathSensitiveBugReport>(MissingReleaseBugType,
589 Buf, ErrNode);
590 C.emitReport(std::move(BR));
591 }
592
593 if (NewUnreleased.isEmpty()) {
594 State = State->remove<UnreleasedIvarMap>(SelfSym);
595 } else {
596 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
597 }
598
599 if (ErrNode) {
600 C.addTransition(State, ErrNode);
601 } else if (State != InitialState) {
602 C.addTransition(State);
603 }
604
605 // Make sure that after checking in the top-most frame the list of
606 // tracked ivars is empty. This is intended to detect accidental leaks in
607 // the UnreleasedIvarMap program state.
608 assert(!SF->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
609}
610
611/// Given a symbol, determine whether the symbol refers to an ivar on
612/// the top-most deallocating instance. If so, find the property for that
613/// ivar, if one exists. Otherwise return null.
614const ObjCPropertyImplDecl *
615ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
616 SymbolRef IvarSym, CheckerContext &C) const {
617 SVal DeallocedInstance;
618 if (!isInInstanceDealloc(C, DeallocedInstance))
619 return nullptr;
620
621 // Try to get the region from which the ivar value was loaded.
622 auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
623 if (!IvarRegion)
624 return nullptr;
625
626 // Don't try to find the property if the ivar was not loaded from the
627 // given instance.
628 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
629 IvarRegion->getSuperRegion())
630 return nullptr;
631
632 const StackFrame *SF = C.getStackFrame();
633 const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
634
635 const ObjCImplDecl *Container = getContainingObjCImpl(SF);
636 const ObjCPropertyImplDecl *PropImpl =
637 Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
638 return PropImpl;
639}
640
641/// Emits a warning if the current context is -dealloc and ReleasedValue
642/// must not be directly released in a -dealloc. Returns true if a diagnostic
643/// was emitted.
644bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
645 const ObjCMethodCall &M,
646 CheckerContext &C) const {
647 // Try to get the region from which the released value was loaded.
648 // Note that, unlike diagnosing for missing releases, here we don't track
649 // values that must not be released in the state. This is because even if
650 // these values escape, it is still an error under the rules of MRR to
651 // release them in -dealloc.
652 const ObjCPropertyImplDecl *PropImpl =
653 findPropertyOnDeallocatingInstance(ReleasedValue, C);
654
655 if (!PropImpl)
656 return false;
657
658 // If the ivar belongs to a property that must not be released directly
659 // in dealloc, emit a warning.
660 if (getDeallocReleaseRequirement(PropImpl) !=
662 return false;
663 }
664
665 // If the property is readwrite but it shadows a read-only property in its
666 // external interface, treat the property a read-only. If the outside
667 // world cannot write to a property then the internal implementation is free
668 // to make its own convention about whether the value is stored retained
669 // or not. We look up the shadow here rather than in
670 // getDeallocReleaseRequirement() because doing so can be expensive.
671 const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl);
672 if (PropDecl) {
673 if (PropDecl->isReadOnly())
674 return false;
675 } else {
676 PropDecl = PropImpl->getPropertyDecl();
677 }
678
679 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
680 if (!ErrNode)
681 return false;
682
683 std::string Buf;
684 llvm::raw_string_ostream OS(Buf);
685
686 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
687 (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
688 !PropDecl->isReadOnly()) ||
689 isReleasedByCIFilterDealloc(PropImpl)
690 );
691
692 const ObjCImplDecl *Container = getContainingObjCImpl(C.getStackFrame());
693 OS << "The '" << *PropImpl->getPropertyIvarDecl()
694 << "' ivar in '" << *Container;
695
696
697 if (isReleasedByCIFilterDealloc(PropImpl)) {
698 OS << "' will be released by '-[CIFilter dealloc]' but also released here";
699 } else {
700 OS << "' was synthesized for ";
701
702 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
703 OS << "a weak";
704 else
705 OS << "an assign, readwrite";
706
707 OS << " property but was released in 'dealloc'";
708 }
709
710 auto BR = std::make_unique<PathSensitiveBugReport>(ExtraReleaseBugType, Buf,
711 ErrNode);
712 BR->addRange(M.getOriginExpr()->getSourceRange());
713
714 C.emitReport(std::move(BR));
715
716 return true;
717}
718
719/// Emits a warning if the current context is -dealloc and DeallocedValue
720/// must not be directly dealloced in a -dealloc. Returns true if a diagnostic
721/// was emitted.
722bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
723 const ObjCMethodCall &M,
724 CheckerContext &C) const {
725 // TODO: Apart from unknown/undefined receivers, this may happen when
726 // dealloc is called as a class method. Should we warn?
727 if (!DeallocedValue)
728 return false;
729
730 // Find the property backing the instance variable that M
731 // is dealloc'ing.
732 const ObjCPropertyImplDecl *PropImpl =
733 findPropertyOnDeallocatingInstance(DeallocedValue, C);
734 if (!PropImpl)
735 return false;
736
737 if (getDeallocReleaseRequirement(PropImpl) !=
739 return false;
740 }
741
742 ExplodedNode *ErrNode = C.generateErrorNode();
743 if (!ErrNode)
744 return false;
745
746 std::string Buf;
747 llvm::raw_string_ostream OS(Buf);
748
749 OS << "'" << *PropImpl->getPropertyIvarDecl()
750 << "' should be released rather than deallocated";
751
752 auto BR = std::make_unique<PathSensitiveBugReport>(MistakenDeallocBugType,
753 Buf, ErrNode);
754 BR->addRange(M.getOriginExpr()->getSourceRange());
755
756 C.emitReport(std::move(BR));
757
758 return true;
759}
760
761void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
762 ASTContext &Ctx) const {
763 if (NSObjectII)
764 return;
765
766 NSObjectII = &Ctx.Idents.get("NSObject");
767 SenTestCaseII = &Ctx.Idents.get("SenTestCase");
768 XCTestCaseII = &Ctx.Idents.get("XCTestCase");
769 Block_releaseII = &Ctx.Idents.get("_Block_release");
770 CIFilterII = &Ctx.Idents.get("CIFilter");
771
772 const IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
773 const IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
774 DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII);
775 ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII);
776}
777
778/// Returns true if M is a call to '[super dealloc]'.
779bool ObjCDeallocChecker::isSuperDeallocMessage(
780 const ObjCMethodCall &M) const {
782 return false;
783
784 return M.getSelector() == DeallocSel;
785}
786
787/// Returns the ObjCImplDecl containing the method declaration in SF.
788const ObjCImplDecl *
789ObjCDeallocChecker::getContainingObjCImpl(const StackFrame *SF) const {
790 auto *MD = cast<ObjCMethodDecl>(SF->getDecl());
792}
793
794/// Returns the property that shadowed by PropImpl if one exists and
795/// nullptr otherwise.
796const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
797 const ObjCPropertyImplDecl *PropImpl) const {
798 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
799
800 // Only readwrite properties can shadow.
801 if (PropDecl->isReadOnly())
802 return nullptr;
803
804 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext());
805
806 // Only class extensions can contain shadowing properties.
807 if (!CatDecl || !CatDecl->IsClassExtension())
808 return nullptr;
809
810 IdentifierInfo *ID = PropDecl->getIdentifier();
811 DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
812 for (const NamedDecl *D : R) {
813 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(D);
814 if (!ShadowedPropDecl)
815 continue;
816
817 if (ShadowedPropDecl->isInstanceProperty()) {
818 assert(ShadowedPropDecl->isReadOnly());
819 return ShadowedPropDecl;
820 }
821 }
822
823 return nullptr;
824}
825
826/// Add a transition noting the release of the given value.
827void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
828 SymbolRef Value) const {
829 assert(Value);
830 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
831 if (!InstanceSym)
832 return;
833 ProgramStateRef InitialState = C.getState();
834
835 ProgramStateRef ReleasedState =
836 removeValueRequiringRelease(InitialState, InstanceSym, Value);
837
838 if (ReleasedState != InitialState) {
839 C.addTransition(ReleasedState);
840 }
841}
842
843/// Remove the Value requiring a release from the tracked set for
844/// Instance and return the resultant state.
845ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease(
846 ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const {
847 assert(Instance);
848 assert(Value);
849 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
850 if (!RemovedRegion)
851 return State;
852
853 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
854 if (!Unreleased)
855 return State;
856
857 // Mark the value as no longer requiring a release.
858 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
859 SymbolSet NewUnreleased = *Unreleased;
860 for (auto &Sym : *Unreleased) {
861 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
862 assert(UnreleasedRegion);
863 if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
864 NewUnreleased = F.remove(NewUnreleased, Sym);
865 }
866 }
867
868 if (NewUnreleased.isEmpty()) {
869 return State->remove<UnreleasedIvarMap>(Instance);
870 }
871
872 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
873}
874
875/// Determines whether the instance variable for \p PropImpl must or must not be
876/// released in -dealloc or whether it cannot be determined.
877ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
878 const ObjCPropertyImplDecl *PropImpl) const {
879 const ObjCIvarDecl *IvarDecl;
880 const ObjCPropertyDecl *PropDecl;
881 if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl))
883
885
886 switch (SK) {
887 // Retain and copy setters retain/copy their values before storing and so
888 // the value in their instance variables must be released in -dealloc.
891 if (isReleasedByCIFilterDealloc(PropImpl))
893
894 if (isNibLoadedIvarWithoutRetain(PropImpl))
896
898
901
903 // It is common for the ivars for read-only assign properties to
904 // always be stored retained, so their release requirement cannot be
905 // be determined.
906 if (PropDecl->isReadOnly())
908
910 }
911 llvm_unreachable("Unrecognized setter kind");
912}
913
914/// Returns the released value if M is a call a setter that releases
915/// and nils out its underlying instance variable.
917ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
918 CheckerContext &C) const {
919 SVal ReceiverVal = M.getReceiverSVal();
920 if (!ReceiverVal.isValid())
921 return nullptr;
922
923 if (M.getNumArgs() == 0)
924 return nullptr;
925
927 return nullptr;
928
929 // Is the first argument nil?
930 SVal Arg = M.getArgSVal(0);
931 ProgramStateRef notNilState, nilState;
932 std::tie(notNilState, nilState) =
933 C.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
934 if (!(nilState && !notNilState))
935 return nullptr;
936
937 const ObjCPropertyDecl *Prop = M.getAccessedProperty();
938 if (!Prop)
939 return nullptr;
940
941 ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
942 if (!PropIvarDecl)
943 return nullptr;
944
945 ProgramStateRef State = C.getState();
946
947 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
948 std::optional<Loc> LValLoc = LVal.getAs<Loc>();
949 if (!LValLoc)
950 return nullptr;
951
952 SVal CurrentValInIvar = State->getSVal(*LValLoc);
953 return CurrentValInIvar.getAsSymbol();
954}
955
956/// Returns true if the current context is a call to -dealloc and false
957/// otherwise. If true, it also sets SelfValOut to the value of
958/// 'self'.
959bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
960 SVal &SelfValOut) const {
961 return isInInstanceDealloc(C, C.getStackFrame(), SelfValOut);
962}
963
964/// Returns true if SF is a call to -dealloc and false
965/// otherwise. If true, it also sets SelfValOut to the value of
966/// 'self'.
967bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
968 const StackFrame *SF,
969 SVal &SelfValOut) const {
970 auto *MD = dyn_cast<ObjCMethodDecl>(SF->getDecl());
971 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
972 return false;
973
974 const ImplicitParamDecl *SelfDecl = SF->getSelfDecl();
975 assert(SelfDecl && "No self in -dealloc?");
976
977 ProgramStateRef State = C.getState();
978 SelfValOut = State->getSVal(State->getRegion(SelfDecl, SF));
979 return true;
980}
981
982/// Returns true if there is a call to -dealloc anywhere on the stack and false
983/// otherwise. If true, it also sets InstanceValOut to the value of
984/// 'self' in the frame for -dealloc.
985bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
986 SVal &InstanceValOut) const {
987 return llvm::any_of(C.stackframes(), [&](const StackFrame &SF) {
988 return isInInstanceDealloc(C, &SF, InstanceValOut);
989 });
990}
991
992/// Returns true if the ID is a class in which is known to have
993/// a separate teardown lifecycle. In this case, -dealloc warnings
994/// about missing releases should be suppressed.
995bool ObjCDeallocChecker::classHasSeparateTeardown(
996 const ObjCInterfaceDecl *ID) const {
997 // Suppress if the class is not a subclass of NSObject.
998 for ( ; ID ; ID = ID->getSuperClass()) {
999 IdentifierInfo *II = ID->getIdentifier();
1000
1001 if (II == NSObjectII)
1002 return false;
1003
1004 // FIXME: For now, ignore classes that subclass SenTestCase and XCTestCase,
1005 // as these don't need to implement -dealloc. They implement tear down in
1006 // another way, which we should try and catch later.
1007 // http://llvm.org/bugs/show_bug.cgi?id=3187
1008 if (II == XCTestCaseII || II == SenTestCaseII)
1009 return true;
1010 }
1011
1012 return true;
1013}
1014
1015/// The -dealloc method in CIFilter highly unusual in that is will release
1016/// instance variables belonging to its *subclasses* if the variable name
1017/// starts with "input" or backs a property whose name starts with "input".
1018/// Subclasses should not release these ivars in their own -dealloc method --
1019/// doing so could result in an over release.
1020///
1021/// This method returns true if the property will be released by
1022/// -[CIFilter dealloc].
1023bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
1024 const ObjCPropertyImplDecl *PropImpl) const {
1025 assert(PropImpl->getPropertyIvarDecl());
1026 StringRef PropName = PropImpl->getPropertyDecl()->getName();
1027 StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName();
1028
1029 const char *ReleasePrefix = "input";
1030 if (!(PropName.starts_with(ReleasePrefix) ||
1031 IvarName.starts_with(ReleasePrefix))) {
1032 return false;
1033 }
1034
1035 const ObjCInterfaceDecl *ID =
1037 for ( ; ID ; ID = ID->getSuperClass()) {
1038 IdentifierInfo *II = ID->getIdentifier();
1039 if (II == CIFilterII)
1040 return true;
1041 }
1042
1043 return false;
1044}
1045
1046/// Returns whether the ivar backing the property is an IBOutlet that
1047/// has its value set by nib loading code without retaining the value.
1048///
1049/// On macOS, if there is no setter, the nib-loading code sets the ivar
1050/// directly, without retaining the value,
1051///
1052/// On iOS and its derivatives, the nib-loading code will call
1053/// -setValue:forKey:, which retains the value before directly setting the ivar.
1054bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
1055 const ObjCPropertyImplDecl *PropImpl) const {
1056 const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl();
1057 if (!IvarDecl->hasAttr<IBOutletAttr>())
1058 return false;
1059
1060 const llvm::Triple &Target =
1061 IvarDecl->getASTContext().getTargetInfo().getTriple();
1062
1063 if (!Target.isMacOSX())
1064 return false;
1065
1066 if (PropImpl->getPropertyDecl()->getSetterMethodDecl())
1067 return false;
1068
1069 return true;
1070}
1071
1072void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
1073 Mgr.registerChecker<ObjCDeallocChecker>();
1074}
1075
1076bool ento::shouldRegisterObjCDeallocChecker(const CheckerManager &mgr) {
1077 // These checker only makes sense under MRR.
1078 const LangOptions &LO = mgr.getLangOpts();
1079 return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount;
1080}
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
ReleaseRequirement
Indicates whether an instance variable is required to be released in -dealloc.
@ MustNotReleaseDirectly
The instance variable must not be directly released with -release.
@ Unknown
The requirement for the instance variable could not be determined.
@ MustRelease
The instance variable must be released, either by calling -release on it directly or by nilling it ou...
static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, const ObjCIvarDecl **ID, const ObjCPropertyDecl **PD)
Returns true if the property implementation is synthesized and the type of the property is retainable...
Defines the clang::LangOptions interface.
llvm::MachO::SymbolSet SymbolSet
Definition MachO.h:44
llvm::MachO::Target Target
Definition MachO.h:51
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set type Name and registers the factory for such sets in the program state,...
IdentifierTable & Idents
Definition ASTContext.h:808
SelectorTable & Selectors
Definition ASTContext.h:809
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:927
BinaryOperatorKind Opcode
Definition Expr.h:4049
DeclContextLookupResult lookup_result
Definition DeclBase.h:2607
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:550
DeclContext * getDeclContext()
Definition DeclBase.h:456
bool hasAttr() const
Definition DeclBase.h:585
QualType getType() const
Definition Expr.h:144
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition Decl.h:295
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
instmeth_range instance_methods() const
Definition DeclObjC.h:1039
propimpl_range property_impls() const
Definition DeclObjC.h:2519
const ObjCInterfaceDecl * getClassInterface() const
Definition DeclObjC.h:2492
ObjCPropertyImplDecl * FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const
FindPropertyImplIvarDecl - This method lookup the ivar in the list of properties implemented in this ...
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition DeclObjC.h:2603
ObjCImplementationDecl * getImplementation() const
ObjCIvarDecl - Represents an ObjC instance variable.
Definition DeclObjC.h:1958
ObjCInterfaceDecl * getContainingInterface()
Return the class interface that this ivar is logically contained in; this is either the interface whe...
@ SuperInstance
The receiver is the instance of the superclass object.
Definition ExprObjC.h:987
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
Definition ExprObjC.h:1262
Selector getSelector() const
Definition DeclObjC.h:330
bool isInstanceMethod() const
Definition DeclObjC.h:429
Represents one property declaration in an Objective-C interface.
Definition DeclObjC.h:734
ObjCMethodDecl * getSetterMethodDecl() const
Definition DeclObjC.h:910
bool isReadOnly() const
isReadOnly - Return true iff the property has a setter.
Definition DeclObjC.h:844
ObjCIvarDecl * getPropertyIvarDecl() const
Definition DeclObjC.h:930
SetterKind getSetterKind() const
getSetterKind - Return the method used for doing assignment in the property setter.
Definition DeclObjC.h:879
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition DeclObjC.h:2811
ObjCIvarDecl * getPropertyIvarDecl() const
Definition DeclObjC.h:2885
Kind getPropertyImplementation() const
Definition DeclObjC.h:2881
ObjCPropertyDecl * getPropertyDecl() const
Definition DeclObjC.h:2876
A (possibly-)qualified type.
Definition TypeBase.h:938
Selector getSelector(unsigned NumArgs, const IdentifierInfo **IIV)
Can create any sort of selector.
const Decl * getDecl() const
const ImplicitParamDecl * getSelfDecl() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isObjCRetainableType() const
Definition Type.cpp:5435
const LangOptions & getLangOpts() const
ASTContext & getASTContext() override
BugReporter is a utility class for generating PathDiagnostics for analysis.
const SourceManager & getSourceManager()
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerFrontend *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
const LangOptions & getLangOpts() const
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
const SymbolicRegion * getSymbolicBase() const
If this is a symbolic region, returns the region.
LLVM_ATTRIBUTE_RETURNS_NONNULL const ObjCIvarDecl * getDecl() const override
const Expr * getArgExpr(unsigned Index) const override
Returns the expression associated with a given argument.
Definition CallEvent.h:1286
unsigned getNumArgs() const override
Returns the number of arguments (explicit and implicit).
Definition CallEvent.h:1284
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
Definition CallEvent.h:1276
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
Selector getSelector() const
Definition CallEvent.h:1298
const ObjCPropertyDecl * getAccessedProperty() const
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition SVals.cpp:103
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition SVals.h:88
bool isValid() const
Definition SVals.h:117
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition SVals.h:84
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getSuperRegion() const
Definition MemRegion.h:486
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
Definition SymExpr.h:124
SymbolRef getSymbol() const
It might return null.
Definition MemRegion.h:825
Defines the clang::TargetInfo interface.
const char *const CoreFoundationObjectiveC
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition Store.h:50
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
Definition SymExpr.h:133
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
Expr * Cond
};
const FunctionProtoType * T
U cast(CodeGen::Address addr)
Definition Address.h:327
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.
Definition TypeBase.h:6016