1//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- 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 file defines the methods for RetainCountChecker, which implements
10// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
11//
12//===----------------------------------------------------------------------===//
13
14#include "RetainCountChecker.h"
15#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
16#include <optional>
17
18using namespace clang;
19using namespace ento;
20using namespace retaincountchecker;
21
22REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal)
23
24namespace clang {
25namespace ento {
26namespace retaincountchecker {
27
28const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) {
29 return State->get<RefBindings>(key: Sym);
30}
31
32} // end namespace retaincountchecker
33} // end namespace ento
34} // end namespace clang
35
36static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
37 RefVal Val) {
38 assert(Sym != nullptr);
39 return State->set<RefBindings>(K: Sym, E: Val);
40}
41
42static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
43 return State->remove<RefBindings>(K: Sym);
44}
45
46void RefVal::print(raw_ostream &Out) const {
47 if (!T.isNull())
48 Out << "Tracked " << T << " | ";
49
50 switch (getKind()) {
51 default: llvm_unreachable("Invalid RefVal kind");
52 case Owned: {
53 Out << "Owned";
54 unsigned cnt = getCount();
55 if (cnt) Out << " (+ " << cnt << ")";
56 break;
57 }
58
59 case NotOwned: {
60 Out << "NotOwned";
61 unsigned cnt = getCount();
62 if (cnt) Out << " (+ " << cnt << ")";
63 break;
64 }
65
66 case ReturnedOwned: {
67 Out << "ReturnedOwned";
68 unsigned cnt = getCount();
69 if (cnt) Out << " (+ " << cnt << ")";
70 break;
71 }
72
73 case ReturnedNotOwned: {
74 Out << "ReturnedNotOwned";
75 unsigned cnt = getCount();
76 if (cnt) Out << " (+ " << cnt << ")";
77 break;
78 }
79
80 case Released:
81 Out << "Released";
82 break;
83
84 case ErrorDeallocNotOwned:
85 Out << "-dealloc (not-owned)";
86 break;
87
88 case ErrorLeak:
89 Out << "Leaked";
90 break;
91
92 case ErrorLeakReturned:
93 Out << "Leaked (Bad naming)";
94 break;
95
96 case ErrorUseAfterRelease:
97 Out << "Use-After-Release [ERROR]";
98 break;
99
100 case ErrorReleaseNotOwned:
101 Out << "Release of Not-Owned [ERROR]";
102 break;
103
104 case RefVal::ErrorOverAutorelease:
105 Out << "Over-autoreleased";
106 break;
107
108 case RefVal::ErrorReturnedNotOwned:
109 Out << "Non-owned object returned instead of owned";
110 break;
111 }
112
113 switch (getIvarAccessHistory()) {
114 case IvarAccessHistory::None:
115 break;
116 case IvarAccessHistory::AccessedDirectly:
117 Out << " [direct ivar access]";
118 break;
119 case IvarAccessHistory::ReleasedAfterDirectAccess:
120 Out << " [released after direct ivar access]";
121 }
122
123 if (ACnt) {
124 Out << " [autorelease -" << ACnt << ']';
125 }
126}
127
128namespace {
129class StopTrackingCallback final : public SymbolVisitor {
130 ProgramStateRef state;
131public:
132 StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
133 ProgramStateRef getState() const { return state; }
134
135 bool VisitSymbol(SymbolRef sym) override {
136 state = removeRefBinding(State: state, Sym: sym);
137 return true;
138 }
139};
140} // end anonymous namespace
141
142//===----------------------------------------------------------------------===//
143// Handle statements that may have an effect on refcounts.
144//===----------------------------------------------------------------------===//
145
146void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
147 CheckerContext &C) const {
148
149 // Scan the BlockDecRefExprs for any object the retain count checker
150 // may be tracking.
151 if (!BE->getBlockDecl()->hasCaptures())
152 return;
153
154 ProgramStateRef state = C.getState();
155 auto *R = cast<BlockDataRegion>(Val: C.getSVal(S: BE).getAsRegion());
156
157 auto ReferencedVars = R->referenced_vars();
158 if (ReferencedVars.empty())
159 return;
160
161 // FIXME: For now we invalidate the tracking of all symbols passed to blocks
162 // via captured variables, even though captured variables result in a copy
163 // and in implicit increment/decrement of a retain count.
164 SmallVector<const MemRegion*, 10> Regions;
165 const LocationContext *LC = C.getLocationContext();
166 MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
167
168 for (auto Var : ReferencedVars) {
169 const VarRegion *VR = Var.getCapturedRegion();
170 if (VR->getSuperRegion() == R) {
171 VR = MemMgr.getVarRegion(VD: VR->getDecl(), LC);
172 }
173 Regions.push_back(Elt: VR);
174 }
175
176 state = state->scanReachableSymbols<StopTrackingCallback>(Reachable: Regions).getState();
177 C.addTransition(State: state);
178}
179
180void RetainCountChecker::checkPostStmt(const CastExpr *CE,
181 CheckerContext &C) const {
182 const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(Val: CE);
183 if (!BE)
184 return;
185
186 QualType QT = CE->getType();
187 ObjKind K;
188 if (QT->isObjCObjectPointerType()) {
189 K = ObjKind::ObjC;
190 } else {
191 K = ObjKind::CF;
192 }
193
194 ArgEffect AE = ArgEffect(IncRef, K);
195
196 switch (BE->getBridgeKind()) {
197 case OBC_Bridge:
198 // Do nothing.
199 return;
200 case OBC_BridgeRetained:
201 AE = AE.withKind(NewK: IncRef);
202 break;
203 case OBC_BridgeTransfer:
204 AE = AE.withKind(NewK: DecRefBridgedTransferred);
205 break;
206 }
207
208 ProgramStateRef state = C.getState();
209 SymbolRef Sym = C.getSVal(S: CE).getAsLocSymbol();
210 if (!Sym)
211 return;
212 const RefVal* T = getRefBinding(State: state, Sym);
213 if (!T)
214 return;
215
216 RefVal::Kind hasErr = (RefVal::Kind) 0;
217 state = updateSymbol(state, sym: Sym, V: *T, E: AE, hasErr, C);
218
219 if (hasErr) {
220 // FIXME: If we get an error during a bridge cast, should we report it?
221 return;
222 }
223
224 C.addTransition(State: state);
225}
226
227void RetainCountChecker::processObjCLiterals(CheckerContext &C,
228 const Expr *Ex) const {
229 ProgramStateRef state = C.getState();
230 const ExplodedNode *pred = C.getPredecessor();
231 for (const Stmt *Child : Ex->children()) {
232 SVal V = pred->getSVal(S: Child);
233 if (SymbolRef sym = V.getAsSymbol())
234 if (const RefVal* T = getRefBinding(State: state, Sym: sym)) {
235 RefVal::Kind hasErr = (RefVal::Kind) 0;
236 state = updateSymbol(state, sym, V: *T,
237 E: ArgEffect(MayEscape, ObjKind::ObjC), hasErr, C);
238 if (hasErr) {
239 processNonLeakError(St: state, ErrorRange: Child->getSourceRange(), ErrorKind: hasErr, Sym: sym, C);
240 return;
241 }
242 }
243 }
244
245 // Return the object as autoreleased.
246 // RetEffect RE = RetEffect::MakeNotOwned(ObjKind::ObjC);
247 if (SymbolRef sym =
248 state->getSVal(Ex, LCtx: pred->getLocationContext()).getAsSymbol()) {
249 QualType ResultTy = Ex->getType();
250 state = setRefBinding(State: state, Sym: sym,
251 Val: RefVal::makeNotOwned(o: ObjKind::ObjC, t: ResultTy));
252 }
253
254 C.addTransition(State: state);
255}
256
257void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
258 CheckerContext &C) const {
259 // Apply the 'MayEscape' to all values.
260 processObjCLiterals(C, Ex: AL);
261}
262
263void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
264 CheckerContext &C) const {
265 // Apply the 'MayEscape' to all keys and values.
266 processObjCLiterals(C, Ex: DL);
267}
268
269void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
270 CheckerContext &C) const {
271 const ExplodedNode *Pred = C.getPredecessor();
272 ProgramStateRef State = Pred->getState();
273
274 if (SymbolRef Sym = Pred->getSVal(S: Ex).getAsSymbol()) {
275 QualType ResultTy = Ex->getType();
276 State = setRefBinding(State, Sym,
277 Val: RefVal::makeNotOwned(o: ObjKind::ObjC, t: ResultTy));
278 }
279
280 C.addTransition(State);
281}
282
283void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
284 CheckerContext &C) const {
285 std::optional<Loc> IVarLoc = C.getSVal(S: IRE).getAs<Loc>();
286 if (!IVarLoc)
287 return;
288
289 ProgramStateRef State = C.getState();
290 SymbolRef Sym = State->getSVal(LV: *IVarLoc).getAsSymbol();
291 if (!Sym || !isa_and_nonnull<ObjCIvarRegion>(Val: Sym->getOriginRegion()))
292 return;
293
294 // Accessing an ivar directly is unusual. If we've done that, be more
295 // forgiving about what the surrounding code is allowed to do.
296
297 QualType Ty = Sym->getType();
298 ObjKind Kind;
299 if (Ty->isObjCRetainableType())
300 Kind = ObjKind::ObjC;
301 else if (coreFoundation::isCFObjectRef(T: Ty))
302 Kind = ObjKind::CF;
303 else
304 return;
305
306 // If the value is already known to be nil, don't bother tracking it.
307 ConstraintManager &CMgr = State->getConstraintManager();
308 if (CMgr.isNull(State, Sym).isConstrainedTrue())
309 return;
310
311 if (const RefVal *RV = getRefBinding(State, Sym)) {
312 // If we've seen this symbol before, or we're only seeing it now because
313 // of something the analyzer has synthesized, don't do anything.
314 if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None ||
315 isSynthesizedAccessor(SFC: C.getStackFrame())) {
316 return;
317 }
318
319 // Note that this value has been loaded from an ivar.
320 C.addTransition(State: setRefBinding(State, Sym, Val: RV->withIvarAccess()));
321 return;
322 }
323
324 RefVal PlusZero = RefVal::makeNotOwned(o: Kind, t: Ty);
325
326 // In a synthesized accessor, the effective retain count is +0.
327 if (isSynthesizedAccessor(SFC: C.getStackFrame())) {
328 C.addTransition(State: setRefBinding(State, Sym, Val: PlusZero));
329 return;
330 }
331
332 State = setRefBinding(State, Sym, Val: PlusZero.withIvarAccess());
333 C.addTransition(State);
334}
335
336static bool isReceiverUnconsumedSelf(const CallEvent &Call) {
337 if (const auto *MC = dyn_cast<ObjCMethodCall>(Val: &Call)) {
338
339 // Check if the message is not consumed, we know it will not be used in
340 // an assignment, ex: "self = [super init]".
341 return MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper() &&
342 !Call.getLocationContext()
343 ->getAnalysisDeclContext()
344 ->getParentMap()
345 .isConsumedExpr(E: Call.getOriginExpr());
346 }
347 return false;
348}
349
350const static RetainSummary *getSummary(RetainSummaryManager &Summaries,
351 const CallEvent &Call,
352 QualType ReceiverType) {
353 const Expr *CE = Call.getOriginExpr();
354 AnyCall C =
355 CE ? *AnyCall::forExpr(E: CE)
356 : AnyCall(cast<CXXDestructorDecl>(Val: Call.getDecl()));
357 return Summaries.getSummary(C, HasNonZeroCallbackArg: Call.hasNonZeroCallbackArg(),
358 IsReceiverUnconsumedSelf: isReceiverUnconsumedSelf(Call), ReceiverType);
359}
360
361void RetainCountChecker::checkPostCall(const CallEvent &Call,
362 CheckerContext &C) const {
363 RetainSummaryManager &Summaries = getSummaryManager(C);
364
365 // Leave null if no receiver.
366 QualType ReceiverType;
367 if (const auto *MC = dyn_cast<ObjCMethodCall>(Val: &Call)) {
368 if (MC->isInstanceMessage()) {
369 SVal ReceiverV = MC->getReceiverSVal();
370 if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
371 if (const RefVal *T = getRefBinding(State: C.getState(), Sym))
372 ReceiverType = T->getType();
373 }
374 }
375
376 const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType);
377
378 if (C.wasInlined) {
379 processSummaryOfInlined(Summ: *Summ, Call, C);
380 return;
381 }
382 checkSummary(Summ: *Summ, Call, C);
383}
384
385/// GetReturnType - Used to get the return type of a message expression or
386/// function call with the intention of affixing that type to a tracked symbol.
387/// While the return type can be queried directly from RetEx, when
388/// invoking class methods we augment to the return type to be that of
389/// a pointer to the class (as opposed it just being id).
390// FIXME: We may be able to do this with related result types instead.
391// This function is probably overestimating.
392static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
393 QualType RetTy = RetE->getType();
394 // If RetE is not a message expression just return its type.
395 // If RetE is a message expression, return its types if it is something
396 /// more specific than id.
397 if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(Val: RetE))
398 if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>())
399 if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() ||
400 PT->isObjCClassType()) {
401 // At this point we know the return type of the message expression is
402 // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this
403 // is a call to a class method whose type we can resolve. In such
404 // cases, promote the return type to XXX* (where XXX is the class).
405 const ObjCInterfaceDecl *D = ME->getReceiverInterface();
406 return !D ? RetTy :
407 Ctx.getObjCObjectPointerType(OIT: Ctx.getObjCInterfaceType(Decl: D));
408 }
409
410 return RetTy;
411}
412
413static std::optional<RefVal> refValFromRetEffect(RetEffect RE,
414 QualType ResultTy) {
415 if (RE.isOwned()) {
416 return RefVal::makeOwned(o: RE.getObjKind(), t: ResultTy);
417 } else if (RE.notOwned()) {
418 return RefVal::makeNotOwned(o: RE.getObjKind(), t: ResultTy);
419 }
420
421 return std::nullopt;
422}
423
424static bool isPointerToObject(QualType QT) {
425 QualType PT = QT->getPointeeType();
426 if (!PT.isNull())
427 if (PT->getAsCXXRecordDecl())
428 return true;
429 return false;
430}
431
432/// Whether the tracked value should be escaped on a given call.
433/// OSObjects are escaped when passed to void * / etc.
434static bool shouldEscapeOSArgumentOnCall(const CallEvent &CE, unsigned ArgIdx,
435 const RefVal *TrackedValue) {
436 if (TrackedValue->getObjKind() != ObjKind::OS)
437 return false;
438 if (ArgIdx >= CE.parameters().size())
439 return false;
440 return !isPointerToObject(QT: CE.parameters()[ArgIdx]->getType());
441}
442
443// We don't always get the exact modeling of the function with regards to the
444// retain count checker even when the function is inlined. For example, we need
445// to stop tracking the symbols which were marked with StopTrackingHard.
446void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
447 const CallEvent &CallOrMsg,
448 CheckerContext &C) const {
449 ProgramStateRef state = C.getState();
450
451 // Evaluate the effect of the arguments.
452 for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
453 SVal V = CallOrMsg.getArgSVal(Index: idx);
454
455 if (SymbolRef Sym = V.getAsLocSymbol()) {
456 bool ShouldRemoveBinding = Summ.getArg(idx).getKind() == StopTrackingHard;
457 if (const RefVal *T = getRefBinding(State: state, Sym))
458 if (shouldEscapeOSArgumentOnCall(CE: CallOrMsg, ArgIdx: idx, TrackedValue: T))
459 ShouldRemoveBinding = true;
460
461 if (ShouldRemoveBinding)
462 state = removeRefBinding(State: state, Sym);
463 }
464 }
465
466 // Evaluate the effect on the message receiver.
467 if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(Val: &CallOrMsg)) {
468 if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
469 if (Summ.getReceiverEffect().getKind() == StopTrackingHard) {
470 state = removeRefBinding(State: state, Sym);
471 }
472 }
473 }
474
475 // Consult the summary for the return value.
476 RetEffect RE = Summ.getRetEffect();
477
478 if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
479 if (RE.getKind() == RetEffect::NoRetHard)
480 state = removeRefBinding(State: state, Sym);
481 }
482
483 C.addTransition(State: state);
484}
485
486static bool isSmartPtrField(const MemRegion *MR) {
487 const auto *TR = dyn_cast<TypedValueRegion>(
488 Val: cast<SubRegion>(Val: MR)->getSuperRegion());
489 return TR && RetainSummaryManager::isKnownSmartPointer(QT: TR->getValueType());
490}
491
492
493/// A value escapes in these possible cases:
494///
495/// - binding to something that is not a memory region.
496/// - binding to a memregion that does not have stack storage
497/// - binding to a variable that has a destructor attached using CleanupAttr
498///
499/// We do not currently model what happens when a symbol is
500/// assigned to a struct field, unless it is a known smart pointer
501/// implementation, about which we know that it is inlined.
502/// FIXME: This could definitely be improved upon.
503static bool shouldEscapeRegion(ProgramStateRef State, const MemRegion *R) {
504 if (isSmartPtrField(MR: R))
505 return false;
506
507 const auto *VR = dyn_cast<VarRegion>(Val: R);
508
509 if (!R->hasMemorySpace<StackSpaceRegion>(State) || !VR)
510 return true;
511
512 const VarDecl *VD = VR->getDecl();
513 if (!VD->hasAttr<CleanupAttr>())
514 return false; // CleanupAttr attaches destructors, which cause escaping.
515 return true;
516}
517
518static SmallVector<ProgramStateRef, 2>
519updateOutParameters(ProgramStateRef State, const RetainSummary &Summ,
520 const CallEvent &CE) {
521
522 SVal L = CE.getReturnValue();
523
524 // Splitting is required to support out parameters,
525 // as out parameters might be created only on the "success" branch.
526 // We want to avoid eagerly splitting unless out parameters are actually
527 // needed.
528 bool SplitNecessary = false;
529 for (auto &P : Summ.getArgEffects())
530 if (P.second.getKind() == RetainedOutParameterOnNonZero ||
531 P.second.getKind() == RetainedOutParameterOnZero)
532 SplitNecessary = true;
533
534 ProgramStateRef AssumeNonZeroReturn = State;
535 ProgramStateRef AssumeZeroReturn = State;
536
537 if (SplitNecessary) {
538 if (!CE.getResultType()->isScalarType()) {
539 // Structures cannot be assumed. This probably deserves
540 // a compiler warning for invalid annotations.
541 return {State};
542 }
543 if (auto DL = L.getAs<DefinedOrUnknownSVal>()) {
544 AssumeNonZeroReturn = AssumeNonZeroReturn->assume(Cond: *DL, Assumption: true);
545 AssumeZeroReturn = AssumeZeroReturn->assume(Cond: *DL, Assumption: false);
546 }
547 }
548
549 for (unsigned idx = 0, e = CE.getNumArgs(); idx != e; ++idx) {
550 SVal ArgVal = CE.getArgSVal(Index: idx);
551 ArgEffect AE = Summ.getArg(idx);
552
553 auto *ArgRegion = dyn_cast_or_null<TypedValueRegion>(Val: ArgVal.getAsRegion());
554 if (!ArgRegion)
555 continue;
556
557 QualType PointeeTy = ArgRegion->getValueType();
558 SVal PointeeVal = State->getSVal(R: ArgRegion);
559 SymbolRef Pointee = PointeeVal.getAsLocSymbol();
560 if (!Pointee)
561 continue;
562
563 if (shouldEscapeRegion(State, R: ArgRegion))
564 continue;
565
566 auto makeNotOwnedParameter = [&](ProgramStateRef St) {
567 return setRefBinding(State: St, Sym: Pointee,
568 Val: RefVal::makeNotOwned(o: AE.getObjKind(), t: PointeeTy));
569 };
570 auto makeOwnedParameter = [&](ProgramStateRef St) {
571 return setRefBinding(State: St, Sym: Pointee,
572 Val: RefVal::makeOwned(o: ObjKind::OS, t: PointeeTy));
573 };
574
575 switch (AE.getKind()) {
576 case UnretainedOutParameter:
577 AssumeNonZeroReturn = makeNotOwnedParameter(AssumeNonZeroReturn);
578 AssumeZeroReturn = makeNotOwnedParameter(AssumeZeroReturn);
579 break;
580 case RetainedOutParameter:
581 AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn);
582 AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn);
583 break;
584 case RetainedOutParameterOnNonZero:
585 AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn);
586 break;
587 case RetainedOutParameterOnZero:
588 AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn);
589 break;
590 default:
591 break;
592 }
593 }
594
595 if (SplitNecessary) {
596 return {AssumeNonZeroReturn, AssumeZeroReturn};
597 } else {
598 assert(AssumeZeroReturn == AssumeNonZeroReturn);
599 return {AssumeZeroReturn};
600 }
601}
602
603void RetainCountChecker::checkSummary(const RetainSummary &Summ,
604 const CallEvent &CallOrMsg,
605 CheckerContext &C) const {
606 ProgramStateRef state = C.getState();
607
608 // Evaluate the effect of the arguments.
609 RefVal::Kind hasErr = (RefVal::Kind) 0;
610 SourceRange ErrorRange;
611 SymbolRef ErrorSym = nullptr;
612
613 // Helper tag for providing diagnostics: indicate whether dealloc was sent
614 // at this location.
615 bool DeallocSent = false;
616
617 for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
618 SVal V = CallOrMsg.getArgSVal(Index: idx);
619
620 ArgEffect Effect = Summ.getArg(idx);
621 if (SymbolRef Sym = V.getAsLocSymbol()) {
622 if (const RefVal *T = getRefBinding(State: state, Sym)) {
623
624 if (shouldEscapeOSArgumentOnCall(CE: CallOrMsg, ArgIdx: idx, TrackedValue: T))
625 Effect = ArgEffect(StopTrackingHard, ObjKind::OS);
626
627 state = updateSymbol(state, sym: Sym, V: *T, E: Effect, hasErr, C);
628 if (hasErr) {
629 ErrorRange = CallOrMsg.getArgSourceRange(Index: idx);
630 ErrorSym = Sym;
631 break;
632 } else if (Effect.getKind() == Dealloc) {
633 DeallocSent = true;
634 }
635 }
636 }
637 }
638
639 // Evaluate the effect on the message receiver / `this` argument.
640 bool ReceiverIsTracked = false;
641 if (!hasErr) {
642 if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(Val: &CallOrMsg)) {
643 if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
644 if (const RefVal *T = getRefBinding(State: state, Sym)) {
645 ReceiverIsTracked = true;
646 state = updateSymbol(state, sym: Sym, V: *T,
647 E: Summ.getReceiverEffect(), hasErr, C);
648 if (hasErr) {
649 ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
650 ErrorSym = Sym;
651 } else if (Summ.getReceiverEffect().getKind() == Dealloc) {
652 DeallocSent = true;
653 }
654 }
655 }
656 } else if (const auto *MCall = dyn_cast<CXXMemberCall>(Val: &CallOrMsg)) {
657 if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) {
658 if (const RefVal *T = getRefBinding(State: state, Sym)) {
659 state = updateSymbol(state, sym: Sym, V: *T, E: Summ.getThisEffect(),
660 hasErr, C);
661 if (hasErr) {
662 ErrorRange = MCall->getOriginExpr()->getSourceRange();
663 ErrorSym = Sym;
664 }
665 }
666 }
667 }
668 }
669
670 // Process any errors.
671 if (hasErr) {
672 processNonLeakError(St: state, ErrorRange, ErrorKind: hasErr, Sym: ErrorSym, C);
673 return;
674 }
675
676 // Consult the summary for the return value.
677 RetEffect RE = Summ.getRetEffect();
678
679 if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
680 if (ReceiverIsTracked)
681 RE = getSummaryManager(C).getObjAllocRetEffect();
682 else
683 RE = RetEffect::MakeNoRet();
684 }
685
686 if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
687 QualType ResultTy = CallOrMsg.getResultType();
688 if (RE.notOwned()) {
689 const Expr *Ex = CallOrMsg.getOriginExpr();
690 assert(Ex);
691 ResultTy = GetReturnType(RetE: Ex, Ctx&: C.getASTContext());
692 }
693 if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
694 state = setRefBinding(State: state, Sym, Val: *updatedRefVal);
695 }
696
697 SmallVector<ProgramStateRef, 2> Out =
698 updateOutParameters(State: state, Summ, CE: CallOrMsg);
699
700 for (ProgramStateRef St : Out) {
701 if (DeallocSent) {
702 C.addTransition(State: St, Pred: C.getPredecessor(), Tag: &getDeallocSentTag());
703 } else {
704 C.addTransition(State: St);
705 }
706 }
707}
708
709ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
710 SymbolRef sym, RefVal V,
711 ArgEffect AE,
712 RefVal::Kind &hasErr,
713 CheckerContext &C) const {
714 bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
715 if (AE.getObjKind() == ObjKind::ObjC && IgnoreRetainMsg) {
716 switch (AE.getKind()) {
717 default:
718 break;
719 case IncRef:
720 AE = AE.withKind(NewK: DoNothing);
721 break;
722 case DecRef:
723 AE = AE.withKind(NewK: DoNothing);
724 break;
725 case DecRefAndStopTrackingHard:
726 AE = AE.withKind(NewK: StopTracking);
727 break;
728 }
729 }
730
731 // Handle all use-after-releases.
732 if (V.getKind() == RefVal::Released) {
733 V = V ^ RefVal::ErrorUseAfterRelease;
734 hasErr = V.getKind();
735 return setRefBinding(State: state, Sym: sym, Val: V);
736 }
737
738 switch (AE.getKind()) {
739 case UnretainedOutParameter:
740 case RetainedOutParameter:
741 case RetainedOutParameterOnZero:
742 case RetainedOutParameterOnNonZero:
743 llvm_unreachable("Applies to pointer-to-pointer parameters, which should "
744 "not have ref state.");
745
746 case Dealloc: // NB. we only need to add a note in a non-error case.
747 switch (V.getKind()) {
748 default:
749 llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
750 case RefVal::Owned:
751 // The object immediately transitions to the released state.
752 V = V ^ RefVal::Released;
753 V.clearCounts();
754 return setRefBinding(State: state, Sym: sym, Val: V);
755 case RefVal::NotOwned:
756 V = V ^ RefVal::ErrorDeallocNotOwned;
757 hasErr = V.getKind();
758 break;
759 }
760 break;
761
762 case MayEscape:
763 if (V.getKind() == RefVal::Owned) {
764 V = V ^ RefVal::NotOwned;
765 break;
766 }
767
768 [[fallthrough]];
769
770 case DoNothing:
771 return state;
772
773 case Autorelease:
774 // Update the autorelease counts.
775 V = V.autorelease();
776 break;
777
778 case StopTracking:
779 case StopTrackingHard:
780 return removeRefBinding(State: state, Sym: sym);
781
782 case IncRef:
783 switch (V.getKind()) {
784 default:
785 llvm_unreachable("Invalid RefVal state for a retain.");
786 case RefVal::Owned:
787 case RefVal::NotOwned:
788 V = V + 1;
789 break;
790 }
791 break;
792
793 case DecRef:
794 case DecRefBridgedTransferred:
795 case DecRefAndStopTrackingHard:
796 switch (V.getKind()) {
797 default:
798 // case 'RefVal::Released' handled above.
799 llvm_unreachable("Invalid RefVal state for a release.");
800
801 case RefVal::Owned:
802 assert(V.getCount() > 0);
803 if (V.getCount() == 1) {
804 if (AE.getKind() == DecRefBridgedTransferred ||
805 V.getIvarAccessHistory() ==
806 RefVal::IvarAccessHistory::AccessedDirectly)
807 V = V ^ RefVal::NotOwned;
808 else
809 V = V ^ RefVal::Released;
810 } else if (AE.getKind() == DecRefAndStopTrackingHard) {
811 return removeRefBinding(State: state, Sym: sym);
812 }
813
814 V = V - 1;
815 break;
816
817 case RefVal::NotOwned:
818 if (V.getCount() > 0) {
819 if (AE.getKind() == DecRefAndStopTrackingHard)
820 return removeRefBinding(State: state, Sym: sym);
821 V = V - 1;
822 } else if (V.getIvarAccessHistory() ==
823 RefVal::IvarAccessHistory::AccessedDirectly) {
824 // Assume that the instance variable was holding on the object at
825 // +1, and we just didn't know.
826 if (AE.getKind() == DecRefAndStopTrackingHard)
827 return removeRefBinding(State: state, Sym: sym);
828 V = V.releaseViaIvar() ^ RefVal::Released;
829 } else {
830 V = V ^ RefVal::ErrorReleaseNotOwned;
831 hasErr = V.getKind();
832 }
833 break;
834 }
835 break;
836 }
837 return setRefBinding(State: state, Sym: sym, Val: V);
838}
839
840const RefCountBug &
841RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind,
842 SymbolRef Sym) const {
843 switch (ErrorKind) {
844 case RefVal::ErrorUseAfterRelease:
845 return *UseAfterRelease;
846 case RefVal::ErrorReleaseNotOwned:
847 return *ReleaseNotOwned;
848 case RefVal::ErrorDeallocNotOwned:
849 if (Sym->getType()->getPointeeCXXRecordDecl())
850 return *FreeNotOwned;
851 return *DeallocNotOwned;
852 default:
853 llvm_unreachable("Unhandled error.");
854 }
855}
856
857void RetainCountChecker::processNonLeakError(ProgramStateRef St,
858 SourceRange ErrorRange,
859 RefVal::Kind ErrorKind,
860 SymbolRef Sym,
861 CheckerContext &C) const {
862 // HACK: Ignore retain-count issues on values accessed through ivars,
863 // because of cases like this:
864 // [_contentView retain];
865 // [_contentView removeFromSuperview];
866 // [self addSubview:_contentView]; // invalidates 'self'
867 // [_contentView release];
868 if (const RefVal *RV = getRefBinding(State: St, Sym))
869 if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
870 return;
871
872 ExplodedNode *N = C.generateErrorNode(State: St);
873 if (!N)
874 return;
875
876 auto report = std::make_unique<RefCountReport>(
877 args: errorKindToBugKind(ErrorKind, Sym),
878 args: C.getASTContext().getLangOpts(), args&: N, args&: Sym);
879 report->addRange(R: ErrorRange);
880 C.emitReport(R: std::move(report));
881}
882
883//===----------------------------------------------------------------------===//
884// Handle the return values of retain-count-related functions.
885//===----------------------------------------------------------------------===//
886
887bool RetainCountChecker::evalCall(const CallEvent &Call,
888 CheckerContext &C) const {
889 ProgramStateRef state = C.getState();
890 const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl());
891 if (!FD)
892 return false;
893
894 const auto *CE = dyn_cast_or_null<CallExpr>(Val: Call.getOriginExpr());
895 if (!CE)
896 return false;
897
898 RetainSummaryManager &SmrMgr = getSummaryManager(C);
899 QualType ResultTy = Call.getResultType();
900
901 // See if the function has 'rc_ownership_trusted_implementation'
902 // annotate attribute. If it does, we will not inline it.
903 bool hasTrustedImplementationAnnotation = false;
904
905 const LocationContext *LCtx = C.getLocationContext();
906
907 using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
908 std::optional<BehaviorSummary> BSmr =
909 SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
910
911 // See if it's one of the specific functions we know how to eval.
912 if (!BSmr)
913 return false;
914
915 // Bind the return value.
916 if (BSmr == BehaviorSummary::Identity ||
917 BSmr == BehaviorSummary::IdentityOrZero ||
918 BSmr == BehaviorSummary::IdentityThis) {
919
920 const Expr *BindReturnTo =
921 (BSmr == BehaviorSummary::IdentityThis)
922 ? cast<CXXMemberCallExpr>(Val: CE)->getImplicitObjectArgument()
923 : CE->getArg(Arg: 0);
924 SVal RetVal = state->getSVal(Ex: BindReturnTo, LCtx);
925
926 // If the receiver is unknown or the function has
927 // 'rc_ownership_trusted_implementation' annotate attribute, conjure a
928 // return value.
929 // FIXME: this branch is very strange.
930 if (RetVal.isUnknown() ||
931 (hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
932 SValBuilder &SVB = C.getSValBuilder();
933 RetVal = SVB.conjureSymbolVal(call: Call, visitCount: C.blockCount());
934 }
935
936 // Bind the value.
937 state = state->BindExpr(S: CE, LCtx, V: RetVal, /*Invalidate=*/false);
938
939 if (BSmr == BehaviorSummary::IdentityOrZero) {
940 // Add a branch where the output is zero.
941 ProgramStateRef NullOutputState = C.getState();
942
943 // Assume that output is zero on the other branch.
944 NullOutputState = NullOutputState->BindExpr(
945 S: CE, LCtx, V: C.getSValBuilder().makeNullWithType(type: ResultTy),
946 /*Invalidate=*/false);
947 C.addTransition(State: NullOutputState, Tag: &getCastFailTag());
948
949 // And on the original branch assume that both input and
950 // output are non-zero.
951 if (auto L = RetVal.getAs<DefinedOrUnknownSVal>())
952 state = state->assume(Cond: *L, /*assumption=*/Assumption: true);
953
954 }
955 }
956
957 C.addTransition(State: state);
958 return true;
959}
960
961ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
962 CheckerContext &C) const {
963 ExplodedNode *Pred = C.getPredecessor();
964
965 // Only adjust the reference count if this is the top-level call frame,
966 // and not the result of inlining. In the future, we should do
967 // better checking even for inlined calls, and see if they match
968 // with their expected semantics (e.g., the method should return a retained
969 // object, etc.).
970 if (!C.inTopFrame())
971 return Pred;
972
973 if (!S)
974 return Pred;
975
976 const Expr *RetE = S->getRetValue();
977 if (!RetE)
978 return Pred;
979
980 ProgramStateRef state = C.getState();
981 // We need to dig down to the symbolic base here because various
982 // custom allocators do sometimes return the symbol with an offset.
983 SymbolRef Sym = state->getSValAsScalarOrLoc(S: RetE, LCtx: C.getLocationContext())
984 .getAsLocSymbol(/*IncludeBaseRegions=*/true);
985 if (!Sym)
986 return Pred;
987
988 // Get the reference count binding (if any).
989 const RefVal *T = getRefBinding(State: state, Sym);
990 if (!T)
991 return Pred;
992
993 // Change the reference count.
994 RefVal X = *T;
995
996 switch (X.getKind()) {
997 case RefVal::Owned: {
998 unsigned cnt = X.getCount();
999 assert(cnt > 0);
1000 X.setCount(cnt - 1);
1001 X = X ^ RefVal::ReturnedOwned;
1002 break;
1003 }
1004
1005 case RefVal::NotOwned: {
1006 unsigned cnt = X.getCount();
1007 if (cnt) {
1008 X.setCount(cnt - 1);
1009 X = X ^ RefVal::ReturnedOwned;
1010 } else {
1011 X = X ^ RefVal::ReturnedNotOwned;
1012 }
1013 break;
1014 }
1015
1016 default:
1017 return Pred;
1018 }
1019
1020 // Update the binding.
1021 state = setRefBinding(State: state, Sym, Val: X);
1022 Pred = C.addTransition(State: state);
1023
1024 // At this point we have updated the state properly.
1025 // Everything after this is merely checking to see if the return value has
1026 // been over- or under-retained.
1027
1028 // Did we cache out?
1029 if (!Pred)
1030 return nullptr;
1031
1032 // Update the autorelease counts.
1033 state = handleAutoreleaseCounts(state, Pred, Ctx&: C, Sym, V: X, S);
1034
1035 // Have we generated a sink node?
1036 if (!state)
1037 return nullptr;
1038
1039 // Get the updated binding.
1040 T = getRefBinding(State: state, Sym);
1041 assert(T);
1042 X = *T;
1043
1044 // Consult the summary of the enclosing method.
1045 RetainSummaryManager &Summaries = getSummaryManager(C);
1046 const Decl *CD = &Pred->getCodeDecl();
1047 RetEffect RE = RetEffect::MakeNoRet();
1048
1049 // FIXME: What is the convention for blocks? Is there one?
1050 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(Val: CD)) {
1051 const RetainSummary *Summ = Summaries.getSummary(C: AnyCall(MD));
1052 RE = Summ->getRetEffect();
1053 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Val: CD)) {
1054 if (!isa<CXXMethodDecl>(Val: FD)) {
1055 const RetainSummary *Summ = Summaries.getSummary(C: AnyCall(FD));
1056 RE = Summ->getRetEffect();
1057 }
1058 }
1059
1060 return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
1061}
1062
1063ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
1064 CheckerContext &C,
1065 ExplodedNode *Pred,
1066 RetEffect RE, RefVal X,
1067 SymbolRef Sym,
1068 ProgramStateRef state) const {
1069 // HACK: Ignore retain-count issues on values accessed through ivars,
1070 // because of cases like this:
1071 // [_contentView retain];
1072 // [_contentView removeFromSuperview];
1073 // [self addSubview:_contentView]; // invalidates 'self'
1074 // [_contentView release];
1075 if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
1076 return Pred;
1077
1078 // Any leaks or other errors?
1079 if (X.isReturnedOwned() && X.getCount() == 0) {
1080 if (RE.getKind() != RetEffect::NoRet) {
1081 if (!RE.isOwned()) {
1082
1083 // The returning type is a CF, we expect the enclosing method should
1084 // return ownership.
1085 X = X ^ RefVal::ErrorLeakReturned;
1086
1087 // Generate an error node.
1088 state = setRefBinding(State: state, Sym, Val: X);
1089
1090 ExplodedNode *N = C.addTransition(State: state, Pred);
1091 if (N) {
1092 const LangOptions &LOpts = C.getASTContext().getLangOpts();
1093 auto R =
1094 std::make_unique<RefLeakReport>(args&: *LeakAtReturn, args: LOpts, args&: N, args&: Sym, args&: C);
1095 C.emitReport(R: std::move(R));
1096 }
1097 return N;
1098 }
1099 }
1100 } else if (X.isReturnedNotOwned()) {
1101 if (RE.isOwned()) {
1102 if (X.getIvarAccessHistory() ==
1103 RefVal::IvarAccessHistory::AccessedDirectly) {
1104 // Assume the method was trying to transfer a +1 reference from a
1105 // strong ivar to the caller.
1106 state = setRefBinding(State: state, Sym,
1107 Val: X.releaseViaIvar() ^ RefVal::ReturnedOwned);
1108 } else {
1109 // Trying to return a not owned object to a caller expecting an
1110 // owned object.
1111 state = setRefBinding(State: state, Sym, Val: X ^ RefVal::ErrorReturnedNotOwned);
1112
1113 ExplodedNode *N = C.addTransition(State: state, Pred);
1114 if (N) {
1115 auto R = std::make_unique<RefCountReport>(
1116 args&: *ReturnNotOwnedForOwned, args: C.getASTContext().getLangOpts(), args&: N, args&: Sym);
1117 C.emitReport(R: std::move(R));
1118 }
1119 return N;
1120 }
1121 }
1122 }
1123 return Pred;
1124}
1125
1126//===----------------------------------------------------------------------===//
1127// Check various ways a symbol can be invalidated.
1128//===----------------------------------------------------------------------===//
1129
1130void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
1131 CheckerContext &C) const {
1132 ProgramStateRef state = C.getState();
1133 const MemRegion *MR = loc.getAsRegion();
1134
1135 // Find all symbols referenced by 'val' that we are tracking
1136 // and stop tracking them.
1137 if (MR && shouldEscapeRegion(State: state, R: MR)) {
1138 state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
1139 C.addTransition(State: state);
1140 }
1141}
1142
1143ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
1144 SVal Cond,
1145 bool Assumption) const {
1146 // FIXME: We may add to the interface of evalAssume the list of symbols
1147 // whose assumptions have changed. For now we just iterate through the
1148 // bindings and check if any of the tracked symbols are NULL. This isn't
1149 // too bad since the number of symbols we will track in practice are
1150 // probably small and evalAssume is only called at branches and a few
1151 // other places.
1152 RefBindingsTy B = state->get<RefBindings>();
1153
1154 if (B.isEmpty())
1155 return state;
1156
1157 bool changed = false;
1158 RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>();
1159 ConstraintManager &CMgr = state->getConstraintManager();
1160
1161 for (auto &I : B) {
1162 // Check if the symbol is null stop tracking the symbol.
1163 ConditionTruthVal AllocFailed = CMgr.isNull(State: state, Sym: I.first);
1164 if (AllocFailed.isConstrainedTrue()) {
1165 changed = true;
1166 B = RefBFactory.remove(Old: B, K: I.first);
1167 }
1168 }
1169
1170 if (changed)
1171 state = state->set<RefBindings>(B);
1172
1173 return state;
1174}
1175
1176ProgramStateRef RetainCountChecker::checkRegionChanges(
1177 ProgramStateRef state, const InvalidatedSymbols *invalidated,
1178 ArrayRef<const MemRegion *> ExplicitRegions,
1179 ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
1180 const CallEvent *Call) const {
1181 if (!invalidated)
1182 return state;
1183
1184 llvm::SmallPtrSet<SymbolRef, 8> AllowedSymbols;
1185
1186 for (const MemRegion *I : ExplicitRegions)
1187 if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>())
1188 AllowedSymbols.insert(Ptr: SR->getSymbol());
1189
1190 for (SymbolRef sym : *invalidated) {
1191 if (AllowedSymbols.count(Ptr: sym))
1192 continue;
1193 // Remove any existing reference-count binding.
1194 state = removeRefBinding(State: state, Sym: sym);
1195 }
1196 return state;
1197}
1198
1199ProgramStateRef RetainCountChecker::handleAutoreleaseCounts(
1200 ProgramStateRef state, ExplodedNode *Pred, CheckerContext &Ctx,
1201 SymbolRef Sym, RefVal V, const ReturnStmt *S) const {
1202 unsigned ACnt = V.getAutoreleaseCount();
1203
1204 // No autorelease counts? Nothing to be done.
1205 if (!ACnt)
1206 return state;
1207
1208 unsigned Cnt = V.getCount();
1209
1210 // FIXME: Handle sending 'autorelease' to already released object.
1211
1212 if (V.getKind() == RefVal::ReturnedOwned)
1213 ++Cnt;
1214
1215 // If we would over-release here, but we know the value came from an ivar,
1216 // assume it was a strong ivar that's just been relinquished.
1217 if (ACnt > Cnt &&
1218 V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) {
1219 V = V.releaseViaIvar();
1220 --ACnt;
1221 }
1222
1223 if (ACnt <= Cnt) {
1224 if (ACnt == Cnt) {
1225 V.clearCounts();
1226 if (V.getKind() == RefVal::ReturnedOwned) {
1227 V = V ^ RefVal::ReturnedNotOwned;
1228 } else {
1229 V = V ^ RefVal::NotOwned;
1230 }
1231 } else {
1232 V.setCount(V.getCount() - ACnt);
1233 V.setAutoreleaseCount(0);
1234 }
1235 return setRefBinding(State: state, Sym, Val: V);
1236 }
1237
1238 // HACK: Ignore retain-count issues on values accessed through ivars,
1239 // because of cases like this:
1240 // [_contentView retain];
1241 // [_contentView removeFromSuperview];
1242 // [self addSubview:_contentView]; // invalidates 'self'
1243 // [_contentView release];
1244 if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
1245 return state;
1246
1247 // Woah! More autorelease counts then retain counts left.
1248 // Emit hard error.
1249 V = V ^ RefVal::ErrorOverAutorelease;
1250 state = setRefBinding(State: state, Sym, Val: V);
1251
1252 ExplodedNode *N = Ctx.generateSink(State: state, Pred);
1253 if (N) {
1254 SmallString<128> sbuf;
1255 llvm::raw_svector_ostream os(sbuf);
1256 os << "Object was autoreleased ";
1257 if (V.getAutoreleaseCount() > 1)
1258 os << V.getAutoreleaseCount() << " times but the object ";
1259 else
1260 os << "but ";
1261 os << "has a +" << V.getCount() << " retain count";
1262
1263 const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
1264 auto R = std::make_unique<RefCountReport>(args&: *OverAutorelease, args: LOpts, args&: N, args&: Sym,
1265 args: os.str());
1266 Ctx.emitReport(R: std::move(R));
1267 }
1268
1269 return nullptr;
1270}
1271
1272ProgramStateRef
1273RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
1274 SymbolRef sid, RefVal V,
1275 SmallVectorImpl<SymbolRef> &Leaked) const {
1276 bool hasLeak;
1277
1278 // HACK: Ignore retain-count issues on values accessed through ivars,
1279 // because of cases like this:
1280 // [_contentView retain];
1281 // [_contentView removeFromSuperview];
1282 // [self addSubview:_contentView]; // invalidates 'self'
1283 // [_contentView release];
1284 if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
1285 hasLeak = false;
1286 else if (V.isOwned())
1287 hasLeak = true;
1288 else if (V.isNotOwned() || V.isReturnedOwned())
1289 hasLeak = (V.getCount() > 0);
1290 else
1291 hasLeak = false;
1292
1293 if (!hasLeak)
1294 return removeRefBinding(State: state, Sym: sid);
1295
1296 Leaked.push_back(Elt: sid);
1297 return setRefBinding(State: state, Sym: sid, Val: V ^ RefVal::ErrorLeak);
1298}
1299
1300ExplodedNode *
1301RetainCountChecker::processLeaks(ProgramStateRef state,
1302 SmallVectorImpl<SymbolRef> &Leaked,
1303 CheckerContext &Ctx,
1304 ExplodedNode *Pred) const {
1305 // Generate an intermediate node representing the leak point.
1306 ExplodedNode *N = Ctx.addTransition(State: state, Pred);
1307 const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
1308
1309 if (N) {
1310 for (SymbolRef L : Leaked) {
1311 const RefCountBug &BT = Pred ? *LeakWithinFunction : *LeakAtReturn;
1312 Ctx.emitReport(R: std::make_unique<RefLeakReport>(args: BT, args: LOpts, args&: N, args&: L, args&: Ctx));
1313 }
1314 }
1315
1316 return N;
1317}
1318
1319void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
1320 if (!Ctx.inTopFrame())
1321 return;
1322
1323 RetainSummaryManager &SmrMgr = getSummaryManager(C&: Ctx);
1324 const LocationContext *LCtx = Ctx.getLocationContext();
1325 const Decl *D = LCtx->getDecl();
1326 std::optional<AnyCall> C = AnyCall::forDecl(D);
1327
1328 if (!C || SmrMgr.isTrustedReferenceCountImplementation(FD: D))
1329 return;
1330
1331 ProgramStateRef state = Ctx.getState();
1332 const RetainSummary *FunctionSummary = SmrMgr.getSummary(C: *C);
1333 ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
1334
1335 for (unsigned idx = 0, e = C->param_size(); idx != e; ++idx) {
1336 const ParmVarDecl *Param = C->parameters()[idx];
1337 SymbolRef Sym = state->getSVal(R: state->getRegion(D: Param, LC: LCtx)).getAsSymbol();
1338
1339 QualType Ty = Param->getType();
1340 const ArgEffect *AE = CalleeSideArgEffects.lookup(K: idx);
1341 if (AE) {
1342 ObjKind K = AE->getObjKind();
1343 if (K == ObjKind::Generalized || K == ObjKind::OS ||
1344 (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) {
1345 RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(o: K, t: Ty)
1346 : RefVal::makeNotOwned(o: K, t: Ty);
1347 state = setRefBinding(State: state, Sym, Val: NewVal);
1348 }
1349 }
1350 }
1351
1352 Ctx.addTransition(State: state);
1353}
1354
1355void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
1356 CheckerContext &Ctx) const {
1357 ExplodedNode *Pred = processReturn(S: RS, C&: Ctx);
1358
1359 // Created state cached out.
1360 if (!Pred) {
1361 return;
1362 }
1363
1364 ProgramStateRef state = Pred->getState();
1365 RefBindingsTy B = state->get<RefBindings>();
1366
1367 // Don't process anything within synthesized bodies.
1368 const LocationContext *LCtx = Pred->getLocationContext();
1369 if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) {
1370 assert(!LCtx->inTopFrame());
1371 return;
1372 }
1373
1374 for (auto &I : B) {
1375 state = handleAutoreleaseCounts(state, Pred, Ctx, Sym: I.first, V: I.second);
1376 if (!state)
1377 return;
1378 }
1379
1380 // If the current LocationContext has a parent, don't check for leaks.
1381 // We will do that later.
1382 // FIXME: we should instead check for imbalances of the retain/releases,
1383 // and suggest annotations.
1384 if (LCtx->getParent())
1385 return;
1386
1387 B = state->get<RefBindings>();
1388 SmallVector<SymbolRef, 10> Leaked;
1389
1390 for (auto &I : B)
1391 state = handleSymbolDeath(state, sid: I.first, V: I.second, Leaked);
1392
1393 processLeaks(state, Leaked, Ctx, Pred);
1394}
1395
1396void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1397 CheckerContext &C) const {
1398 ExplodedNode *Pred = C.getPredecessor();
1399
1400 ProgramStateRef state = C.getState();
1401 SmallVector<SymbolRef, 10> Leaked;
1402
1403 // Update counts from autorelease pools
1404 for (const auto &I: state->get<RefBindings>()) {
1405 SymbolRef Sym = I.first;
1406 if (SymReaper.isDead(sym: Sym)) {
1407 const RefVal &V = I.second;
1408 state = handleAutoreleaseCounts(state, Pred, Ctx&: C, Sym, V);
1409 if (!state)
1410 return;
1411
1412 // Fetch the new reference count from the state, and use it to handle
1413 // this symbol.
1414 state = handleSymbolDeath(state, sid: Sym, V: *getRefBinding(State: state, Sym), Leaked);
1415 }
1416 }
1417
1418 if (Leaked.empty()) {
1419 C.addTransition(State: state);
1420 return;
1421 }
1422
1423 Pred = processLeaks(state, Leaked, Ctx&: C, Pred);
1424
1425 // Did we cache out?
1426 if (!Pred)
1427 return;
1428
1429 // Now generate a new node that nukes the old bindings.
1430 // The only bindings left at this point are the leaked symbols.
1431 RefBindingsTy::Factory &F = state->get_context<RefBindings>();
1432 RefBindingsTy B = state->get<RefBindings>();
1433
1434 for (SymbolRef L : Leaked)
1435 B = F.remove(Old: B, K: L);
1436
1437 state = state->set<RefBindings>(B);
1438 C.addTransition(State: state, Pred);
1439}
1440
1441void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
1442 const char *NL, const char *Sep) const {
1443
1444 RefBindingsTy B = State->get<RefBindings>();
1445
1446 if (B.isEmpty())
1447 return;
1448
1449 Out << Sep << NL;
1450
1451 for (auto &I : B) {
1452 Out << I.first << " : ";
1453 I.second.print(Out);
1454 Out << NL;
1455 }
1456}
1457
1458//===----------------------------------------------------------------------===//
1459// Checker registration.
1460//===----------------------------------------------------------------------===//
1461
1462std::unique_ptr<SimpleProgramPointTag> RetainCountChecker::DeallocSentTag;
1463std::unique_ptr<SimpleProgramPointTag> RetainCountChecker::CastFailTag;
1464
1465void ento::registerRetainCountBase(CheckerManager &Mgr) {
1466 auto *Chk = Mgr.registerChecker<RetainCountChecker>();
1467 Chk->DeallocSentTag = std::make_unique<SimpleProgramPointTag>(
1468 args: "RetainCountChecker", args: "DeallocSent");
1469 Chk->CastFailTag = std::make_unique<SimpleProgramPointTag>(
1470 args: "RetainCountChecker", args: "DynamicCastFail");
1471}
1472
1473bool ento::shouldRegisterRetainCountBase(const CheckerManager &mgr) {
1474 return true;
1475}
1476void ento::registerRetainCountChecker(CheckerManager &Mgr) {
1477 auto *Chk = Mgr.getChecker<RetainCountChecker>();
1478 Chk->TrackObjCAndCFObjects = true;
1479 Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
1480 CheckerName: Mgr.getCurrentCheckerName(), OptionName: "TrackNSCFStartParam");
1481
1482#define INIT_BUGTYPE(KIND) \
1483 Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \
1484 RefCountBug::KIND);
1485 // TODO: Ideally, we should have a checker for each of these bug types.
1486 INIT_BUGTYPE(UseAfterRelease)
1487 INIT_BUGTYPE(ReleaseNotOwned)
1488 INIT_BUGTYPE(DeallocNotOwned)
1489 INIT_BUGTYPE(FreeNotOwned)
1490 INIT_BUGTYPE(OverAutorelease)
1491 INIT_BUGTYPE(ReturnNotOwnedForOwned)
1492 INIT_BUGTYPE(LeakWithinFunction)
1493 INIT_BUGTYPE(LeakAtReturn)
1494#undef INIT_BUGTYPE
1495}
1496
1497bool ento::shouldRegisterRetainCountChecker(const CheckerManager &mgr) {
1498 return true;
1499}
1500
1501void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
1502 auto *Chk = Mgr.getChecker<RetainCountChecker>();
1503 Chk->TrackOSObjects = true;
1504
1505 // FIXME: We want bug reports to always have the same checker name associated
1506 // with them, yet here, if RetainCountChecker is disabled but
1507 // OSObjectRetainCountChecker is enabled, the checker names will be different.
1508 // This hack will make it so that the checker name depends on which checker is
1509 // enabled rather than on the registration order.
1510 // For the most part, we want **non-hidden checkers** to be associated with
1511 // diagnostics, and **hidden checker options** with the fine-tuning of
1512 // modeling. Following this logic, OSObjectRetainCountChecker should be the
1513 // latter, but we can't just remove it for backward compatibility reasons.
1514#define LAZY_INIT_BUGTYPE(KIND) \
1515 if (!Chk->KIND) \
1516 Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \
1517 RefCountBug::KIND);
1518 LAZY_INIT_BUGTYPE(UseAfterRelease)
1519 LAZY_INIT_BUGTYPE(ReleaseNotOwned)
1520 LAZY_INIT_BUGTYPE(DeallocNotOwned)
1521 LAZY_INIT_BUGTYPE(FreeNotOwned)
1522 LAZY_INIT_BUGTYPE(OverAutorelease)
1523 LAZY_INIT_BUGTYPE(ReturnNotOwnedForOwned)
1524 LAZY_INIT_BUGTYPE(LeakWithinFunction)
1525 LAZY_INIT_BUGTYPE(LeakAtReturn)
1526#undef LAZY_INIT_BUGTYPE
1527}
1528
1529bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &mgr) {
1530 return true;
1531}
1532