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 const RefCountFrontend &FE = getPreferredFrontend();
844
845 switch (ErrorKind) {
846 case RefVal::ErrorUseAfterRelease:
847 return FE.UseAfterRelease;
848 case RefVal::ErrorReleaseNotOwned:
849 return FE.ReleaseNotOwned;
850 case RefVal::ErrorDeallocNotOwned:
851 if (Sym->getType()->getPointeeCXXRecordDecl())
852 return FE.FreeNotOwned;
853 return FE.DeallocNotOwned;
854 default:
855 llvm_unreachable("Unhandled error.");
856 }
857}
858
859bool RetainCountChecker::isReleaseUnownedError(RefVal::Kind ErrorKind) const {
860 return ErrorKind == RefVal::ErrorReleaseNotOwned ||
861 ErrorKind == RefVal::ErrorDeallocNotOwned;
862}
863
864void RetainCountChecker::processNonLeakError(ProgramStateRef St,
865 SourceRange ErrorRange,
866 RefVal::Kind ErrorKind,
867 SymbolRef Sym,
868 CheckerContext &C) const {
869 // HACK: Ignore retain-count issues on values accessed through ivars,
870 // because of cases like this:
871 // [_contentView retain];
872 // [_contentView removeFromSuperview];
873 // [self addSubview:_contentView]; // invalidates 'self'
874 // [_contentView release];
875 if (const RefVal *RV = getRefBinding(State: St, Sym))
876 if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
877 return;
878
879 ExplodedNode *N = C.generateErrorNode(State: St);
880 if (!N)
881 return;
882
883 auto report = std::make_unique<RefCountReport>(
884 args: errorKindToBugKind(ErrorKind, Sym), args: C.getASTContext().getLangOpts(), args&: N,
885 args&: Sym, /*isLeak=*/args: false, args: isReleaseUnownedError(ErrorKind));
886 report->addRange(R: ErrorRange);
887 C.emitReport(R: std::move(report));
888}
889
890//===----------------------------------------------------------------------===//
891// Handle the return values of retain-count-related functions.
892//===----------------------------------------------------------------------===//
893
894bool RetainCountChecker::evalCall(const CallEvent &Call,
895 CheckerContext &C) const {
896 ProgramStateRef state = C.getState();
897 const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl());
898 if (!FD)
899 return false;
900
901 const auto *CE = dyn_cast_or_null<CallExpr>(Val: Call.getOriginExpr());
902 if (!CE)
903 return false;
904
905 RetainSummaryManager &SmrMgr = getSummaryManager(C);
906 QualType ResultTy = Call.getResultType();
907
908 // See if the function has 'rc_ownership_trusted_implementation'
909 // annotate attribute. If it does, we will not inline it.
910 bool hasTrustedImplementationAnnotation = false;
911
912 const LocationContext *LCtx = C.getLocationContext();
913
914 using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
915 std::optional<BehaviorSummary> BSmr =
916 SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
917
918 // See if it's one of the specific functions we know how to eval.
919 if (!BSmr)
920 return false;
921
922 // Bind the return value.
923 if (BSmr == BehaviorSummary::Identity ||
924 BSmr == BehaviorSummary::IdentityOrZero ||
925 BSmr == BehaviorSummary::IdentityThis) {
926
927 const Expr *BindReturnTo =
928 (BSmr == BehaviorSummary::IdentityThis)
929 ? cast<CXXMemberCallExpr>(Val: CE)->getImplicitObjectArgument()
930 : CE->getArg(Arg: 0);
931 SVal RetVal = state->getSVal(Ex: BindReturnTo, LCtx);
932
933 // If the receiver is unknown or the function has
934 // 'rc_ownership_trusted_implementation' annotate attribute, conjure a
935 // return value.
936 // FIXME: this branch is very strange.
937 if (RetVal.isUnknown() ||
938 (hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
939 SValBuilder &SVB = C.getSValBuilder();
940 RetVal = SVB.conjureSymbolVal(call: Call, visitCount: C.blockCount());
941 }
942
943 // Bind the value.
944 state = state->BindExpr(S: CE, LCtx, V: RetVal, /*Invalidate=*/false);
945
946 if (BSmr == BehaviorSummary::IdentityOrZero) {
947 // Add a branch where the output is zero.
948 ProgramStateRef NullOutputState = C.getState();
949
950 // Assume that output is zero on the other branch.
951 NullOutputState = NullOutputState->BindExpr(
952 S: CE, LCtx, V: C.getSValBuilder().makeNullWithType(type: ResultTy),
953 /*Invalidate=*/false);
954 C.addTransition(State: NullOutputState, Tag: &getCastFailTag());
955
956 // And on the original branch assume that both input and
957 // output are non-zero.
958 if (auto L = RetVal.getAs<DefinedOrUnknownSVal>())
959 state = state->assume(Cond: *L, /*assumption=*/Assumption: true);
960
961 }
962 }
963
964 C.addTransition(State: state);
965 return true;
966}
967
968ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
969 CheckerContext &C) const {
970 ExplodedNode *Pred = C.getPredecessor();
971
972 // Only adjust the reference count if this is the top-level call frame,
973 // and not the result of inlining. In the future, we should do
974 // better checking even for inlined calls, and see if they match
975 // with their expected semantics (e.g., the method should return a retained
976 // object, etc.).
977 if (!C.inTopFrame())
978 return Pred;
979
980 if (!S)
981 return Pred;
982
983 const Expr *RetE = S->getRetValue();
984 if (!RetE)
985 return Pred;
986
987 ProgramStateRef state = C.getState();
988 // We need to dig down to the symbolic base here because various
989 // custom allocators do sometimes return the symbol with an offset.
990 SymbolRef Sym = state->getSValAsScalarOrLoc(S: RetE, LCtx: C.getLocationContext())
991 .getAsLocSymbol(/*IncludeBaseRegions=*/true);
992 if (!Sym)
993 return Pred;
994
995 // Get the reference count binding (if any).
996 const RefVal *T = getRefBinding(State: state, Sym);
997 if (!T)
998 return Pred;
999
1000 // Change the reference count.
1001 RefVal X = *T;
1002
1003 switch (X.getKind()) {
1004 case RefVal::Owned: {
1005 unsigned cnt = X.getCount();
1006 assert(cnt > 0);
1007 X.setCount(cnt - 1);
1008 X = X ^ RefVal::ReturnedOwned;
1009 break;
1010 }
1011
1012 case RefVal::NotOwned: {
1013 unsigned cnt = X.getCount();
1014 if (cnt) {
1015 X.setCount(cnt - 1);
1016 X = X ^ RefVal::ReturnedOwned;
1017 } else {
1018 X = X ^ RefVal::ReturnedNotOwned;
1019 }
1020 break;
1021 }
1022
1023 default:
1024 return Pred;
1025 }
1026
1027 // Update the binding.
1028 state = setRefBinding(State: state, Sym, Val: X);
1029 Pred = C.addTransition(State: state);
1030
1031 // At this point we have updated the state properly.
1032 // Everything after this is merely checking to see if the return value has
1033 // been over- or under-retained.
1034
1035 // Did we cache out?
1036 if (!Pred)
1037 return nullptr;
1038
1039 // Update the autorelease counts.
1040 state = handleAutoreleaseCounts(state, Pred, Ctx&: C, Sym, V: X, S);
1041
1042 // Have we generated a sink node?
1043 if (!state)
1044 return nullptr;
1045
1046 // Get the updated binding.
1047 T = getRefBinding(State: state, Sym);
1048 assert(T);
1049 X = *T;
1050
1051 // Consult the summary of the enclosing method.
1052 RetainSummaryManager &Summaries = getSummaryManager(C);
1053 const Decl *CD = &Pred->getCodeDecl();
1054 RetEffect RE = RetEffect::MakeNoRet();
1055
1056 // FIXME: What is the convention for blocks? Is there one?
1057 if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(Val: CD)) {
1058 const RetainSummary *Summ = Summaries.getSummary(C: AnyCall(MD));
1059 RE = Summ->getRetEffect();
1060 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Val: CD)) {
1061 if (!isa<CXXMethodDecl>(Val: FD)) {
1062 const RetainSummary *Summ = Summaries.getSummary(C: AnyCall(FD));
1063 RE = Summ->getRetEffect();
1064 }
1065 }
1066
1067 return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
1068}
1069
1070ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
1071 CheckerContext &C,
1072 ExplodedNode *Pred,
1073 RetEffect RE, RefVal X,
1074 SymbolRef Sym,
1075 ProgramStateRef state) const {
1076 // HACK: Ignore retain-count issues on values accessed through ivars,
1077 // because of cases like this:
1078 // [_contentView retain];
1079 // [_contentView removeFromSuperview];
1080 // [self addSubview:_contentView]; // invalidates 'self'
1081 // [_contentView release];
1082 if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
1083 return Pred;
1084
1085 // Any leaks or other errors?
1086 if (X.isReturnedOwned() && X.getCount() == 0) {
1087 if (RE.getKind() != RetEffect::NoRet) {
1088 if (!RE.isOwned()) {
1089
1090 // The returning type is a CF, we expect the enclosing method should
1091 // return ownership.
1092 X = X ^ RefVal::ErrorLeakReturned;
1093
1094 // Generate an error node.
1095 state = setRefBinding(State: state, Sym, Val: X);
1096
1097 ExplodedNode *N = C.addTransition(State: state, Pred);
1098 if (N) {
1099 const LangOptions &LOpts = C.getASTContext().getLangOpts();
1100 auto R = std::make_unique<RefLeakReport>(
1101 args: getPreferredFrontend().LeakAtReturn, args: LOpts, args&: N, args&: Sym, args&: C);
1102 C.emitReport(R: std::move(R));
1103 }
1104 return N;
1105 }
1106 }
1107 } else if (X.isReturnedNotOwned()) {
1108 if (RE.isOwned()) {
1109 if (X.getIvarAccessHistory() ==
1110 RefVal::IvarAccessHistory::AccessedDirectly) {
1111 // Assume the method was trying to transfer a +1 reference from a
1112 // strong ivar to the caller.
1113 state = setRefBinding(State: state, Sym,
1114 Val: X.releaseViaIvar() ^ RefVal::ReturnedOwned);
1115 } else {
1116 // Trying to return a not owned object to a caller expecting an
1117 // owned object.
1118 state = setRefBinding(State: state, Sym, Val: X ^ RefVal::ErrorReturnedNotOwned);
1119
1120 ExplodedNode *N = C.addTransition(State: state, Pred);
1121 if (N) {
1122 auto R = std::make_unique<RefCountReport>(
1123 args: getPreferredFrontend().ReturnNotOwnedForOwned,
1124 args: C.getASTContext().getLangOpts(), args&: N, args&: Sym);
1125 C.emitReport(R: std::move(R));
1126 }
1127 return N;
1128 }
1129 }
1130 }
1131 return Pred;
1132}
1133
1134//===----------------------------------------------------------------------===//
1135// Check various ways a symbol can be invalidated.
1136//===----------------------------------------------------------------------===//
1137
1138void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
1139 bool AtDeclInit, CheckerContext &C) const {
1140 ProgramStateRef state = C.getState();
1141 const MemRegion *MR = loc.getAsRegion();
1142
1143 // Find all symbols referenced by 'val' that we are tracking
1144 // and stop tracking them.
1145 if (MR && shouldEscapeRegion(State: state, R: MR)) {
1146 state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
1147 C.addTransition(State: state);
1148 }
1149}
1150
1151ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
1152 SVal Cond,
1153 bool Assumption) const {
1154 // FIXME: We may add to the interface of evalAssume the list of symbols
1155 // whose assumptions have changed. For now we just iterate through the
1156 // bindings and check if any of the tracked symbols are NULL. This isn't
1157 // too bad since the number of symbols we will track in practice are
1158 // probably small and evalAssume is only called at branches and a few
1159 // other places.
1160 RefBindingsTy B = state->get<RefBindings>();
1161
1162 if (B.isEmpty())
1163 return state;
1164
1165 bool changed = false;
1166 RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>();
1167 ConstraintManager &CMgr = state->getConstraintManager();
1168
1169 for (auto &I : B) {
1170 // Check if the symbol is null stop tracking the symbol.
1171 ConditionTruthVal AllocFailed = CMgr.isNull(State: state, Sym: I.first);
1172 if (AllocFailed.isConstrainedTrue()) {
1173 changed = true;
1174 B = RefBFactory.remove(Old: B, K: I.first);
1175 }
1176 }
1177
1178 if (changed)
1179 state = state->set<RefBindings>(B);
1180
1181 return state;
1182}
1183
1184ProgramStateRef RetainCountChecker::checkRegionChanges(
1185 ProgramStateRef state, const InvalidatedSymbols *invalidated,
1186 ArrayRef<const MemRegion *> ExplicitRegions,
1187 ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
1188 const CallEvent *Call) const {
1189 if (!invalidated)
1190 return state;
1191
1192 llvm::SmallPtrSet<SymbolRef, 8> AllowedSymbols;
1193
1194 for (const MemRegion *I : ExplicitRegions)
1195 if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>())
1196 AllowedSymbols.insert(Ptr: SR->getSymbol());
1197
1198 for (SymbolRef sym : *invalidated) {
1199 if (AllowedSymbols.count(Ptr: sym))
1200 continue;
1201 // Remove any existing reference-count binding.
1202 state = removeRefBinding(State: state, Sym: sym);
1203 }
1204 return state;
1205}
1206
1207ProgramStateRef RetainCountChecker::handleAutoreleaseCounts(
1208 ProgramStateRef state, ExplodedNode *Pred, CheckerContext &Ctx,
1209 SymbolRef Sym, RefVal V, const ReturnStmt *S) const {
1210 unsigned ACnt = V.getAutoreleaseCount();
1211
1212 // No autorelease counts? Nothing to be done.
1213 if (!ACnt)
1214 return state;
1215
1216 unsigned Cnt = V.getCount();
1217
1218 // FIXME: Handle sending 'autorelease' to already released object.
1219
1220 if (V.getKind() == RefVal::ReturnedOwned)
1221 ++Cnt;
1222
1223 // If we would over-release here, but we know the value came from an ivar,
1224 // assume it was a strong ivar that's just been relinquished.
1225 if (ACnt > Cnt &&
1226 V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) {
1227 V = V.releaseViaIvar();
1228 --ACnt;
1229 }
1230
1231 if (ACnt <= Cnt) {
1232 if (ACnt == Cnt) {
1233 V.clearCounts();
1234 if (V.getKind() == RefVal::ReturnedOwned) {
1235 V = V ^ RefVal::ReturnedNotOwned;
1236 } else {
1237 V = V ^ RefVal::NotOwned;
1238 }
1239 } else {
1240 V.setCount(V.getCount() - ACnt);
1241 V.setAutoreleaseCount(0);
1242 }
1243 return setRefBinding(State: state, Sym, Val: V);
1244 }
1245
1246 // HACK: Ignore retain-count issues on values accessed through ivars,
1247 // because of cases like this:
1248 // [_contentView retain];
1249 // [_contentView removeFromSuperview];
1250 // [self addSubview:_contentView]; // invalidates 'self'
1251 // [_contentView release];
1252 if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
1253 return state;
1254
1255 // Woah! More autorelease counts then retain counts left.
1256 // Emit hard error.
1257 V = V ^ RefVal::ErrorOverAutorelease;
1258 state = setRefBinding(State: state, Sym, Val: V);
1259
1260 ExplodedNode *N = Ctx.generateSink(State: state, Pred);
1261 if (N) {
1262 SmallString<128> sbuf;
1263 llvm::raw_svector_ostream os(sbuf);
1264 os << "Object was autoreleased ";
1265 if (V.getAutoreleaseCount() > 1)
1266 os << V.getAutoreleaseCount() << " times but the object ";
1267 else
1268 os << "but ";
1269 os << "has a +" << V.getCount() << " retain count";
1270
1271 const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
1272 auto R = std::make_unique<RefCountReport>(
1273 args: getPreferredFrontend().OverAutorelease, args: LOpts, args&: N, args&: Sym, args: os.str());
1274 Ctx.emitReport(R: std::move(R));
1275 }
1276
1277 return nullptr;
1278}
1279
1280ProgramStateRef
1281RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
1282 SymbolRef sid, RefVal V,
1283 SmallVectorImpl<SymbolRef> &Leaked) const {
1284 bool hasLeak;
1285
1286 // HACK: Ignore retain-count issues on values accessed through ivars,
1287 // because of cases like this:
1288 // [_contentView retain];
1289 // [_contentView removeFromSuperview];
1290 // [self addSubview:_contentView]; // invalidates 'self'
1291 // [_contentView release];
1292 if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
1293 hasLeak = false;
1294 else if (V.isOwned())
1295 hasLeak = true;
1296 else if (V.isNotOwned() || V.isReturnedOwned())
1297 hasLeak = (V.getCount() > 0);
1298 else
1299 hasLeak = false;
1300
1301 if (!hasLeak)
1302 return removeRefBinding(State: state, Sym: sid);
1303
1304 Leaked.push_back(Elt: sid);
1305 return setRefBinding(State: state, Sym: sid, Val: V ^ RefVal::ErrorLeak);
1306}
1307
1308ExplodedNode *
1309RetainCountChecker::processLeaks(ProgramStateRef state,
1310 SmallVectorImpl<SymbolRef> &Leaked,
1311 CheckerContext &Ctx,
1312 ExplodedNode *Pred) const {
1313 // Generate an intermediate node representing the leak point.
1314 ExplodedNode *N = Ctx.addTransition(State: state, Pred);
1315 const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
1316
1317 if (N) {
1318 const RefCountFrontend &FE = getPreferredFrontend();
1319 const RefCountBug &BT = Pred ? FE.LeakWithinFunction : FE.LeakAtReturn;
1320
1321 for (SymbolRef L : Leaked) {
1322 Ctx.emitReport(R: std::make_unique<RefLeakReport>(args: BT, args: LOpts, args&: N, args&: L, args&: Ctx));
1323 }
1324 }
1325
1326 return N;
1327}
1328
1329void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
1330 if (!Ctx.inTopFrame())
1331 return;
1332
1333 RetainSummaryManager &SmrMgr = getSummaryManager(C&: Ctx);
1334 const LocationContext *LCtx = Ctx.getLocationContext();
1335 const Decl *D = LCtx->getDecl();
1336 std::optional<AnyCall> C = AnyCall::forDecl(D);
1337
1338 if (!C || SmrMgr.isTrustedReferenceCountImplementation(FD: D))
1339 return;
1340
1341 ProgramStateRef state = Ctx.getState();
1342 const RetainSummary *FunctionSummary = SmrMgr.getSummary(C: *C);
1343 ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
1344
1345 for (unsigned idx = 0, e = C->param_size(); idx != e; ++idx) {
1346 const ParmVarDecl *Param = C->parameters()[idx];
1347 SymbolRef Sym = state->getSVal(R: state->getRegion(D: Param, LC: LCtx)).getAsSymbol();
1348
1349 QualType Ty = Param->getType();
1350 const ArgEffect *AE = CalleeSideArgEffects.lookup(K: idx);
1351 if (AE) {
1352 ObjKind K = AE->getObjKind();
1353 if (K == ObjKind::Generalized || K == ObjKind::OS ||
1354 (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) {
1355 RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(o: K, t: Ty)
1356 : RefVal::makeNotOwned(o: K, t: Ty);
1357 state = setRefBinding(State: state, Sym, Val: NewVal);
1358 }
1359 }
1360 }
1361
1362 Ctx.addTransition(State: state);
1363}
1364
1365void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
1366 CheckerContext &Ctx) const {
1367 ExplodedNode *Pred = processReturn(S: RS, C&: Ctx);
1368
1369 // Created state cached out.
1370 if (!Pred) {
1371 return;
1372 }
1373
1374 ProgramStateRef state = Pred->getState();
1375 RefBindingsTy B = state->get<RefBindings>();
1376
1377 // Don't process anything within synthesized bodies.
1378 const LocationContext *LCtx = Pred->getLocationContext();
1379 if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) {
1380 assert(!LCtx->inTopFrame());
1381 return;
1382 }
1383
1384 for (auto &I : B) {
1385 state = handleAutoreleaseCounts(state, Pred, Ctx, Sym: I.first, V: I.second);
1386 if (!state)
1387 return;
1388 }
1389
1390 // If the current LocationContext has a parent, don't check for leaks.
1391 // We will do that later.
1392 // FIXME: we should instead check for imbalances of the retain/releases,
1393 // and suggest annotations.
1394 if (LCtx->getParent())
1395 return;
1396
1397 B = state->get<RefBindings>();
1398 SmallVector<SymbolRef, 10> Leaked;
1399
1400 for (auto &I : B)
1401 state = handleSymbolDeath(state, sid: I.first, V: I.second, Leaked);
1402
1403 processLeaks(state, Leaked, Ctx, Pred);
1404}
1405
1406void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1407 CheckerContext &C) const {
1408 ExplodedNode *Pred = C.getPredecessor();
1409
1410 ProgramStateRef state = C.getState();
1411 SmallVector<SymbolRef, 10> Leaked;
1412
1413 // Update counts from autorelease pools
1414 for (const auto &I: state->get<RefBindings>()) {
1415 SymbolRef Sym = I.first;
1416 if (SymReaper.isDead(sym: Sym)) {
1417 const RefVal &V = I.second;
1418 state = handleAutoreleaseCounts(state, Pred, Ctx&: C, Sym, V);
1419 if (!state)
1420 return;
1421
1422 // Fetch the new reference count from the state, and use it to handle
1423 // this symbol.
1424 state = handleSymbolDeath(state, sid: Sym, V: *getRefBinding(State: state, Sym), Leaked);
1425 }
1426 }
1427
1428 if (Leaked.empty()) {
1429 C.addTransition(State: state);
1430 return;
1431 }
1432
1433 Pred = processLeaks(state, Leaked, Ctx&: C, Pred);
1434
1435 // Did we cache out?
1436 if (!Pred)
1437 return;
1438
1439 // Now generate a new node that nukes the old bindings.
1440 // The only bindings left at this point are the leaked symbols.
1441 RefBindingsTy::Factory &F = state->get_context<RefBindings>();
1442 RefBindingsTy B = state->get<RefBindings>();
1443
1444 for (SymbolRef L : Leaked)
1445 B = F.remove(Old: B, K: L);
1446
1447 state = state->set<RefBindings>(B);
1448 C.addTransition(State: state, Pred);
1449}
1450
1451void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
1452 const char *NL, const char *Sep) const {
1453
1454 RefBindingsTy B = State->get<RefBindings>();
1455
1456 if (B.isEmpty())
1457 return;
1458
1459 Out << Sep << NL;
1460
1461 for (auto &I : B) {
1462 Out << I.first << " : ";
1463 I.second.print(Out);
1464 Out << NL;
1465 }
1466}
1467
1468//===----------------------------------------------------------------------===//
1469// Checker registration.
1470//===----------------------------------------------------------------------===//
1471
1472std::unique_ptr<SimpleProgramPointTag> RetainCountChecker::DeallocSentTag;
1473std::unique_ptr<SimpleProgramPointTag> RetainCountChecker::CastFailTag;
1474
1475void ento::registerRetainCountBase(CheckerManager &Mgr) {
1476 auto *Chk = Mgr.getChecker<RetainCountChecker>();
1477 Chk->DeallocSentTag = std::make_unique<SimpleProgramPointTag>(
1478 args: "RetainCountChecker", args: "DeallocSent");
1479 Chk->CastFailTag = std::make_unique<SimpleProgramPointTag>(
1480 args: "RetainCountChecker", args: "DynamicCastFail");
1481}
1482
1483bool ento::shouldRegisterRetainCountBase(const CheckerManager &) {
1484 return true;
1485}
1486
1487void ento::registerRetainCountChecker(CheckerManager &Mgr) {
1488 auto *Chk = Mgr.getChecker<RetainCountChecker>();
1489 Chk->RetainCount.enable(Mgr);
1490 Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
1491 CheckerName: Mgr.getCurrentCheckerName(), OptionName: "TrackNSCFStartParam");
1492}
1493
1494bool ento::shouldRegisterRetainCountChecker(const CheckerManager &) {
1495 return true;
1496}
1497
1498void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
1499 auto *Chk = Mgr.getChecker<RetainCountChecker>();
1500 Chk->OSObjectRetainCount.enable(Mgr);
1501
1502 // FIXME: We want bug reports to always have the same checker name associated
1503 // with them, yet here, if RetainCountChecker is disabled but
1504 // OSObjectRetainCountChecker is enabled, the checker names will be different.
1505 // This hack will make it so that the checker name depends on which checker is
1506 // enabled rather than on the registration order.
1507 // For the most part, we want **non-hidden checkers** to be associated with
1508 // diagnostics, and **hidden checker options** with the fine-tuning of
1509 // modeling. Following this logic, OSObjectRetainCountChecker should be the
1510 // latter, but we can't just remove it for backward compatibility reasons.
1511}
1512
1513bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &) {
1514 return true;
1515}
1516