1516 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the methods for RetainCountChecker, which implements10// 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>(Sym);30}31 32} // end namespace retaincountchecker33} // end namespace ento34} // end namespace clang35 36static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,37 RefVal Val) {38 assert(Sym != nullptr);39 return State->set<RefBindings>(Sym, Val);40}41 42static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {43 return State->remove<RefBindings>(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, sym);137 return true;138 }139};140} // end anonymous namespace141 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 checker150 // may be tracking.151 if (!BE->getBlockDecl()->hasCaptures())152 return;153 154 ProgramStateRef state = C.getState();155 auto *R = cast<BlockDataRegion>(C.getSVal(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 blocks162 // via captured variables, even though captured variables result in a copy163 // 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(VR->getDecl(), LC);172 }173 Regions.push_back(VR);174 }175 176 state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();177 C.addTransition(state);178}179 180void RetainCountChecker::checkPostStmt(const CastExpr *CE,181 CheckerContext &C) const {182 const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(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(IncRef);202 break;203 case OBC_BridgeTransfer:204 AE = AE.withKind(DecRefBridgedTransferred);205 break;206 }207 208 ProgramStateRef state = C.getState();209 SymbolRef Sym = C.getSVal(CE).getAsLocSymbol();210 if (!Sym)211 return;212 const RefVal* T = getRefBinding(state, Sym);213 if (!T)214 return;215 216 RefVal::Kind hasErr = (RefVal::Kind) 0;217 state = updateSymbol(state, Sym, *T, 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);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(Child);233 if (SymbolRef sym = V.getAsSymbol())234 if (const RefVal* T = getRefBinding(state, sym)) {235 RefVal::Kind hasErr = (RefVal::Kind) 0;236 state = updateSymbol(state, sym, *T,237 ArgEffect(MayEscape, ObjKind::ObjC), hasErr, C);238 if (hasErr) {239 processNonLeakError(state, Child->getSourceRange(), hasErr, 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, pred->getLocationContext()).getAsSymbol()) {249 QualType ResultTy = Ex->getType();250 state = setRefBinding(state, sym,251 RefVal::makeNotOwned(ObjKind::ObjC, ResultTy));252 }253 254 C.addTransition(state);255}256 257void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,258 CheckerContext &C) const {259 // Apply the 'MayEscape' to all values.260 processObjCLiterals(C, 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, 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(Ex).getAsSymbol()) {275 QualType ResultTy = Ex->getType();276 State = setRefBinding(State, Sym,277 RefVal::makeNotOwned(ObjKind::ObjC, 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(IRE).getAs<Loc>();286 if (!IVarLoc)287 return;288 289 ProgramStateRef State = C.getState();290 SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();291 if (!Sym || !isa_and_nonnull<ObjCIvarRegion>(Sym->getOriginRegion()))292 return;293 294 // Accessing an ivar directly is unusual. If we've done that, be more295 // 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(Ty))302 Kind = ObjKind::CF;303 else304 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 because313 // of something the analyzer has synthesized, don't do anything.314 if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None ||315 isSynthesizedAccessor(C.getStackFrame())) {316 return;317 }318 319 // Note that this value has been loaded from an ivar.320 C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess()));321 return;322 }323 324 RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty);325 326 // In a synthesized accessor, the effective retain count is +0.327 if (isSynthesizedAccessor(C.getStackFrame())) {328 C.addTransition(setRefBinding(State, Sym, PlusZero));329 return;330 }331 332 State = setRefBinding(State, Sym, PlusZero.withIvarAccess());333 C.addTransition(State);334}335 336static bool isReceiverUnconsumedSelf(const CallEvent &Call) {337 if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {338 339 // Check if the message is not consumed, we know it will not be used in340 // an assignment, ex: "self = [super init]".341 return MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper() &&342 !Call.getLocationContext()343 ->getAnalysisDeclContext()344 ->getParentMap()345 .isConsumedExpr(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(CE)356 : AnyCall(cast<CXXDestructorDecl>(Call.getDecl()));357 return Summaries.getSummary(C, Call.hasNonZeroCallbackArg(),358 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>(&Call)) {368 if (MC->isInstanceMessage()) {369 SVal ReceiverV = MC->getReceiverSVal();370 if (SymbolRef Sym = ReceiverV.getAsLocSymbol())371 if (const RefVal *T = getRefBinding(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, Call, C);380 return;381 }382 checkSummary(*Summ, Call, C);383}384 385/// GetReturnType - Used to get the return type of a message expression or386/// function call with the intention of affixing that type to a tracked symbol.387/// While the return type can be queried directly from RetEx, when388/// invoking class methods we augment to the return type to be that of389/// 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 something396 /// more specific than id.397 if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(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 is402 // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this403 // is a call to a class method whose type we can resolve. In such404 // 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(Ctx.getObjCInterfaceType(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(RE.getObjKind(), ResultTy);417 } else if (RE.notOwned()) {418 return RefVal::makeNotOwned(RE.getObjKind(), 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(CE.parameters()[ArgIdx]->getType());441}442 443// We don't always get the exact modeling of the function with regards to the444// retain count checker even when the function is inlined. For example, we need445// 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(idx);454 455 if (SymbolRef Sym = V.getAsLocSymbol()) {456 bool ShouldRemoveBinding = Summ.getArg(idx).getKind() == StopTrackingHard;457 if (const RefVal *T = getRefBinding(state, Sym))458 if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T))459 ShouldRemoveBinding = true;460 461 if (ShouldRemoveBinding)462 state = removeRefBinding(state, Sym);463 }464 }465 466 // Evaluate the effect on the message receiver.467 if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {468 if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {469 if (Summ.getReceiverEffect().getKind() == StopTrackingHard) {470 state = removeRefBinding(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, Sym);481 }482 483 C.addTransition(state);484}485 486static bool isSmartPtrField(const MemRegion *MR) {487 const auto *TR = dyn_cast<TypedValueRegion>(488 cast<SubRegion>(MR)->getSuperRegion());489 return TR && RetainSummaryManager::isKnownSmartPointer(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 storage497/// - binding to a variable that has a destructor attached using CleanupAttr498///499/// We do not currently model what happens when a symbol is500/// assigned to a struct field, unless it is a known smart pointer501/// 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(R))505 return false;506 507 const auto *VR = dyn_cast<VarRegion>(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 actually527 // 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 deserves540 // a compiler warning for invalid annotations.541 return {State};542 }543 if (auto DL = L.getAs<DefinedOrUnknownSVal>()) {544 AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true);545 AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false);546 }547 }548 549 for (unsigned idx = 0, e = CE.getNumArgs(); idx != e; ++idx) {550 SVal ArgVal = CE.getArgSVal(idx);551 ArgEffect AE = Summ.getArg(idx);552 553 auto *ArgRegion = dyn_cast_or_null<TypedValueRegion>(ArgVal.getAsRegion());554 if (!ArgRegion)555 continue;556 557 QualType PointeeTy = ArgRegion->getValueType();558 SVal PointeeVal = State->getSVal(ArgRegion);559 SymbolRef Pointee = PointeeVal.getAsLocSymbol();560 if (!Pointee)561 continue;562 563 if (shouldEscapeRegion(State, ArgRegion))564 continue;565 566 auto makeNotOwnedParameter = [&](ProgramStateRef St) {567 return setRefBinding(St, Pointee,568 RefVal::makeNotOwned(AE.getObjKind(), PointeeTy));569 };570 auto makeOwnedParameter = [&](ProgramStateRef St) {571 return setRefBinding(St, Pointee,572 RefVal::makeOwned(ObjKind::OS, 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 sent614 // at this location.615 bool DeallocSent = false;616 617 for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {618 SVal V = CallOrMsg.getArgSVal(idx);619 620 ArgEffect Effect = Summ.getArg(idx);621 if (SymbolRef Sym = V.getAsLocSymbol()) {622 if (const RefVal *T = getRefBinding(state, Sym)) {623 624 if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T))625 Effect = ArgEffect(StopTrackingHard, ObjKind::OS);626 627 state = updateSymbol(state, Sym, *T, Effect, hasErr, C);628 if (hasErr) {629 ErrorRange = CallOrMsg.getArgSourceRange(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>(&CallOrMsg)) {643 if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {644 if (const RefVal *T = getRefBinding(state, Sym)) {645 ReceiverIsTracked = true;646 state = updateSymbol(state, Sym, *T,647 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>(&CallOrMsg)) {657 if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) {658 if (const RefVal *T = getRefBinding(state, Sym)) {659 state = updateSymbol(state, Sym, *T, 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(state, ErrorRange, hasErr, 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 else683 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(Ex, C.getASTContext());692 }693 if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))694 state = setRefBinding(state, Sym, *updatedRefVal);695 }696 697 SmallVector<ProgramStateRef, 2> Out =698 updateOutParameters(state, Summ, CallOrMsg);699 700 for (ProgramStateRef St : Out) {701 if (DeallocSent) {702 C.addTransition(St, C.getPredecessor(), &getDeallocSentTag());703 } else {704 C.addTransition(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(DoNothing);721 break;722 case DecRef:723 AE = AE.withKind(DoNothing);724 break;725 case DecRefAndStopTrackingHard:726 AE = AE.withKind(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, sym, 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, sym, 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, 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 else809 V = V ^ RefVal::Released;810 } else if (AE.getKind() == DecRefAndStopTrackingHard) {811 return removeRefBinding(state, 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, 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 at825 // +1, and we just didn't know.826 if (AE.getKind() == DecRefAndStopTrackingHard)827 return removeRefBinding(state, 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, sym, 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(St, Sym))876 if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)877 return;878 879 ExplodedNode *N = C.generateErrorNode(St);880 if (!N)881 return;882 883 auto report = std::make_unique<RefCountReport>(884 errorKindToBugKind(ErrorKind, Sym), C.getASTContext().getLangOpts(), N,885 Sym, /*isLeak=*/false, isReleaseUnownedError(ErrorKind));886 report->addRange(ErrorRange);887 C.emitReport(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>(Call.getDecl());898 if (!FD)899 return false;900 901 const auto *CE = dyn_cast_or_null<CallExpr>(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>(CE)->getImplicitObjectArgument()930 : CE->getArg(0);931 SVal RetVal = state->getSVal(BindReturnTo, LCtx);932 933 // If the receiver is unknown or the function has934 // 'rc_ownership_trusted_implementation' annotate attribute, conjure a935 // 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, C.blockCount());941 }942 943 // Bind the value.944 state = state->BindExpr(CE, LCtx, 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 CE, LCtx, C.getSValBuilder().makeNullWithType(ResultTy),953 /*Invalidate=*/false);954 C.addTransition(NullOutputState, &getCastFailTag());955 956 // And on the original branch assume that both input and957 // output are non-zero.958 if (auto L = RetVal.getAs<DefinedOrUnknownSVal>())959 state = state->assume(*L, /*assumption=*/true);960 961 }962 }963 964 C.addTransition(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 do974 // better checking even for inlined calls, and see if they match975 // with their expected semantics (e.g., the method should return a retained976 // 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 various989 // custom allocators do sometimes return the symbol with an offset.990 SymbolRef Sym = state->getSValAsScalarOrLoc(RetE, 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, 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, Sym, X);1029 Pred = C.addTransition(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 has1033 // 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, C, Sym, 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, 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>(CD)) {1058 const RetainSummary *Summ = Summaries.getSummary(AnyCall(MD));1059 RE = Summ->getRetEffect();1060 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {1061 if (!isa<CXXMethodDecl>(FD)) {1062 const RetainSummary *Summ = Summaries.getSummary(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 should1091 // return ownership.1092 X = X ^ RefVal::ErrorLeakReturned;1093 1094 // Generate an error node.1095 state = setRefBinding(state, Sym, X);1096 1097 ExplodedNode *N = C.addTransition(state, Pred);1098 if (N) {1099 const LangOptions &LOpts = C.getASTContext().getLangOpts();1100 auto R = std::make_unique<RefLeakReport>(1101 getPreferredFrontend().LeakAtReturn, LOpts, N, Sym, C);1102 C.emitReport(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 a1112 // strong ivar to the caller.1113 state = setRefBinding(state, Sym,1114 X.releaseViaIvar() ^ RefVal::ReturnedOwned);1115 } else {1116 // Trying to return a not owned object to a caller expecting an1117 // owned object.1118 state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);1119 1120 ExplodedNode *N = C.addTransition(state, Pred);1121 if (N) {1122 auto R = std::make_unique<RefCountReport>(1123 getPreferredFrontend().ReturnNotOwnedForOwned,1124 C.getASTContext().getLangOpts(), N, Sym);1125 C.emitReport(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 tracking1144 // and stop tracking them.1145 if (MR && shouldEscapeRegion(state, MR)) {1146 state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();1147 C.addTransition(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 symbols1155 // whose assumptions have changed. For now we just iterate through the1156 // bindings and check if any of the tracked symbols are NULL. This isn't1157 // too bad since the number of symbols we will track in practice are1158 // probably small and evalAssume is only called at branches and a few1159 // 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, I.first);1172 if (AllocFailed.isConstrainedTrue()) {1173 changed = true;1174 B = RefBFactory.remove(B, 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(SR->getSymbol());1197 1198 for (SymbolRef sym : *invalidated) {1199 if (AllowedSymbols.count(sym))1200 continue;1201 // Remove any existing reference-count binding.1202 state = removeRefBinding(state, 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, Sym, 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, Sym, V);1259 1260 ExplodedNode *N = Ctx.generateSink(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 else1268 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 getPreferredFrontend().OverAutorelease, LOpts, N, Sym, os.str());1274 Ctx.emitReport(std::move(R));1275 }1276 1277 return nullptr;1278}1279 1280ProgramStateRef1281RetainCountChecker::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 else1299 hasLeak = false;1300 1301 if (!hasLeak)1302 return removeRefBinding(state, sid);1303 1304 Leaked.push_back(sid);1305 return setRefBinding(state, sid, 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, 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(std::make_unique<RefLeakReport>(BT, LOpts, N, L, 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(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(D))1339 return;1340 1341 ProgramStateRef state = Ctx.getState();1342 const RetainSummary *FunctionSummary = SmrMgr.getSummary(*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(state->getRegion(Param, LCtx)).getAsSymbol();1348 1349 QualType Ty = Param->getType();1350 const ArgEffect *AE = CalleeSideArgEffects.lookup(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(K, Ty)1356 : RefVal::makeNotOwned(K, Ty);1357 state = setRefBinding(state, Sym, NewVal);1358 }1359 }1360 }1361 1362 Ctx.addTransition(state);1363}1364 1365void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,1366 CheckerContext &Ctx) const {1367 ExplodedNode *Pred = processReturn(RS, 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, I.first, 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, I.first, 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 pools1414 for (const auto &I: state->get<RefBindings>()) {1415 SymbolRef Sym = I.first;1416 if (SymReaper.isDead(Sym)) {1417 const RefVal &V = I.second;1418 state = handleAutoreleaseCounts(state, Pred, C, Sym, V);1419 if (!state)1420 return;1421 1422 // Fetch the new reference count from the state, and use it to handle1423 // this symbol.1424 state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked);1425 }1426 }1427 1428 if (Leaked.empty()) {1429 C.addTransition(state);1430 return;1431 }1432 1433 Pred = processLeaks(state, Leaked, 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(B, L);1446 1447 state = state->set<RefBindings>(B);1448 C.addTransition(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 "RetainCountChecker", "DeallocSent");1479 Chk->CastFailTag = std::make_unique<SimpleProgramPointTag>(1480 "RetainCountChecker", "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 Mgr.getCurrentCheckerName(), "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 associated1503 // with them, yet here, if RetainCountChecker is disabled but1504 // OSObjectRetainCountChecker is enabled, the checker names will be different.1505 // This hack will make it so that the checker name depends on which checker is1506 // enabled rather than on the registration order.1507 // For the most part, we want **non-hidden checkers** to be associated with1508 // diagnostics, and **hidden checker options** with the fine-tuning of1509 // modeling. Following this logic, OSObjectRetainCountChecker should be the1510 // latter, but we can't just remove it for backward compatibility reasons.1511}1512 1513bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &) {1514 return true;1515}1516