blob: 28fae7ca1b3a1d44a3434a794e0957382027e085 [file] [log] [blame]
Ted Kremenek0e7d2522008-07-03 04:29:21 +00001//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
Devin Coughlinad9f53e2016-02-25 21:15:16 +000010// This checker analyzes Objective-C -dealloc methods and their callees
11// to warn about improper releasing of instance variables that back synthesized
12// properties. It warns about missing releases in the following cases:
13// - When a class has a synthesized instance variable for a 'retain' or 'copy'
14// property and lacks a -dealloc method in its implementation.
15// - When a class has a synthesized instance variable for a 'retain'/'copy'
16// property but the ivar is not released in -dealloc by either -release
17// or by nilling out the property.
18//
19// It warns about extra releases in -dealloc (but not in callees) when a
20// synthesized instance variable is released in the following cases:
21// - When the property is 'assign' and is not 'readonly'.
22// - When the property is 'weak'.
23//
24// This checker only warns for instance variables synthesized to back
25// properties. Handling the more general case would require inferring whether
26// an instance variable is stored retained or not. For synthesized properties,
27// this is specified in the property declaration itself.
Ted Kremenek0e7d2522008-07-03 04:29:21 +000028//
29//===----------------------------------------------------------------------===//
30
Argyrios Kyrtzidisaf45aca2011-02-17 21:39:33 +000031#include "ClangSACheckers.h"
Benjamin Kramerea70eb32012-12-01 15:09:41 +000032#include "clang/AST/Attr.h"
Ted Kremenek0e7d2522008-07-03 04:29:21 +000033#include "clang/AST/DeclObjC.h"
Benjamin Kramerea70eb32012-12-01 15:09:41 +000034#include "clang/AST/Expr.h"
35#include "clang/AST/ExprObjC.h"
Ted Kremeneke66ca6f2008-07-03 14:35:01 +000036#include "clang/Basic/LangOptions.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000037#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
Devin Coughlinad9f53e2016-02-25 21:15:16 +000038#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000039#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
40#include "clang/StaticAnalyzer/Core/Checker.h"
41#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
Devin Coughlinad9f53e2016-02-25 21:15:16 +000042#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
43#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
44#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
45#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
46#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
Ted Kremenek3f049492008-10-29 04:30:28 +000047#include "llvm/Support/raw_ostream.h"
Ted Kremenek0e7d2522008-07-03 04:29:21 +000048
49using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000050using namespace ento;
Ted Kremenek0e7d2522008-07-03 04:29:21 +000051
Devin Coughlinad9f53e2016-02-25 21:15:16 +000052/// Indicates whether an instance variable is required to be released in
53/// -dealloc.
54enum class ReleaseRequirement {
55 /// The instance variable must be released, either by calling
56 /// -release on it directly or by nilling it out with a property setter.
57 MustRelease,
Devin Coughlin982c42d2016-02-11 22:13:20 +000058
Devin Coughlinad9f53e2016-02-25 21:15:16 +000059 /// The instance variable must not be directly released with -release.
60 MustNotReleaseDirectly,
Mike Stump11289f42009-09-09 15:08:12 +000061
Devin Coughlinad9f53e2016-02-25 21:15:16 +000062 /// The requirement for the instance variable could not be determined.
63 Unknown
64};
Ted Kremenek3f049492008-10-29 04:30:28 +000065
Devin Coughlinad9f53e2016-02-25 21:15:16 +000066/// Returns true if the property implementation is synthesized and the
67/// type of the property is retainable.
Devin Coughlin30751342016-01-27 01:41:58 +000068static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I,
69 const ObjCIvarDecl **ID,
70 const ObjCPropertyDecl **PD) {
71
72 if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
73 return false;
74
75 (*ID) = I->getPropertyIvarDecl();
76 if (!(*ID))
77 return false;
78
79 QualType T = (*ID)->getType();
80 if (!T->isObjCRetainableType())
81 return false;
82
83 (*PD) = I->getPropertyDecl();
84 // Shouldn't be able to synthesize a property that doesn't exist.
85 assert(*PD);
86
87 return true;
88}
89
Devin Coughlinad9f53e2016-02-25 21:15:16 +000090namespace {
91
92class ObjCDeallocChecker
93 : public Checker<check::ASTDecl<ObjCImplementationDecl>,
94 check::PreObjCMessage, check::PostObjCMessage,
95 check::BeginFunction, check::EndFunction,
96 check::PointerEscape,
97 check::PreStmt<ReturnStmt>> {
98
99 mutable IdentifierInfo *NSObjectII, *SenTestCaseII;
100 mutable Selector DeallocSel, ReleaseSel;
101
102 std::unique_ptr<BugType> MissingReleaseBugType;
103 std::unique_ptr<BugType> ExtraReleaseBugType;
104
105public:
106 ObjCDeallocChecker();
107
108 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
109 BugReporter &BR) const;
110 void checkBeginFunction(CheckerContext &Ctx) const;
111 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
112 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
113
114 ProgramStateRef checkPointerEscape(ProgramStateRef State,
115 const InvalidatedSymbols &Escaped,
116 const CallEvent *Call,
117 PointerEscapeKind Kind) const;
118 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
119 void checkEndFunction(CheckerContext &Ctx) const;
120
121private:
122 void diagnoseMissingReleases(CheckerContext &C) const;
123
124 bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
125 CheckerContext &C) const;
126
127 SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M,
128 CheckerContext &C) const;
129 SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
130 CheckerContext &C) const;
131
132 SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
133
134 ReleaseRequirement
135 getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
136
137 bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const;
138 bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx,
139 SVal &SelfValOut) const;
140 bool instanceDeallocIsOnStack(const CheckerContext &C,
141 SVal &InstanceValOut) const;
142
143 bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
144
145 const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const;
146
147 const ObjCPropertyDecl *
148 findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const;
149
150 ProgramStateRef removeValueRequiringRelease(ProgramStateRef State,
151 SymbolRef InstanceSym,
152 SymbolRef ValueSym) const;
153
154 void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
155
156 bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
157};
158} // End anonymous namespace.
159
160typedef llvm::ImmutableSet<SymbolRef> SymbolSet;
161
162/// Maps from the symbol for a class instance to the set of
163/// symbols remaining that must be released in -dealloc.
164REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet)
165
166namespace clang {
167namespace ento {
168template<> struct ProgramStateTrait<SymbolSet>
169: public ProgramStatePartialTrait<SymbolSet> {
170 static void *GDMIndex() { static int index = 0; return &index; }
171};
172}
Devin Coughlinea02bba2016-02-25 19:13:43 +0000173}
Devin Coughlin30751342016-01-27 01:41:58 +0000174
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000175/// An AST check that diagnose when the class requires a -dealloc method and
176/// is missing one.
177void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D,
178 AnalysisManager &Mgr,
179 BugReporter &BR) const {
180 assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly);
181 assert(!Mgr.getLangOpts().ObjCAutoRefCount);
182 initIdentifierInfoAndSelectors(Mgr.getASTContext());
Ted Kremenek0e7d2522008-07-03 04:29:21 +0000183
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000184 const ObjCInterfaceDecl *ID = D->getClassInterface();
Mike Stump11289f42009-09-09 15:08:12 +0000185
Devin Coughlin30751342016-01-27 01:41:58 +0000186 // Does the class contain any synthesized properties that are retainable?
Ted Kremenek37a2c0d2008-07-07 06:36:08 +0000187 // If not, skip the check entirely.
Devin Coughlin30751342016-01-27 01:41:58 +0000188 bool containsRetainedSynthesizedProperty = false;
189 for (const auto *I : D->property_impls()) {
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000190 if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) {
Devin Coughlin30751342016-01-27 01:41:58 +0000191 containsRetainedSynthesizedProperty = true;
192 break;
193 }
Ted Kremenek37a2c0d2008-07-07 06:36:08 +0000194 }
Mike Stump11289f42009-09-09 15:08:12 +0000195
Devin Coughlin30751342016-01-27 01:41:58 +0000196 if (!containsRetainedSynthesizedProperty)
Ted Kremenek37a2c0d2008-07-07 06:36:08 +0000197 return;
Mike Stump11289f42009-09-09 15:08:12 +0000198
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000199 // If the class is known to have a lifecycle with a separate teardown method
200 // then it may not require a -dealloc method.
201 if (classHasSeparateTeardown(ID))
Devin Coughlin88691c12016-02-25 18:55:24 +0000202 return;
203
Devin Coughlinea02bba2016-02-25 19:13:43 +0000204 const ObjCMethodDecl *MD = nullptr;
205
206 // Scan the instance methods for "dealloc".
207 for (const auto *I : D->instance_methods()) {
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000208 if (I->getSelector() == DeallocSel) {
Devin Coughlinea02bba2016-02-25 19:13:43 +0000209 MD = I;
210 break;
211 }
212 }
213
214 if (!MD) { // No dealloc found.
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000215 const char* Name = "Missing -dealloc";
Devin Coughlinea02bba2016-02-25 19:13:43 +0000216
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000217 std::string Buf;
218 llvm::raw_string_ostream OS(Buf);
219 OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method";
Devin Coughlinea02bba2016-02-25 19:13:43 +0000220
221 PathDiagnosticLocation DLoc =
222 PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
223
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000224 BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC,
225 OS.str(), DLoc);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000226 return;
227 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000228}
Devin Coughlinea02bba2016-02-25 19:13:43 +0000229
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000230/// If this is the beginning of -dealloc, mark the values initially stored in
231/// instance variables that must be released by the end of -dealloc
232/// as unreleased in the state.
233void ObjCDeallocChecker::checkBeginFunction(
234 CheckerContext &C) const {
235 initIdentifierInfoAndSelectors(C.getASTContext());
Devin Coughlinea02bba2016-02-25 19:13:43 +0000236
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000237 // Only do this if the current method is -dealloc.
238 SVal SelfVal;
239 if (!isInInstanceDealloc(C, SelfVal))
240 return;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000241
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000242 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
243
244 const LocationContext *LCtx = C.getLocationContext();
245 ProgramStateRef InitialState = C.getState();
246
247 ProgramStateRef State = InitialState;
248
249 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
250
251 // Symbols that must be released by the end of the -dealloc;
252 SymbolSet RequiredReleases = F.getEmptySet();
253
254 // If we're an inlined -dealloc, we should add our symbols to the existing
255 // set from our subclass.
256 if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
257 RequiredReleases = *CurrSet;
258
259 for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
260 ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl);
261 if (Requirement != ReleaseRequirement::MustRelease)
Devin Coughlinea02bba2016-02-25 19:13:43 +0000262 continue;
263
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000264 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
265 Optional<Loc> LValLoc = LVal.getAs<Loc>();
266 if (!LValLoc)
267 continue;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000268
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000269 SVal InitialVal = State->getSVal(LValLoc.getValue());
270 SymbolRef Symbol = InitialVal.getAsSymbol();
271 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
272 continue;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000273
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000274 // Mark the value as requiring a release.
275 RequiredReleases = F.add(RequiredReleases, Symbol);
276 }
Devin Coughlinea02bba2016-02-25 19:13:43 +0000277
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000278 if (!RequiredReleases.isEmpty()) {
279 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
280 }
Devin Coughlinea02bba2016-02-25 19:13:43 +0000281
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000282 if (State != InitialState) {
283 C.addTransition(State);
284 }
285}
Devin Coughlinea02bba2016-02-25 19:13:43 +0000286
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000287/// Given a symbol for an ivar, return a symbol for the instance containing
288/// the ivar. Returns nullptr if the instance symbol cannot be found.
289SymbolRef
290ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
291 if (auto *SRV = dyn_cast<SymbolRegionValue>(IvarSym)) {
292 const TypedValueRegion *TVR = SRV->getRegion();
293 const ObjCIvarRegion *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(TVR);
294 if (!IvarRegion)
295 return nullptr;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000296
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000297 return IvarRegion->getSymbolicBase()->getSymbol();
298 }
299
300 return nullptr;
301}
302
303/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
304/// a release or a nilling-out property setter.
305void ObjCDeallocChecker::checkPreObjCMessage(
306 const ObjCMethodCall &M, CheckerContext &C) const {
307 // Only run if -dealloc is on the stack.
308 SVal DeallocedInstance;
309 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
310 return;
311
312 SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C);
313
314 if (ReleasedValue) {
315 // An instance variable symbol was released with -release:
316 // [_property release];
317 if (diagnoseExtraRelease(ReleasedValue,M, C))
318 return;
319 } else {
320 // An instance variable symbol was released nilling out its property:
321 // self.property = nil;
322 ReleasedValue = getValueReleasedByNillingOut(M, C);
323 }
324
325 if (!ReleasedValue)
326 return;
327
328 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue);
329 if (!InstanceSym)
330 return;
331 ProgramStateRef InitialState = C.getState();
332
333 ProgramStateRef ReleasedState =
334 removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue);
335
336 if (ReleasedState != InitialState) {
337 C.addTransition(ReleasedState);
338 }
339}
340
341/// If the message was a call to '[super dealloc]', diagnose any missing
342/// releases.
343void ObjCDeallocChecker::checkPostObjCMessage(
344 const ObjCMethodCall &M, CheckerContext &C) const {
345 // We perform this check post-message so that if the super -dealloc
346 // calls a helper method and that this class overrides, any ivars released in
347 // the helper method will be recorded before checking.
348 if (isSuperDeallocMessage(M))
349 diagnoseMissingReleases(C);
350}
351
352/// Check for missing releases even when -dealloc does not call
353/// '[super dealloc]'.
354void ObjCDeallocChecker::checkEndFunction(
355 CheckerContext &C) const {
356 diagnoseMissingReleases(C);
357}
358
359/// Check for missing releases on early return.
360void ObjCDeallocChecker::checkPreStmt(
361 const ReturnStmt *RS, CheckerContext &C) const {
362 diagnoseMissingReleases(C);
363}
364
365/// If a symbol escapes conservatively assume unseen code released it.
366ProgramStateRef ObjCDeallocChecker::checkPointerEscape(
367 ProgramStateRef State, const InvalidatedSymbols &Escaped,
368 const CallEvent *Call, PointerEscapeKind Kind) const {
369
370 // Don't treat calls to '[super dealloc]' as escaping for the purposes
371 // of this checker. Because the checker diagnoses missing releases in the
372 // post-message handler for '[super dealloc], escaping here would cause
373 // the checker to never warn.
374 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
375 if (OMC && isSuperDeallocMessage(*OMC))
376 return State;
377
378 for (const auto &Sym : Escaped) {
379 State = State->remove<UnreleasedIvarMap>(Sym);
380
381 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
382 if (!InstanceSymbol)
383 continue;
384
385 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
386 }
387
388 return State;
389}
390
391/// Report any unreleased instance variables for the current instance being
392/// dealloced.
393void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
394 ProgramStateRef State = C.getState();
395
396 SVal SelfVal;
397 if (!isInInstanceDealloc(C, SelfVal))
398 return;
399
400 const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
401 const LocationContext *LCtx = C.getLocationContext();
402
403 ExplodedNode *ErrNode = nullptr;
404
405 SymbolRef SelfSym = SelfVal.getAsSymbol();
406 if (!SelfSym)
407 return;
408
409 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
410 if (!OldUnreleased)
411 return;
412
413 SymbolSet NewUnreleased = *OldUnreleased;
414 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
415
416 ProgramStateRef InitialState = State;
417
418 for (auto *IvarSymbol : *OldUnreleased) {
419 const TypedValueRegion *TVR =
420 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
421 const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
422
423 // Don't warn if the ivar is not for this instance.
424 if (SelfRegion != IvarRegion->getSuperRegion())
425 continue;
426
427 // Prevent an inlined call to -dealloc in a super class from warning
428 // about the values the subclass's -dealloc should release.
429 if (IvarRegion->getDecl()->getContainingInterface() !=
430 cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface())
431 continue;
432
433 // Prevents diagnosing multiple times for the same instance variable
434 // at, for example, both a return and at the end of of the function.
435 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
436
437 if (State->getStateManager()
438 .getConstraintManager()
439 .isNull(State, IvarSymbol)
440 .isConstrainedTrue()) {
441 continue;
442 }
443
444 // A missing release manifests as a leak, so treat as a non-fatal error.
445 if (!ErrNode)
446 ErrNode = C.generateNonFatalErrorNode();
447 // If we've already reached this node on another path, return without
448 // diagnosing.
449 if (!ErrNode)
450 return;
451
452 std::string Buf;
453 llvm::raw_string_ostream OS(Buf);
454
455 const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
456 const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface();
457 // If the class is known to have a lifecycle with teardown that is
458 // separate from -dealloc, do not warn about missing releases. We
459 // suppress here (rather than not tracking for instance variables in
460 // such classes) because these classes are rare.
461 if (classHasSeparateTeardown(Interface))
462 return;
463
464 ObjCImplDecl *ImplDecl = Interface->getImplementation();
465
466 const ObjCPropertyImplDecl *PropImpl =
467 ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
468
469 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
470
471 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy ||
472 PropDecl->getSetterKind() == ObjCPropertyDecl::Retain);
473
474 OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl
475 << "' was ";
476
477 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain)
478 OS << "retained";
479 else
480 OS << "copied";
481
482 OS << " by a synthesized property but not released"
483 " before '[super dealloc]'";
484
485 std::unique_ptr<BugReport> BR(
486 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
487
488 C.emitReport(std::move(BR));
489 }
490
491 if (NewUnreleased.isEmpty()) {
492 State = State->remove<UnreleasedIvarMap>(SelfSym);
493 } else {
494 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
495 }
496
497 if (ErrNode) {
498 C.addTransition(State, ErrNode);
499 } else if (State != InitialState) {
500 C.addTransition(State);
501 }
502
503 // Make sure that after checking in the top-most frame the list of
504 // tracked ivars is empty. This is intended to detect accidental leaks in
505 // the UnreleasedIvarMap program state.
506 assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
507}
508
509/// Emits a warning if the current context is -dealloc and \param ReleasedValue
510/// must not be directly released in a -dealloc. Returns true if a diagnostic
511/// was emitted.
512bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
513 const ObjCMethodCall &M,
514 CheckerContext &C) const {
515 SVal DeallocedInstance;
516 if (!isInInstanceDealloc(C, DeallocedInstance))
517 return false;
518
519 // Try to get the region from which the the released value was loaded.
520 // Note that, unlike diagnosing for missing releases, here we don't track
521 // values that must not be released in the state. This is because even if
522 // these values escape, it is still an error under the rules of MRR to
523 // release them in -dealloc.
524 const MemRegion *RegionLoadedFrom = nullptr;
525 if (auto *DerivedSym = dyn_cast<SymbolDerived>(ReleasedValue))
526 RegionLoadedFrom = DerivedSym->getRegion();
527 else if (auto *RegionSym = dyn_cast<SymbolRegionValue>(ReleasedValue))
528 RegionLoadedFrom = RegionSym->getRegion();
529 else
530 return false;
531
532 auto *ReleasedIvar = dyn_cast<ObjCIvarRegion>(RegionLoadedFrom);
533 if (!ReleasedIvar)
534 return false;
535
536 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
537 ReleasedIvar->getSuperRegion())
538 return false;
539
540 const LocationContext *LCtx = C.getLocationContext();
541 const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl();
542
543 // If the ivar belongs to a property that must not be released directly
544 // in dealloc, emit a warning.
545 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
546 const ObjCPropertyImplDecl *PropImpl =
547 Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier());
548 if (!PropImpl)
549 return false;
550
551 if (getDeallocReleaseRequirement(PropImpl) !=
552 ReleaseRequirement::MustNotReleaseDirectly) {
553 return false;
554 }
555
556 // If the property is readwrite but it shadows a read-only property in its
557 // external interface, treat the property a read-only. If the outside
558 // world cannot write to a property then the internal implementation is free
559 // to make its own convention about whether the value is stored retained
560 // or not. We look up the shadow here rather than in
561 // getDeallocReleaseRequirement() because doing so can be expensive.
562 const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl);
563 if (PropDecl) {
564 if (PropDecl->isReadOnly())
565 return false;
566 } else {
567 PropDecl = PropImpl->getPropertyDecl();
568 }
569
570 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
571 if (!ErrNode)
572 return false;
573
574 std::string Buf;
575 llvm::raw_string_ostream OS(Buf);
576
577 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
578 (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
579 !PropDecl->isReadOnly()));
580
581 OS << "The '" << *PropImpl->getPropertyIvarDecl()
582 << "' ivar in '" << *Container
583 << "' was synthesized for ";
584
585 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
586 OS << "a weak";
587 else
588 OS << "an assign, readwrite";
589
590 OS << " property but was released in 'dealloc'";
591
592 std::unique_ptr<BugReport> BR(
593 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
594 BR->addRange(M.getOriginExpr()->getSourceRange());
595
596 C.emitReport(std::move(BR));
597
598 return true;
599}
600
601
602ObjCDeallocChecker::
603 ObjCDeallocChecker()
604 : NSObjectII(nullptr), SenTestCaseII(nullptr) {
605
606 MissingReleaseBugType.reset(
607 new BugType(this, "Missing ivar release (leak)",
608 categories::MemoryCoreFoundationObjectiveC));
609
610 ExtraReleaseBugType.reset(
611 new BugType(this, "Extra ivar release",
612 categories::MemoryCoreFoundationObjectiveC));
613}
614
615void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
616 ASTContext &Ctx) const {
617 if (NSObjectII)
618 return;
619
620 NSObjectII = &Ctx.Idents.get("NSObject");
621 SenTestCaseII = &Ctx.Idents.get("SenTestCase");
622
623 IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
624 IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
625 DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII);
626 ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII);
627}
628
629/// Returns true if \param M is a call to '[super dealloc]'.
630bool ObjCDeallocChecker::isSuperDeallocMessage(
631 const ObjCMethodCall &M) const {
632 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
633 return false;
634
635 return M.getSelector() == DeallocSel;
636}
637
638/// Returns the ObjCImplDecl containing the method declaration in \param LCtx.
639const ObjCImplDecl *
640ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const {
641 auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl());
642 return cast<ObjCImplDecl>(MD->getDeclContext());
643}
644
645/// Returns the property that shadowed by \param PropImpl if one exists and
646/// nullptr otherwise.
647const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
648 const ObjCPropertyImplDecl *PropImpl) const {
649 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
650
651 // Only readwrite properties can shadow.
652 if (PropDecl->isReadOnly())
653 return nullptr;
654
655 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext());
656
657 // Only class extensions can contain shadowing properties.
658 if (!CatDecl || !CatDecl->IsClassExtension())
659 return nullptr;
660
661 IdentifierInfo *ID = PropDecl->getIdentifier();
662 DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
663 for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
664 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I);
665 if (!ShadowedPropDecl)
666 continue;
667
668 if (ShadowedPropDecl->isInstanceProperty()) {
669 assert(ShadowedPropDecl->isReadOnly());
670 return ShadowedPropDecl;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000671 }
672 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000673
674 return nullptr;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000675}
676
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000677/// Remove the \param Value requiring a release from the tracked set for
678/// \param Instance and return the resultant state.
679ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease(
680 ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const {
681 assert(Instance);
682 assert(Value);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000683
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000684 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
685 if (!Unreleased)
686 return State;
687
688 // Mark the value as no longer requiring a release.
689 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
690 SymbolSet NewUnreleased = F.remove(*Unreleased, Value);
691
692 if (NewUnreleased.isEmpty()) {
693 return State->remove<UnreleasedIvarMap>(Instance);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000694 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000695
696 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000697}
698
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000699/// Determines whether the instance variable for \p PropImpl must or must not be
700/// released in -dealloc or whether it cannot be determined.
701ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
702 const ObjCPropertyImplDecl *PropImpl) const {
703 const ObjCIvarDecl *IvarDecl;
704 const ObjCPropertyDecl *PropDecl;
705 if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl))
706 return ReleaseRequirement::Unknown;
707
708 ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind();
709
710 switch (SK) {
711 // Retain and copy setters retain/copy their values before storing and so
712 // the value in their instance variables must be released in -dealloc.
713 case ObjCPropertyDecl::Retain:
714 case ObjCPropertyDecl::Copy:
715 return ReleaseRequirement::MustRelease;
716
717 case ObjCPropertyDecl::Weak:
718 return ReleaseRequirement::MustNotReleaseDirectly;
719
720 case ObjCPropertyDecl::Assign:
721 // It is common for the ivars for read-only assign properties to
722 // always be stored retained, so their release requirement cannot be
723 // be determined.
724 if (PropDecl->isReadOnly())
725 return ReleaseRequirement::Unknown;
726
727 return ReleaseRequirement::MustNotReleaseDirectly;
728 }
729 llvm_unreachable("Unrecognized setter kind");
730}
731
732/// Returns the released value if \param M is a call to -release. Returns
733/// nullptr otherwise.
734SymbolRef
735ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M,
736 CheckerContext &C) const {
737 if (M.getSelector() != ReleaseSel)
738 return nullptr;
739
740 return M.getReceiverSVal().getAsSymbol();
741}
742
743/// Returns the released value if \param M is a call a setter that releases
744/// and nils out its underlying instance variable.
745SymbolRef
746ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
747 CheckerContext &C) const {
748 SVal ReceiverVal = M.getReceiverSVal();
749 if (!ReceiverVal.isValid())
750 return nullptr;
751
752 // Is the first argument nil?
753 if (M.getNumArgs() == 0)
754 return nullptr;
755 SVal Arg = M.getArgSVal(0);
756 ProgramStateRef notNilState, nilState;
757 std::tie(notNilState, nilState) =
758 M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
759 if (!(nilState && !notNilState))
760 return nullptr;
761
762 const ObjCPropertyDecl *Prop = M.getAccessedProperty();
763 if (!Prop)
764 return nullptr;
765
766 ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
767 if (!PropIvarDecl)
768 return nullptr;
769
770 ProgramStateRef State = C.getState();
771
772 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
773 Optional<Loc> LValLoc = LVal.getAs<Loc>();
774 if (!LValLoc)
775 return nullptr;
776
777 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
778 return CurrentValInIvar.getAsSymbol();
779}
780
781/// Returns true if the current context is a call to -dealloc and false
782/// otherwise. If true, it also sets \param SelfValOut to the value of
783/// 'self'.
784bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
785 SVal &SelfValOut) const {
786 return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
787}
788
789/// Returns true if \param LCtx is a call to -dealloc and false
790/// otherwise. If true, it also sets \param SelfValOut to the value of
791/// 'self'.
792bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
793 const LocationContext *LCtx,
794 SVal &SelfValOut) const {
795 auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl());
796 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
797 return false;
798
799 const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
800 assert(SelfDecl && "No self in -dealloc?");
801
802 ProgramStateRef State = C.getState();
803 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
804 return true;
805}
806
807/// Returns true if there is a call to -dealloc anywhere on the stack and false
808/// otherwise. If true, it also sets \param InstanceValOut to the value of
809/// 'self' in the frame for -dealloc.
810bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
811 SVal &InstanceValOut) const {
812 const LocationContext *LCtx = C.getLocationContext();
813
814 while (LCtx) {
815 if (isInInstanceDealloc(C, LCtx, InstanceValOut))
816 return true;
817
818 LCtx = LCtx->getParent();
819 }
820
821 return false;
822}
823
824/// Returns true if the \param ID is a class in which which is known to have
825/// a separate teardown lifecycle. In this case, -dealloc warnings
826/// about missing releases should be suppressed.
827bool ObjCDeallocChecker::classHasSeparateTeardown(
828 const ObjCInterfaceDecl *ID) const {
829 // Suppress if the class is not a subclass of NSObject.
830 for ( ; ID ; ID = ID->getSuperClass()) {
831 IdentifierInfo *II = ID->getIdentifier();
832
833 if (II == NSObjectII)
834 return false;
835
836 // FIXME: For now, ignore classes that subclass SenTestCase, as these don't
837 // need to implement -dealloc. They implement tear down in another way,
838 // which we should try and catch later.
839 // http://llvm.org/bugs/show_bug.cgi?id=3187
840 if (II == SenTestCaseII)
841 return true;
842 }
843
844 return true;
845}
846
847void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
848 const LangOptions &LangOpts = Mgr.getLangOpts();
849 // These checker only makes sense under MRR.
850 if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
851 return;
852
853 Mgr.registerChecker<ObjCDeallocChecker>();
Argyrios Kyrtzidisaf45aca2011-02-17 21:39:33 +0000854}