blob: f72a1aeb8a97edb252f7f7a86cb5dc63e1768bf3 [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,
Devin Coughlin3fc67e42016-02-29 21:44:08 +000096 eval::Assume,
Devin Coughlinad9f53e2016-02-25 21:15:16 +000097 check::PointerEscape,
98 check::PreStmt<ReturnStmt>> {
99
100 mutable IdentifierInfo *NSObjectII, *SenTestCaseII;
101 mutable Selector DeallocSel, ReleaseSel;
102
103 std::unique_ptr<BugType> MissingReleaseBugType;
104 std::unique_ptr<BugType> ExtraReleaseBugType;
105
106public:
107 ObjCDeallocChecker();
108
109 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
110 BugReporter &BR) const;
111 void checkBeginFunction(CheckerContext &Ctx) const;
112 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
113 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
114
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000115 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
116 bool Assumption) const;
117
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000118 ProgramStateRef checkPointerEscape(ProgramStateRef State,
119 const InvalidatedSymbols &Escaped,
120 const CallEvent *Call,
121 PointerEscapeKind Kind) const;
122 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
123 void checkEndFunction(CheckerContext &Ctx) const;
124
125private:
126 void diagnoseMissingReleases(CheckerContext &C) const;
127
128 bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
129 CheckerContext &C) const;
130
131 SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M,
132 CheckerContext &C) const;
133 SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
134 CheckerContext &C) const;
135
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000136 const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const;
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000137 SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
138
139 ReleaseRequirement
140 getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
141
142 bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const;
143 bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx,
144 SVal &SelfValOut) const;
145 bool instanceDeallocIsOnStack(const CheckerContext &C,
146 SVal &InstanceValOut) const;
147
148 bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
149
150 const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const;
151
152 const ObjCPropertyDecl *
153 findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const;
154
155 ProgramStateRef removeValueRequiringRelease(ProgramStateRef State,
156 SymbolRef InstanceSym,
157 SymbolRef ValueSym) const;
158
159 void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
160
161 bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
162};
163} // End anonymous namespace.
164
165typedef llvm::ImmutableSet<SymbolRef> SymbolSet;
166
167/// Maps from the symbol for a class instance to the set of
168/// symbols remaining that must be released in -dealloc.
169REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet)
170
171namespace clang {
172namespace ento {
173template<> struct ProgramStateTrait<SymbolSet>
174: public ProgramStatePartialTrait<SymbolSet> {
175 static void *GDMIndex() { static int index = 0; return &index; }
176};
177}
Devin Coughlinea02bba2016-02-25 19:13:43 +0000178}
Devin Coughlin30751342016-01-27 01:41:58 +0000179
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000180/// An AST check that diagnose when the class requires a -dealloc method and
181/// is missing one.
182void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D,
183 AnalysisManager &Mgr,
184 BugReporter &BR) const {
185 assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly);
186 assert(!Mgr.getLangOpts().ObjCAutoRefCount);
187 initIdentifierInfoAndSelectors(Mgr.getASTContext());
Ted Kremenek0e7d2522008-07-03 04:29:21 +0000188
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000189 const ObjCInterfaceDecl *ID = D->getClassInterface();
Mike Stump11289f42009-09-09 15:08:12 +0000190
Devin Coughlin30751342016-01-27 01:41:58 +0000191 // Does the class contain any synthesized properties that are retainable?
Ted Kremenek37a2c0d2008-07-07 06:36:08 +0000192 // If not, skip the check entirely.
Devin Coughlin30751342016-01-27 01:41:58 +0000193 bool containsRetainedSynthesizedProperty = false;
194 for (const auto *I : D->property_impls()) {
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000195 if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) {
Devin Coughlin30751342016-01-27 01:41:58 +0000196 containsRetainedSynthesizedProperty = true;
197 break;
198 }
Ted Kremenek37a2c0d2008-07-07 06:36:08 +0000199 }
Mike Stump11289f42009-09-09 15:08:12 +0000200
Devin Coughlin30751342016-01-27 01:41:58 +0000201 if (!containsRetainedSynthesizedProperty)
Ted Kremenek37a2c0d2008-07-07 06:36:08 +0000202 return;
Mike Stump11289f42009-09-09 15:08:12 +0000203
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000204 // 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))
Devin Coughlin88691c12016-02-25 18:55:24 +0000207 return;
208
Devin Coughlinea02bba2016-02-25 19:13:43 +0000209 const ObjCMethodDecl *MD = nullptr;
210
211 // Scan the instance methods for "dealloc".
212 for (const auto *I : D->instance_methods()) {
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000213 if (I->getSelector() == DeallocSel) {
Devin Coughlinea02bba2016-02-25 19:13:43 +0000214 MD = I;
215 break;
216 }
217 }
218
219 if (!MD) { // No dealloc found.
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000220 const char* Name = "Missing -dealloc";
Devin Coughlinea02bba2016-02-25 19:13:43 +0000221
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000222 std::string Buf;
223 llvm::raw_string_ostream OS(Buf);
224 OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method";
Devin Coughlinea02bba2016-02-25 19:13:43 +0000225
226 PathDiagnosticLocation DLoc =
227 PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
228
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000229 BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC,
230 OS.str(), DLoc);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000231 return;
232 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000233}
Devin Coughlinea02bba2016-02-25 19:13:43 +0000234
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000235/// If this is the beginning of -dealloc, mark the values initially stored in
236/// instance variables that must be released by the end of -dealloc
237/// as unreleased in the state.
238void ObjCDeallocChecker::checkBeginFunction(
239 CheckerContext &C) const {
240 initIdentifierInfoAndSelectors(C.getASTContext());
Devin Coughlinea02bba2016-02-25 19:13:43 +0000241
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000242 // Only do this if the current method is -dealloc.
243 SVal SelfVal;
244 if (!isInInstanceDealloc(C, SelfVal))
245 return;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000246
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000247 SymbolRef SelfSymbol = SelfVal.getAsSymbol();
248
249 const LocationContext *LCtx = C.getLocationContext();
250 ProgramStateRef InitialState = C.getState();
251
252 ProgramStateRef State = InitialState;
253
254 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
255
256 // Symbols that must be released by the end of the -dealloc;
257 SymbolSet RequiredReleases = F.getEmptySet();
258
259 // If we're an inlined -dealloc, we should add our symbols to the existing
260 // set from our subclass.
261 if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
262 RequiredReleases = *CurrSet;
263
264 for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
265 ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl);
266 if (Requirement != ReleaseRequirement::MustRelease)
Devin Coughlinea02bba2016-02-25 19:13:43 +0000267 continue;
268
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000269 SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
270 Optional<Loc> LValLoc = LVal.getAs<Loc>();
271 if (!LValLoc)
272 continue;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000273
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000274 SVal InitialVal = State->getSVal(LValLoc.getValue());
275 SymbolRef Symbol = InitialVal.getAsSymbol();
276 if (!Symbol || !isa<SymbolRegionValue>(Symbol))
277 continue;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000278
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000279 // Mark the value as requiring a release.
280 RequiredReleases = F.add(RequiredReleases, Symbol);
281 }
Devin Coughlinea02bba2016-02-25 19:13:43 +0000282
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000283 if (!RequiredReleases.isEmpty()) {
284 State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
285 }
Devin Coughlinea02bba2016-02-25 19:13:43 +0000286
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000287 if (State != InitialState) {
288 C.addTransition(State);
289 }
290}
Devin Coughlinea02bba2016-02-25 19:13:43 +0000291
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000292/// Given a symbol for an ivar, return the ivar region it was loaded from.
293/// Returns nullptr if the instance symbol cannot be found.
294const ObjCIvarRegion *
295ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const {
296 const MemRegion *RegionLoadedFrom = nullptr;
297 if (auto *DerivedSym = dyn_cast<SymbolDerived>(IvarSym))
298 RegionLoadedFrom = DerivedSym->getRegion();
299 else if (auto *RegionSym = dyn_cast<SymbolRegionValue>(IvarSym))
300 RegionLoadedFrom = RegionSym->getRegion();
301 else
302 return nullptr;
303
304 return dyn_cast<ObjCIvarRegion>(RegionLoadedFrom);
305}
306
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000307/// Given a symbol for an ivar, return a symbol for the instance containing
308/// the ivar. Returns nullptr if the instance symbol cannot be found.
309SymbolRef
310ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
Devin Coughlinea02bba2016-02-25 19:13:43 +0000311
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000312 const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
313 if (!IvarRegion)
314 return nullptr;
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000315
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000316 return IvarRegion->getSymbolicBase()->getSymbol();
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000317}
318
319/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
320/// a release or a nilling-out property setter.
321void ObjCDeallocChecker::checkPreObjCMessage(
322 const ObjCMethodCall &M, CheckerContext &C) const {
323 // Only run if -dealloc is on the stack.
324 SVal DeallocedInstance;
325 if (!instanceDeallocIsOnStack(C, DeallocedInstance))
326 return;
327
328 SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C);
329
330 if (ReleasedValue) {
331 // An instance variable symbol was released with -release:
332 // [_property release];
333 if (diagnoseExtraRelease(ReleasedValue,M, C))
334 return;
335 } else {
336 // An instance variable symbol was released nilling out its property:
337 // self.property = nil;
338 ReleasedValue = getValueReleasedByNillingOut(M, C);
339 }
340
341 if (!ReleasedValue)
342 return;
343
344 SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue);
345 if (!InstanceSym)
346 return;
347 ProgramStateRef InitialState = C.getState();
348
349 ProgramStateRef ReleasedState =
350 removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue);
351
352 if (ReleasedState != InitialState) {
353 C.addTransition(ReleasedState);
354 }
355}
356
357/// If the message was a call to '[super dealloc]', diagnose any missing
358/// releases.
359void ObjCDeallocChecker::checkPostObjCMessage(
360 const ObjCMethodCall &M, CheckerContext &C) const {
361 // We perform this check post-message so that if the super -dealloc
362 // calls a helper method and that this class overrides, any ivars released in
363 // the helper method will be recorded before checking.
364 if (isSuperDeallocMessage(M))
365 diagnoseMissingReleases(C);
366}
367
368/// Check for missing releases even when -dealloc does not call
369/// '[super dealloc]'.
370void ObjCDeallocChecker::checkEndFunction(
371 CheckerContext &C) const {
372 diagnoseMissingReleases(C);
373}
374
375/// Check for missing releases on early return.
376void ObjCDeallocChecker::checkPreStmt(
377 const ReturnStmt *RS, CheckerContext &C) const {
378 diagnoseMissingReleases(C);
379}
380
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000381/// When a symbol is assumed to be nil, remove it from the set of symbols
382/// require to be nil.
383ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond,
384 bool Assumption) const {
385 if (State->get<UnreleasedIvarMap>().isEmpty())
386 return State;
387
388 auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
389 if (!CondBSE)
390 return State;
391
392 BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
393 if (Assumption) {
394 if (OpCode != BO_EQ)
395 return State;
396 } else {
397 if (OpCode != BO_NE)
398 return State;
399 }
400
401 SymbolRef NullSymbol = nullptr;
402 if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
403 const llvm::APInt &RHS = SIE->getRHS();
404 if (RHS != 0)
405 return State;
406 NullSymbol = SIE->getLHS();
407 } else if (auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
408 const llvm::APInt &LHS = SIE->getLHS();
409 if (LHS != 0)
410 return State;
411 NullSymbol = SIE->getRHS();
412 } else {
413 return State;
414 }
415
416 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
417 if (!InstanceSymbol)
418 return State;
419
420 State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
421
422 return State;
423}
424
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000425/// If a symbol escapes conservatively assume unseen code released it.
426ProgramStateRef ObjCDeallocChecker::checkPointerEscape(
427 ProgramStateRef State, const InvalidatedSymbols &Escaped,
428 const CallEvent *Call, PointerEscapeKind Kind) const {
429
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000430 if (State->get<UnreleasedIvarMap>().isEmpty())
431 return State;
432
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000433 // Don't treat calls to '[super dealloc]' as escaping for the purposes
434 // of this checker. Because the checker diagnoses missing releases in the
435 // post-message handler for '[super dealloc], escaping here would cause
436 // the checker to never warn.
437 auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
438 if (OMC && isSuperDeallocMessage(*OMC))
439 return State;
440
441 for (const auto &Sym : Escaped) {
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000442 if (!Call || (Call && !Call->isInSystemHeader())) {
443 // If Sym is a symbol for an object with instance variables that
444 // must be released, remove these obligations when the object escapes
445 // unless via a call to a system function. System functions are
446 // very unlikely to release instance variables on objects passed to them,
447 // and are frequently called on 'self' in -dealloc (e.g., to remove
448 // observers) -- we want to avoid false negatives from escaping on
449 // them.
450 State = State->remove<UnreleasedIvarMap>(Sym);
451 }
452
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000453
454 SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
455 if (!InstanceSymbol)
456 continue;
457
458 State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
459 }
460
461 return State;
462}
463
464/// Report any unreleased instance variables for the current instance being
465/// dealloced.
466void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
467 ProgramStateRef State = C.getState();
468
469 SVal SelfVal;
470 if (!isInInstanceDealloc(C, SelfVal))
471 return;
472
473 const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
474 const LocationContext *LCtx = C.getLocationContext();
475
476 ExplodedNode *ErrNode = nullptr;
477
478 SymbolRef SelfSym = SelfVal.getAsSymbol();
479 if (!SelfSym)
480 return;
481
482 const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
483 if (!OldUnreleased)
484 return;
485
486 SymbolSet NewUnreleased = *OldUnreleased;
487 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
488
489 ProgramStateRef InitialState = State;
490
491 for (auto *IvarSymbol : *OldUnreleased) {
492 const TypedValueRegion *TVR =
493 cast<SymbolRegionValue>(IvarSymbol)->getRegion();
494 const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
495
496 // Don't warn if the ivar is not for this instance.
497 if (SelfRegion != IvarRegion->getSuperRegion())
498 continue;
499
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000500 const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000501 // Prevent an inlined call to -dealloc in a super class from warning
502 // about the values the subclass's -dealloc should release.
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000503 if (IvarDecl->getContainingInterface() !=
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000504 cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface())
505 continue;
506
507 // Prevents diagnosing multiple times for the same instance variable
508 // at, for example, both a return and at the end of of the function.
509 NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
510
511 if (State->getStateManager()
512 .getConstraintManager()
513 .isNull(State, IvarSymbol)
514 .isConstrainedTrue()) {
515 continue;
516 }
517
518 // A missing release manifests as a leak, so treat as a non-fatal error.
519 if (!ErrNode)
520 ErrNode = C.generateNonFatalErrorNode();
521 // If we've already reached this node on another path, return without
522 // diagnosing.
523 if (!ErrNode)
524 return;
525
526 std::string Buf;
527 llvm::raw_string_ostream OS(Buf);
528
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000529 const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface();
530 // If the class is known to have a lifecycle with teardown that is
531 // separate from -dealloc, do not warn about missing releases. We
532 // suppress here (rather than not tracking for instance variables in
533 // such classes) because these classes are rare.
534 if (classHasSeparateTeardown(Interface))
535 return;
536
537 ObjCImplDecl *ImplDecl = Interface->getImplementation();
538
539 const ObjCPropertyImplDecl *PropImpl =
540 ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
541
542 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
543
544 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy ||
545 PropDecl->getSetterKind() == ObjCPropertyDecl::Retain);
546
547 OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl
548 << "' was ";
549
550 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain)
551 OS << "retained";
552 else
553 OS << "copied";
554
555 OS << " by a synthesized property but not released"
556 " before '[super dealloc]'";
557
558 std::unique_ptr<BugReport> BR(
559 new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
560
561 C.emitReport(std::move(BR));
562 }
563
564 if (NewUnreleased.isEmpty()) {
565 State = State->remove<UnreleasedIvarMap>(SelfSym);
566 } else {
567 State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
568 }
569
570 if (ErrNode) {
571 C.addTransition(State, ErrNode);
572 } else if (State != InitialState) {
573 C.addTransition(State);
574 }
575
576 // Make sure that after checking in the top-most frame the list of
577 // tracked ivars is empty. This is intended to detect accidental leaks in
578 // the UnreleasedIvarMap program state.
579 assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
580}
581
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000582/// Emits a warning if the current context is -dealloc and ReleasedValue
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000583/// must not be directly released in a -dealloc. Returns true if a diagnostic
584/// was emitted.
585bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
586 const ObjCMethodCall &M,
587 CheckerContext &C) const {
588 SVal DeallocedInstance;
589 if (!isInInstanceDealloc(C, DeallocedInstance))
590 return false;
591
592 // Try to get the region from which the the released value was loaded.
593 // Note that, unlike diagnosing for missing releases, here we don't track
594 // values that must not be released in the state. This is because even if
595 // these values escape, it is still an error under the rules of MRR to
596 // release them in -dealloc.
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000597 auto *ReleasedIvar = getIvarRegionForIvarSymbol(ReleasedValue);
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000598 if (!ReleasedIvar)
599 return false;
600
601 if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
602 ReleasedIvar->getSuperRegion())
603 return false;
604
605 const LocationContext *LCtx = C.getLocationContext();
606 const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl();
607
608 // If the ivar belongs to a property that must not be released directly
609 // in dealloc, emit a warning.
610 const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
611 const ObjCPropertyImplDecl *PropImpl =
612 Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier());
613 if (!PropImpl)
614 return false;
615
616 if (getDeallocReleaseRequirement(PropImpl) !=
617 ReleaseRequirement::MustNotReleaseDirectly) {
618 return false;
619 }
620
621 // If the property is readwrite but it shadows a read-only property in its
622 // external interface, treat the property a read-only. If the outside
623 // world cannot write to a property then the internal implementation is free
624 // to make its own convention about whether the value is stored retained
625 // or not. We look up the shadow here rather than in
626 // getDeallocReleaseRequirement() because doing so can be expensive.
627 const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl);
628 if (PropDecl) {
629 if (PropDecl->isReadOnly())
630 return false;
631 } else {
632 PropDecl = PropImpl->getPropertyDecl();
633 }
634
635 ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
636 if (!ErrNode)
637 return false;
638
639 std::string Buf;
640 llvm::raw_string_ostream OS(Buf);
641
642 assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
643 (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
644 !PropDecl->isReadOnly()));
645
646 OS << "The '" << *PropImpl->getPropertyIvarDecl()
647 << "' ivar in '" << *Container
648 << "' was synthesized for ";
649
650 if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
651 OS << "a weak";
652 else
653 OS << "an assign, readwrite";
654
655 OS << " property but was released in 'dealloc'";
656
657 std::unique_ptr<BugReport> BR(
658 new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
659 BR->addRange(M.getOriginExpr()->getSourceRange());
660
661 C.emitReport(std::move(BR));
662
663 return true;
664}
665
666
667ObjCDeallocChecker::
668 ObjCDeallocChecker()
669 : NSObjectII(nullptr), SenTestCaseII(nullptr) {
670
671 MissingReleaseBugType.reset(
672 new BugType(this, "Missing ivar release (leak)",
673 categories::MemoryCoreFoundationObjectiveC));
674
675 ExtraReleaseBugType.reset(
676 new BugType(this, "Extra ivar release",
677 categories::MemoryCoreFoundationObjectiveC));
678}
679
680void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
681 ASTContext &Ctx) const {
682 if (NSObjectII)
683 return;
684
685 NSObjectII = &Ctx.Idents.get("NSObject");
686 SenTestCaseII = &Ctx.Idents.get("SenTestCase");
687
688 IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
689 IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
690 DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII);
691 ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII);
692}
693
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000694/// Returns true if M is a call to '[super dealloc]'.
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000695bool ObjCDeallocChecker::isSuperDeallocMessage(
696 const ObjCMethodCall &M) const {
697 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
698 return false;
699
700 return M.getSelector() == DeallocSel;
701}
702
NAKAMURA Takumia8aa5f02016-02-26 03:15:13 +0000703/// Returns the ObjCImplDecl containing the method declaration in LCtx.
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000704const ObjCImplDecl *
705ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const {
706 auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl());
707 return cast<ObjCImplDecl>(MD->getDeclContext());
708}
709
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000710/// Returns the property that shadowed by PropImpl if one exists and
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000711/// nullptr otherwise.
712const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
713 const ObjCPropertyImplDecl *PropImpl) const {
714 const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
715
716 // Only readwrite properties can shadow.
717 if (PropDecl->isReadOnly())
718 return nullptr;
719
720 auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext());
721
722 // Only class extensions can contain shadowing properties.
723 if (!CatDecl || !CatDecl->IsClassExtension())
724 return nullptr;
725
726 IdentifierInfo *ID = PropDecl->getIdentifier();
727 DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
728 for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
729 auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I);
730 if (!ShadowedPropDecl)
731 continue;
732
733 if (ShadowedPropDecl->isInstanceProperty()) {
734 assert(ShadowedPropDecl->isReadOnly());
735 return ShadowedPropDecl;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000736 }
737 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000738
739 return nullptr;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000740}
741
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000742/// Remove the Value requiring a release from the tracked set for
743/// Instance and return the resultant state.
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000744ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease(
745 ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const {
746 assert(Instance);
747 assert(Value);
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000748 const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
749 if (!RemovedRegion)
750 return State;
Devin Coughlinea02bba2016-02-25 19:13:43 +0000751
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000752 const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
753 if (!Unreleased)
754 return State;
755
756 // Mark the value as no longer requiring a release.
757 SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
Devin Coughlin3fc67e42016-02-29 21:44:08 +0000758 SymbolSet NewUnreleased = *Unreleased;
759 for (auto &Sym : *Unreleased) {
760 const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
761 assert(UnreleasedRegion);
762 if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
763 NewUnreleased = F.remove(NewUnreleased, Sym);
764 }
765 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000766
767 if (NewUnreleased.isEmpty()) {
768 return State->remove<UnreleasedIvarMap>(Instance);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000769 }
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000770
771 return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
Devin Coughlinea02bba2016-02-25 19:13:43 +0000772}
773
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000774/// Determines whether the instance variable for \p PropImpl must or must not be
775/// released in -dealloc or whether it cannot be determined.
776ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
777 const ObjCPropertyImplDecl *PropImpl) const {
778 const ObjCIvarDecl *IvarDecl;
779 const ObjCPropertyDecl *PropDecl;
780 if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl))
781 return ReleaseRequirement::Unknown;
782
783 ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind();
784
785 switch (SK) {
786 // Retain and copy setters retain/copy their values before storing and so
787 // the value in their instance variables must be released in -dealloc.
788 case ObjCPropertyDecl::Retain:
789 case ObjCPropertyDecl::Copy:
790 return ReleaseRequirement::MustRelease;
791
792 case ObjCPropertyDecl::Weak:
793 return ReleaseRequirement::MustNotReleaseDirectly;
794
795 case ObjCPropertyDecl::Assign:
796 // It is common for the ivars for read-only assign properties to
797 // always be stored retained, so their release requirement cannot be
798 // be determined.
799 if (PropDecl->isReadOnly())
800 return ReleaseRequirement::Unknown;
801
802 return ReleaseRequirement::MustNotReleaseDirectly;
803 }
804 llvm_unreachable("Unrecognized setter kind");
805}
806
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000807/// Returns the released value if M is a call to -release. Returns
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000808/// nullptr otherwise.
809SymbolRef
810ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M,
811 CheckerContext &C) const {
812 if (M.getSelector() != ReleaseSel)
813 return nullptr;
814
815 return M.getReceiverSVal().getAsSymbol();
816}
817
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000818/// Returns the released value if M is a call a setter that releases
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000819/// and nils out its underlying instance variable.
820SymbolRef
821ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
822 CheckerContext &C) const {
823 SVal ReceiverVal = M.getReceiverSVal();
824 if (!ReceiverVal.isValid())
825 return nullptr;
826
827 // Is the first argument nil?
828 if (M.getNumArgs() == 0)
829 return nullptr;
830 SVal Arg = M.getArgSVal(0);
831 ProgramStateRef notNilState, nilState;
832 std::tie(notNilState, nilState) =
833 M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
834 if (!(nilState && !notNilState))
835 return nullptr;
836
837 const ObjCPropertyDecl *Prop = M.getAccessedProperty();
838 if (!Prop)
839 return nullptr;
840
841 ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
842 if (!PropIvarDecl)
843 return nullptr;
844
845 ProgramStateRef State = C.getState();
846
847 SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
848 Optional<Loc> LValLoc = LVal.getAs<Loc>();
849 if (!LValLoc)
850 return nullptr;
851
852 SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
853 return CurrentValInIvar.getAsSymbol();
854}
855
856/// Returns true if the current context is a call to -dealloc and false
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000857/// otherwise. If true, it also sets SelfValOut to the value of
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000858/// 'self'.
859bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
860 SVal &SelfValOut) const {
861 return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
862}
863
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000864/// Returns true if LCtx is a call to -dealloc and false
865/// otherwise. If true, it also sets SelfValOut to the value of
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000866/// 'self'.
867bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
868 const LocationContext *LCtx,
869 SVal &SelfValOut) const {
870 auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl());
871 if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
872 return false;
873
874 const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
875 assert(SelfDecl && "No self in -dealloc?");
876
877 ProgramStateRef State = C.getState();
878 SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
879 return true;
880}
881
882/// Returns true if there is a call to -dealloc anywhere on the stack and false
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000883/// otherwise. If true, it also sets InstanceValOut to the value of
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000884/// 'self' in the frame for -dealloc.
885bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
886 SVal &InstanceValOut) const {
887 const LocationContext *LCtx = C.getLocationContext();
888
889 while (LCtx) {
890 if (isInInstanceDealloc(C, LCtx, InstanceValOut))
891 return true;
892
893 LCtx = LCtx->getParent();
894 }
895
896 return false;
897}
898
Devin Coughlinec6f61c2016-02-26 03:41:31 +0000899/// Returns true if the ID is a class in which which is known to have
Devin Coughlinad9f53e2016-02-25 21:15:16 +0000900/// a separate teardown lifecycle. In this case, -dealloc warnings
901/// about missing releases should be suppressed.
902bool ObjCDeallocChecker::classHasSeparateTeardown(
903 const ObjCInterfaceDecl *ID) const {
904 // Suppress if the class is not a subclass of NSObject.
905 for ( ; ID ; ID = ID->getSuperClass()) {
906 IdentifierInfo *II = ID->getIdentifier();
907
908 if (II == NSObjectII)
909 return false;
910
911 // FIXME: For now, ignore classes that subclass SenTestCase, as these don't
912 // need to implement -dealloc. They implement tear down in another way,
913 // which we should try and catch later.
914 // http://llvm.org/bugs/show_bug.cgi?id=3187
915 if (II == SenTestCaseII)
916 return true;
917 }
918
919 return true;
920}
921
922void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
923 const LangOptions &LangOpts = Mgr.getLangOpts();
924 // These checker only makes sense under MRR.
925 if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
926 return;
927
928 Mgr.registerChecker<ObjCDeallocChecker>();
Argyrios Kyrtzidisaf45aca2011-02-17 21:39:33 +0000929}