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

source code of clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp