170c2ee30SGeorge Karpenkov //==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--//
270c2ee30SGeorge Karpenkov //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
670c2ee30SGeorge Karpenkov //
770c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
870c2ee30SGeorge Karpenkov //
970c2ee30SGeorge Karpenkov // This file defines the methods for RetainCountChecker, which implements
1070c2ee30SGeorge Karpenkov // a reference count checker for Core Foundation and Cocoa on (Mac OS X).
1170c2ee30SGeorge Karpenkov //
1270c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
1370c2ee30SGeorge Karpenkov
1470c2ee30SGeorge Karpenkov #include "RetainCountChecker.h"
155192783bSKirstóf Umann #include "clang/StaticAnalyzer/Core/Checker.h"
166fdd2bd5SGeorge Karpenkov #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
1770c2ee30SGeorge Karpenkov
1870c2ee30SGeorge Karpenkov using namespace clang;
1970c2ee30SGeorge Karpenkov using namespace ento;
2070c2ee30SGeorge Karpenkov using namespace retaincountchecker;
2170c2ee30SGeorge Karpenkov
2270c2ee30SGeorge Karpenkov REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal)
2370c2ee30SGeorge Karpenkov
2470c2ee30SGeorge Karpenkov namespace clang {
2570c2ee30SGeorge Karpenkov namespace ento {
2670c2ee30SGeorge Karpenkov namespace retaincountchecker {
2770c2ee30SGeorge Karpenkov
getRefBinding(ProgramStateRef State,SymbolRef Sym)2870c2ee30SGeorge Karpenkov const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) {
2970c2ee30SGeorge Karpenkov return State->get<RefBindings>(Sym);
3070c2ee30SGeorge Karpenkov }
3170c2ee30SGeorge Karpenkov
322c2d0b6eSGeorge Karpenkov } // end namespace retaincountchecker
332c2d0b6eSGeorge Karpenkov } // end namespace ento
342c2d0b6eSGeorge Karpenkov } // end namespace clang
352c2d0b6eSGeorge Karpenkov
setRefBinding(ProgramStateRef State,SymbolRef Sym,RefVal Val)36f153cdfbSGeorge Karpenkov static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
3770c2ee30SGeorge Karpenkov RefVal Val) {
38d5ef0d2aSGeorge Karpenkov assert(Sym != nullptr);
3970c2ee30SGeorge Karpenkov return State->set<RefBindings>(Sym, Val);
4070c2ee30SGeorge Karpenkov }
4170c2ee30SGeorge Karpenkov
removeRefBinding(ProgramStateRef State,SymbolRef Sym)42f153cdfbSGeorge Karpenkov static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
4370c2ee30SGeorge Karpenkov return State->remove<RefBindings>(Sym);
4470c2ee30SGeorge Karpenkov }
4570c2ee30SGeorge Karpenkov
print(raw_ostream & Out) const4670c2ee30SGeorge Karpenkov void RefVal::print(raw_ostream &Out) const {
4770c2ee30SGeorge Karpenkov if (!T.isNull())
48*cfb81690SNathan James Out << "Tracked " << T << " | ";
4970c2ee30SGeorge Karpenkov
5070c2ee30SGeorge Karpenkov switch (getKind()) {
5170c2ee30SGeorge Karpenkov default: llvm_unreachable("Invalid RefVal kind");
5270c2ee30SGeorge Karpenkov case Owned: {
5370c2ee30SGeorge Karpenkov Out << "Owned";
5470c2ee30SGeorge Karpenkov unsigned cnt = getCount();
5570c2ee30SGeorge Karpenkov if (cnt) Out << " (+ " << cnt << ")";
5670c2ee30SGeorge Karpenkov break;
5770c2ee30SGeorge Karpenkov }
5870c2ee30SGeorge Karpenkov
5970c2ee30SGeorge Karpenkov case NotOwned: {
6070c2ee30SGeorge Karpenkov Out << "NotOwned";
6170c2ee30SGeorge Karpenkov unsigned cnt = getCount();
6270c2ee30SGeorge Karpenkov if (cnt) Out << " (+ " << cnt << ")";
6370c2ee30SGeorge Karpenkov break;
6470c2ee30SGeorge Karpenkov }
6570c2ee30SGeorge Karpenkov
6670c2ee30SGeorge Karpenkov case ReturnedOwned: {
6770c2ee30SGeorge Karpenkov Out << "ReturnedOwned";
6870c2ee30SGeorge Karpenkov unsigned cnt = getCount();
6970c2ee30SGeorge Karpenkov if (cnt) Out << " (+ " << cnt << ")";
7070c2ee30SGeorge Karpenkov break;
7170c2ee30SGeorge Karpenkov }
7270c2ee30SGeorge Karpenkov
7370c2ee30SGeorge Karpenkov case ReturnedNotOwned: {
7470c2ee30SGeorge Karpenkov Out << "ReturnedNotOwned";
7570c2ee30SGeorge Karpenkov unsigned cnt = getCount();
7670c2ee30SGeorge Karpenkov if (cnt) Out << " (+ " << cnt << ")";
7770c2ee30SGeorge Karpenkov break;
7870c2ee30SGeorge Karpenkov }
7970c2ee30SGeorge Karpenkov
8070c2ee30SGeorge Karpenkov case Released:
8170c2ee30SGeorge Karpenkov Out << "Released";
8270c2ee30SGeorge Karpenkov break;
8370c2ee30SGeorge Karpenkov
8470c2ee30SGeorge Karpenkov case ErrorDeallocNotOwned:
8570c2ee30SGeorge Karpenkov Out << "-dealloc (not-owned)";
8670c2ee30SGeorge Karpenkov break;
8770c2ee30SGeorge Karpenkov
8870c2ee30SGeorge Karpenkov case ErrorLeak:
8970c2ee30SGeorge Karpenkov Out << "Leaked";
9070c2ee30SGeorge Karpenkov break;
9170c2ee30SGeorge Karpenkov
9270c2ee30SGeorge Karpenkov case ErrorLeakReturned:
9370c2ee30SGeorge Karpenkov Out << "Leaked (Bad naming)";
9470c2ee30SGeorge Karpenkov break;
9570c2ee30SGeorge Karpenkov
9670c2ee30SGeorge Karpenkov case ErrorUseAfterRelease:
9770c2ee30SGeorge Karpenkov Out << "Use-After-Release [ERROR]";
9870c2ee30SGeorge Karpenkov break;
9970c2ee30SGeorge Karpenkov
10070c2ee30SGeorge Karpenkov case ErrorReleaseNotOwned:
10170c2ee30SGeorge Karpenkov Out << "Release of Not-Owned [ERROR]";
10270c2ee30SGeorge Karpenkov break;
10370c2ee30SGeorge Karpenkov
10470c2ee30SGeorge Karpenkov case RefVal::ErrorOverAutorelease:
10570c2ee30SGeorge Karpenkov Out << "Over-autoreleased";
10670c2ee30SGeorge Karpenkov break;
10770c2ee30SGeorge Karpenkov
10870c2ee30SGeorge Karpenkov case RefVal::ErrorReturnedNotOwned:
10970c2ee30SGeorge Karpenkov Out << "Non-owned object returned instead of owned";
11070c2ee30SGeorge Karpenkov break;
11170c2ee30SGeorge Karpenkov }
11270c2ee30SGeorge Karpenkov
11370c2ee30SGeorge Karpenkov switch (getIvarAccessHistory()) {
11470c2ee30SGeorge Karpenkov case IvarAccessHistory::None:
11570c2ee30SGeorge Karpenkov break;
11670c2ee30SGeorge Karpenkov case IvarAccessHistory::AccessedDirectly:
11770c2ee30SGeorge Karpenkov Out << " [direct ivar access]";
11870c2ee30SGeorge Karpenkov break;
11970c2ee30SGeorge Karpenkov case IvarAccessHistory::ReleasedAfterDirectAccess:
12070c2ee30SGeorge Karpenkov Out << " [released after direct ivar access]";
12170c2ee30SGeorge Karpenkov }
12270c2ee30SGeorge Karpenkov
12370c2ee30SGeorge Karpenkov if (ACnt) {
12470c2ee30SGeorge Karpenkov Out << " [autorelease -" << ACnt << ']';
12570c2ee30SGeorge Karpenkov }
12670c2ee30SGeorge Karpenkov }
12770c2ee30SGeorge Karpenkov
12870c2ee30SGeorge Karpenkov namespace {
12970c2ee30SGeorge Karpenkov class StopTrackingCallback final : public SymbolVisitor {
13070c2ee30SGeorge Karpenkov ProgramStateRef state;
13170c2ee30SGeorge Karpenkov public:
StopTrackingCallback(ProgramStateRef st)13270c2ee30SGeorge Karpenkov StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
getState() const13370c2ee30SGeorge Karpenkov ProgramStateRef getState() const { return state; }
13470c2ee30SGeorge Karpenkov
VisitSymbol(SymbolRef sym)13570c2ee30SGeorge Karpenkov bool VisitSymbol(SymbolRef sym) override {
136f153cdfbSGeorge Karpenkov state = removeRefBinding(state, sym);
13770c2ee30SGeorge Karpenkov return true;
13870c2ee30SGeorge Karpenkov }
13970c2ee30SGeorge Karpenkov };
14070c2ee30SGeorge Karpenkov } // end anonymous namespace
14170c2ee30SGeorge Karpenkov
14270c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
14370c2ee30SGeorge Karpenkov // Handle statements that may have an effect on refcounts.
14470c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
14570c2ee30SGeorge Karpenkov
checkPostStmt(const BlockExpr * BE,CheckerContext & C) const14670c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
14770c2ee30SGeorge Karpenkov CheckerContext &C) const {
14870c2ee30SGeorge Karpenkov
14970c2ee30SGeorge Karpenkov // Scan the BlockDecRefExprs for any object the retain count checker
15070c2ee30SGeorge Karpenkov // may be tracking.
15170c2ee30SGeorge Karpenkov if (!BE->getBlockDecl()->hasCaptures())
15270c2ee30SGeorge Karpenkov return;
15370c2ee30SGeorge Karpenkov
15470c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
15570c2ee30SGeorge Karpenkov auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
15670c2ee30SGeorge Karpenkov
15770c2ee30SGeorge Karpenkov BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
15870c2ee30SGeorge Karpenkov E = R->referenced_vars_end();
15970c2ee30SGeorge Karpenkov
16070c2ee30SGeorge Karpenkov if (I == E)
16170c2ee30SGeorge Karpenkov return;
16270c2ee30SGeorge Karpenkov
16370c2ee30SGeorge Karpenkov // FIXME: For now we invalidate the tracking of all symbols passed to blocks
16470c2ee30SGeorge Karpenkov // via captured variables, even though captured variables result in a copy
16570c2ee30SGeorge Karpenkov // and in implicit increment/decrement of a retain count.
16670c2ee30SGeorge Karpenkov SmallVector<const MemRegion*, 10> Regions;
16770c2ee30SGeorge Karpenkov const LocationContext *LC = C.getLocationContext();
16870c2ee30SGeorge Karpenkov MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
16970c2ee30SGeorge Karpenkov
17070c2ee30SGeorge Karpenkov for ( ; I != E; ++I) {
17170c2ee30SGeorge Karpenkov const VarRegion *VR = I.getCapturedRegion();
17270c2ee30SGeorge Karpenkov if (VR->getSuperRegion() == R) {
17370c2ee30SGeorge Karpenkov VR = MemMgr.getVarRegion(VR->getDecl(), LC);
17470c2ee30SGeorge Karpenkov }
17570c2ee30SGeorge Karpenkov Regions.push_back(VR);
17670c2ee30SGeorge Karpenkov }
17770c2ee30SGeorge Karpenkov
178d3e76753SGeorge Karpenkov state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
17970c2ee30SGeorge Karpenkov C.addTransition(state);
18070c2ee30SGeorge Karpenkov }
18170c2ee30SGeorge Karpenkov
checkPostStmt(const CastExpr * CE,CheckerContext & C) const18270c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostStmt(const CastExpr *CE,
18370c2ee30SGeorge Karpenkov CheckerContext &C) const {
18470c2ee30SGeorge Karpenkov const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
18570c2ee30SGeorge Karpenkov if (!BE)
18670c2ee30SGeorge Karpenkov return;
18770c2ee30SGeorge Karpenkov
188b6c6ab31SGeorge Karpenkov QualType QT = CE->getType();
189b6c6ab31SGeorge Karpenkov ObjKind K;
19077b35308SGeorge Karpenkov if (QT->isObjCObjectPointerType()) {
191b6c6ab31SGeorge Karpenkov K = ObjKind::ObjC;
19277b35308SGeorge Karpenkov } else {
19377b35308SGeorge Karpenkov K = ObjKind::CF;
194b6c6ab31SGeorge Karpenkov }
195b6c6ab31SGeorge Karpenkov
196b6c6ab31SGeorge Karpenkov ArgEffect AE = ArgEffect(IncRef, K);
19770c2ee30SGeorge Karpenkov
19870c2ee30SGeorge Karpenkov switch (BE->getBridgeKind()) {
19970c2ee30SGeorge Karpenkov case OBC_Bridge:
20070c2ee30SGeorge Karpenkov // Do nothing.
20170c2ee30SGeorge Karpenkov return;
20270c2ee30SGeorge Karpenkov case OBC_BridgeRetained:
2039cbcc21aSGeorge Karpenkov AE = AE.withKind(IncRef);
20470c2ee30SGeorge Karpenkov break;
20570c2ee30SGeorge Karpenkov case OBC_BridgeTransfer:
2069cbcc21aSGeorge Karpenkov AE = AE.withKind(DecRefBridgedTransferred);
20770c2ee30SGeorge Karpenkov break;
20870c2ee30SGeorge Karpenkov }
20970c2ee30SGeorge Karpenkov
21070c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
21170c2ee30SGeorge Karpenkov SymbolRef Sym = C.getSVal(CE).getAsLocSymbol();
21270c2ee30SGeorge Karpenkov if (!Sym)
21370c2ee30SGeorge Karpenkov return;
21470c2ee30SGeorge Karpenkov const RefVal* T = getRefBinding(state, Sym);
21570c2ee30SGeorge Karpenkov if (!T)
21670c2ee30SGeorge Karpenkov return;
21770c2ee30SGeorge Karpenkov
21870c2ee30SGeorge Karpenkov RefVal::Kind hasErr = (RefVal::Kind) 0;
21970c2ee30SGeorge Karpenkov state = updateSymbol(state, Sym, *T, AE, hasErr, C);
22070c2ee30SGeorge Karpenkov
22170c2ee30SGeorge Karpenkov if (hasErr) {
22270c2ee30SGeorge Karpenkov // FIXME: If we get an error during a bridge cast, should we report it?
22370c2ee30SGeorge Karpenkov return;
22470c2ee30SGeorge Karpenkov }
22570c2ee30SGeorge Karpenkov
22670c2ee30SGeorge Karpenkov C.addTransition(state);
22770c2ee30SGeorge Karpenkov }
22870c2ee30SGeorge Karpenkov
processObjCLiterals(CheckerContext & C,const Expr * Ex) const22970c2ee30SGeorge Karpenkov void RetainCountChecker::processObjCLiterals(CheckerContext &C,
23070c2ee30SGeorge Karpenkov const Expr *Ex) const {
23170c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
23270c2ee30SGeorge Karpenkov const ExplodedNode *pred = C.getPredecessor();
23370c2ee30SGeorge Karpenkov for (const Stmt *Child : Ex->children()) {
23470c2ee30SGeorge Karpenkov SVal V = pred->getSVal(Child);
23570c2ee30SGeorge Karpenkov if (SymbolRef sym = V.getAsSymbol())
23670c2ee30SGeorge Karpenkov if (const RefVal* T = getRefBinding(state, sym)) {
23770c2ee30SGeorge Karpenkov RefVal::Kind hasErr = (RefVal::Kind) 0;
2389cbcc21aSGeorge Karpenkov state = updateSymbol(state, sym, *T,
2399cbcc21aSGeorge Karpenkov ArgEffect(MayEscape, ObjKind::ObjC), hasErr, C);
24070c2ee30SGeorge Karpenkov if (hasErr) {
24170c2ee30SGeorge Karpenkov processNonLeakError(state, Child->getSourceRange(), hasErr, sym, C);
24270c2ee30SGeorge Karpenkov return;
24370c2ee30SGeorge Karpenkov }
24470c2ee30SGeorge Karpenkov }
24570c2ee30SGeorge Karpenkov }
24670c2ee30SGeorge Karpenkov
24770c2ee30SGeorge Karpenkov // Return the object as autoreleased.
2487e3016deSGeorge Karpenkov // RetEffect RE = RetEffect::MakeNotOwned(ObjKind::ObjC);
24970c2ee30SGeorge Karpenkov if (SymbolRef sym =
25070c2ee30SGeorge Karpenkov state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
25170c2ee30SGeorge Karpenkov QualType ResultTy = Ex->getType();
25270c2ee30SGeorge Karpenkov state = setRefBinding(state, sym,
2537e3016deSGeorge Karpenkov RefVal::makeNotOwned(ObjKind::ObjC, ResultTy));
25470c2ee30SGeorge Karpenkov }
25570c2ee30SGeorge Karpenkov
25670c2ee30SGeorge Karpenkov C.addTransition(state);
25770c2ee30SGeorge Karpenkov }
25870c2ee30SGeorge Karpenkov
checkPostStmt(const ObjCArrayLiteral * AL,CheckerContext & C) const25970c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
26070c2ee30SGeorge Karpenkov CheckerContext &C) const {
26170c2ee30SGeorge Karpenkov // Apply the 'MayEscape' to all values.
26270c2ee30SGeorge Karpenkov processObjCLiterals(C, AL);
26370c2ee30SGeorge Karpenkov }
26470c2ee30SGeorge Karpenkov
checkPostStmt(const ObjCDictionaryLiteral * DL,CheckerContext & C) const26570c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
26670c2ee30SGeorge Karpenkov CheckerContext &C) const {
26770c2ee30SGeorge Karpenkov // Apply the 'MayEscape' to all keys and values.
26870c2ee30SGeorge Karpenkov processObjCLiterals(C, DL);
26970c2ee30SGeorge Karpenkov }
27070c2ee30SGeorge Karpenkov
checkPostStmt(const ObjCBoxedExpr * Ex,CheckerContext & C) const27170c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
27270c2ee30SGeorge Karpenkov CheckerContext &C) const {
27370c2ee30SGeorge Karpenkov const ExplodedNode *Pred = C.getPredecessor();
27470c2ee30SGeorge Karpenkov ProgramStateRef State = Pred->getState();
27570c2ee30SGeorge Karpenkov
27670c2ee30SGeorge Karpenkov if (SymbolRef Sym = Pred->getSVal(Ex).getAsSymbol()) {
27770c2ee30SGeorge Karpenkov QualType ResultTy = Ex->getType();
27870c2ee30SGeorge Karpenkov State = setRefBinding(State, Sym,
2797e3016deSGeorge Karpenkov RefVal::makeNotOwned(ObjKind::ObjC, ResultTy));
28070c2ee30SGeorge Karpenkov }
28170c2ee30SGeorge Karpenkov
28270c2ee30SGeorge Karpenkov C.addTransition(State);
28370c2ee30SGeorge Karpenkov }
28470c2ee30SGeorge Karpenkov
checkPostStmt(const ObjCIvarRefExpr * IRE,CheckerContext & C) const28570c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
28670c2ee30SGeorge Karpenkov CheckerContext &C) const {
28770c2ee30SGeorge Karpenkov Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
28870c2ee30SGeorge Karpenkov if (!IVarLoc)
28970c2ee30SGeorge Karpenkov return;
29070c2ee30SGeorge Karpenkov
29170c2ee30SGeorge Karpenkov ProgramStateRef State = C.getState();
29270c2ee30SGeorge Karpenkov SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();
293d0ac215dSKazu Hirata if (!Sym || !isa_and_nonnull<ObjCIvarRegion>(Sym->getOriginRegion()))
29470c2ee30SGeorge Karpenkov return;
29570c2ee30SGeorge Karpenkov
29670c2ee30SGeorge Karpenkov // Accessing an ivar directly is unusual. If we've done that, be more
29770c2ee30SGeorge Karpenkov // forgiving about what the surrounding code is allowed to do.
29870c2ee30SGeorge Karpenkov
29970c2ee30SGeorge Karpenkov QualType Ty = Sym->getType();
3007e3016deSGeorge Karpenkov ObjKind Kind;
30170c2ee30SGeorge Karpenkov if (Ty->isObjCRetainableType())
3027e3016deSGeorge Karpenkov Kind = ObjKind::ObjC;
30370c2ee30SGeorge Karpenkov else if (coreFoundation::isCFObjectRef(Ty))
3047e3016deSGeorge Karpenkov Kind = ObjKind::CF;
30570c2ee30SGeorge Karpenkov else
30670c2ee30SGeorge Karpenkov return;
30770c2ee30SGeorge Karpenkov
30870c2ee30SGeorge Karpenkov // If the value is already known to be nil, don't bother tracking it.
30970c2ee30SGeorge Karpenkov ConstraintManager &CMgr = State->getConstraintManager();
31070c2ee30SGeorge Karpenkov if (CMgr.isNull(State, Sym).isConstrainedTrue())
31170c2ee30SGeorge Karpenkov return;
31270c2ee30SGeorge Karpenkov
31370c2ee30SGeorge Karpenkov if (const RefVal *RV = getRefBinding(State, Sym)) {
31470c2ee30SGeorge Karpenkov // If we've seen this symbol before, or we're only seeing it now because
31570c2ee30SGeorge Karpenkov // of something the analyzer has synthesized, don't do anything.
31670c2ee30SGeorge Karpenkov if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None ||
31770c2ee30SGeorge Karpenkov isSynthesizedAccessor(C.getStackFrame())) {
31870c2ee30SGeorge Karpenkov return;
31970c2ee30SGeorge Karpenkov }
32070c2ee30SGeorge Karpenkov
32170c2ee30SGeorge Karpenkov // Note that this value has been loaded from an ivar.
32270c2ee30SGeorge Karpenkov C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess()));
32370c2ee30SGeorge Karpenkov return;
32470c2ee30SGeorge Karpenkov }
32570c2ee30SGeorge Karpenkov
32670c2ee30SGeorge Karpenkov RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty);
32770c2ee30SGeorge Karpenkov
32870c2ee30SGeorge Karpenkov // In a synthesized accessor, the effective retain count is +0.
32970c2ee30SGeorge Karpenkov if (isSynthesizedAccessor(C.getStackFrame())) {
33070c2ee30SGeorge Karpenkov C.addTransition(setRefBinding(State, Sym, PlusZero));
33170c2ee30SGeorge Karpenkov return;
33270c2ee30SGeorge Karpenkov }
33370c2ee30SGeorge Karpenkov
33470c2ee30SGeorge Karpenkov State = setRefBinding(State, Sym, PlusZero.withIvarAccess());
33570c2ee30SGeorge Karpenkov C.addTransition(State);
33670c2ee30SGeorge Karpenkov }
33770c2ee30SGeorge Karpenkov
isReceiverUnconsumedSelf(const CallEvent & Call)3386fdd2bd5SGeorge Karpenkov static bool isReceiverUnconsumedSelf(const CallEvent &Call) {
3396fdd2bd5SGeorge Karpenkov if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {
3406fdd2bd5SGeorge Karpenkov
3416fdd2bd5SGeorge Karpenkov // Check if the message is not consumed, we know it will not be used in
3426fdd2bd5SGeorge Karpenkov // an assignment, ex: "self = [super init]".
3436fdd2bd5SGeorge Karpenkov return MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper() &&
3446fdd2bd5SGeorge Karpenkov !Call.getLocationContext()
3456fdd2bd5SGeorge Karpenkov ->getAnalysisDeclContext()
3466fdd2bd5SGeorge Karpenkov ->getParentMap()
3476fdd2bd5SGeorge Karpenkov .isConsumedExpr(Call.getOriginExpr());
3486fdd2bd5SGeorge Karpenkov }
3496fdd2bd5SGeorge Karpenkov return false;
3506fdd2bd5SGeorge Karpenkov }
3516fdd2bd5SGeorge Karpenkov
getSummary(RetainSummaryManager & Summaries,const CallEvent & Call,QualType ReceiverType)3526fdd2bd5SGeorge Karpenkov const static RetainSummary *getSummary(RetainSummaryManager &Summaries,
3536fdd2bd5SGeorge Karpenkov const CallEvent &Call,
3546fdd2bd5SGeorge Karpenkov QualType ReceiverType) {
3556fdd2bd5SGeorge Karpenkov const Expr *CE = Call.getOriginExpr();
3566fdd2bd5SGeorge Karpenkov AnyCall C =
3576fdd2bd5SGeorge Karpenkov CE ? *AnyCall::forExpr(CE)
35838bc347fSGeorge Karpenkov : AnyCall(cast<CXXDestructorDecl>(Call.getDecl()));
3596fdd2bd5SGeorge Karpenkov return Summaries.getSummary(C, Call.hasNonZeroCallbackArg(),
3606fdd2bd5SGeorge Karpenkov isReceiverUnconsumedSelf(Call), ReceiverType);
3616fdd2bd5SGeorge Karpenkov }
3626fdd2bd5SGeorge Karpenkov
checkPostCall(const CallEvent & Call,CheckerContext & C) const36370c2ee30SGeorge Karpenkov void RetainCountChecker::checkPostCall(const CallEvent &Call,
36470c2ee30SGeorge Karpenkov CheckerContext &C) const {
36570c2ee30SGeorge Karpenkov RetainSummaryManager &Summaries = getSummaryManager(C);
366efef49cdSGeorge Karpenkov
367efef49cdSGeorge Karpenkov // Leave null if no receiver.
368efef49cdSGeorge Karpenkov QualType ReceiverType;
369efef49cdSGeorge Karpenkov if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {
370efef49cdSGeorge Karpenkov if (MC->isInstanceMessage()) {
371efef49cdSGeorge Karpenkov SVal ReceiverV = MC->getReceiverSVal();
372efef49cdSGeorge Karpenkov if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
373efef49cdSGeorge Karpenkov if (const RefVal *T = getRefBinding(C.getState(), Sym))
374efef49cdSGeorge Karpenkov ReceiverType = T->getType();
375efef49cdSGeorge Karpenkov }
376efef49cdSGeorge Karpenkov }
377efef49cdSGeorge Karpenkov
3786fdd2bd5SGeorge Karpenkov const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType);
37970c2ee30SGeorge Karpenkov
38070c2ee30SGeorge Karpenkov if (C.wasInlined) {
38170c2ee30SGeorge Karpenkov processSummaryOfInlined(*Summ, Call, C);
38270c2ee30SGeorge Karpenkov return;
38370c2ee30SGeorge Karpenkov }
38470c2ee30SGeorge Karpenkov checkSummary(*Summ, Call, C);
38570c2ee30SGeorge Karpenkov }
38670c2ee30SGeorge Karpenkov
38770c2ee30SGeorge Karpenkov /// GetReturnType - Used to get the return type of a message expression or
38870c2ee30SGeorge Karpenkov /// function call with the intention of affixing that type to a tracked symbol.
38970c2ee30SGeorge Karpenkov /// While the return type can be queried directly from RetEx, when
39070c2ee30SGeorge Karpenkov /// invoking class methods we augment to the return type to be that of
39170c2ee30SGeorge Karpenkov /// a pointer to the class (as opposed it just being id).
39270c2ee30SGeorge Karpenkov // FIXME: We may be able to do this with related result types instead.
39370c2ee30SGeorge Karpenkov // This function is probably overestimating.
GetReturnType(const Expr * RetE,ASTContext & Ctx)39470c2ee30SGeorge Karpenkov static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
39570c2ee30SGeorge Karpenkov QualType RetTy = RetE->getType();
39670c2ee30SGeorge Karpenkov // If RetE is not a message expression just return its type.
39770c2ee30SGeorge Karpenkov // If RetE is a message expression, return its types if it is something
39870c2ee30SGeorge Karpenkov /// more specific than id.
39970c2ee30SGeorge Karpenkov if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE))
40070c2ee30SGeorge Karpenkov if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>())
40170c2ee30SGeorge Karpenkov if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() ||
40270c2ee30SGeorge Karpenkov PT->isObjCClassType()) {
40370c2ee30SGeorge Karpenkov // At this point we know the return type of the message expression is
40470c2ee30SGeorge Karpenkov // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this
40570c2ee30SGeorge Karpenkov // is a call to a class method whose type we can resolve. In such
40670c2ee30SGeorge Karpenkov // cases, promote the return type to XXX* (where XXX is the class).
40770c2ee30SGeorge Karpenkov const ObjCInterfaceDecl *D = ME->getReceiverInterface();
40870c2ee30SGeorge Karpenkov return !D ? RetTy :
40970c2ee30SGeorge Karpenkov Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D));
41070c2ee30SGeorge Karpenkov }
41170c2ee30SGeorge Karpenkov
41270c2ee30SGeorge Karpenkov return RetTy;
41370c2ee30SGeorge Karpenkov }
41470c2ee30SGeorge Karpenkov
refValFromRetEffect(RetEffect RE,QualType ResultTy)41580c9e78eSGeorge Karpenkov static Optional<RefVal> refValFromRetEffect(RetEffect RE,
41680c9e78eSGeorge Karpenkov QualType ResultTy) {
41780c9e78eSGeorge Karpenkov if (RE.isOwned()) {
41880c9e78eSGeorge Karpenkov return RefVal::makeOwned(RE.getObjKind(), ResultTy);
41980c9e78eSGeorge Karpenkov } else if (RE.notOwned()) {
42080c9e78eSGeorge Karpenkov return RefVal::makeNotOwned(RE.getObjKind(), ResultTy);
42180c9e78eSGeorge Karpenkov }
42280c9e78eSGeorge Karpenkov
42380c9e78eSGeorge Karpenkov return None;
42480c9e78eSGeorge Karpenkov }
42580c9e78eSGeorge Karpenkov
isPointerToObject(QualType QT)426255b0582SGeorge Karpenkov static bool isPointerToObject(QualType QT) {
427255b0582SGeorge Karpenkov QualType PT = QT->getPointeeType();
428255b0582SGeorge Karpenkov if (!PT.isNull())
429255b0582SGeorge Karpenkov if (PT->getAsCXXRecordDecl())
430255b0582SGeorge Karpenkov return true;
431255b0582SGeorge Karpenkov return false;
432255b0582SGeorge Karpenkov }
433255b0582SGeorge Karpenkov
434255b0582SGeorge Karpenkov /// Whether the tracked value should be escaped on a given call.
435255b0582SGeorge Karpenkov /// OSObjects are escaped when passed to void * / etc.
shouldEscapeOSArgumentOnCall(const CallEvent & CE,unsigned ArgIdx,const RefVal * TrackedValue)4369cbcc21aSGeorge Karpenkov static bool shouldEscapeOSArgumentOnCall(const CallEvent &CE, unsigned ArgIdx,
437255b0582SGeorge Karpenkov const RefVal *TrackedValue) {
4387e3016deSGeorge Karpenkov if (TrackedValue->getObjKind() != ObjKind::OS)
439255b0582SGeorge Karpenkov return false;
440255b0582SGeorge Karpenkov if (ArgIdx >= CE.parameters().size())
441255b0582SGeorge Karpenkov return false;
442255b0582SGeorge Karpenkov return !isPointerToObject(CE.parameters()[ArgIdx]->getType());
443255b0582SGeorge Karpenkov }
444255b0582SGeorge Karpenkov
44570c2ee30SGeorge Karpenkov // We don't always get the exact modeling of the function with regards to the
44670c2ee30SGeorge Karpenkov // retain count checker even when the function is inlined. For example, we need
44770c2ee30SGeorge Karpenkov // to stop tracking the symbols which were marked with StopTrackingHard.
processSummaryOfInlined(const RetainSummary & Summ,const CallEvent & CallOrMsg,CheckerContext & C) const44870c2ee30SGeorge Karpenkov void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
44970c2ee30SGeorge Karpenkov const CallEvent &CallOrMsg,
45070c2ee30SGeorge Karpenkov CheckerContext &C) const {
45170c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
45270c2ee30SGeorge Karpenkov
45370c2ee30SGeorge Karpenkov // Evaluate the effect of the arguments.
45470c2ee30SGeorge Karpenkov for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
45570c2ee30SGeorge Karpenkov SVal V = CallOrMsg.getArgSVal(idx);
456255b0582SGeorge Karpenkov
45770c2ee30SGeorge Karpenkov if (SymbolRef Sym = V.getAsLocSymbol()) {
458585a210eSGeorge Karpenkov bool ShouldRemoveBinding = Summ.getArg(idx).getKind() == StopTrackingHard;
459255b0582SGeorge Karpenkov if (const RefVal *T = getRefBinding(state, Sym))
4609cbcc21aSGeorge Karpenkov if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T))
461255b0582SGeorge Karpenkov ShouldRemoveBinding = true;
462255b0582SGeorge Karpenkov
463255b0582SGeorge Karpenkov if (ShouldRemoveBinding)
46470c2ee30SGeorge Karpenkov state = removeRefBinding(state, Sym);
46570c2ee30SGeorge Karpenkov }
46670c2ee30SGeorge Karpenkov }
46770c2ee30SGeorge Karpenkov
46870c2ee30SGeorge Karpenkov // Evaluate the effect on the message receiver.
4696e9fd137SGeorge Karpenkov if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
47070c2ee30SGeorge Karpenkov if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
471585a210eSGeorge Karpenkov if (Summ.getReceiverEffect().getKind() == StopTrackingHard) {
47270c2ee30SGeorge Karpenkov state = removeRefBinding(state, Sym);
47370c2ee30SGeorge Karpenkov }
47470c2ee30SGeorge Karpenkov }
47570c2ee30SGeorge Karpenkov }
47670c2ee30SGeorge Karpenkov
47770c2ee30SGeorge Karpenkov // Consult the summary for the return value.
478ab0011ebSGeorge Karpenkov RetEffect RE = Summ.getRetEffect();
479d5ef0d2aSGeorge Karpenkov
480d5ef0d2aSGeorge Karpenkov if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
481d5ef0d2aSGeorge Karpenkov if (RE.getKind() == RetEffect::NoRetHard)
482ab0011ebSGeorge Karpenkov state = removeRefBinding(state, Sym);
483d5ef0d2aSGeorge Karpenkov }
48470c2ee30SGeorge Karpenkov
48570c2ee30SGeorge Karpenkov C.addTransition(state);
48670c2ee30SGeorge Karpenkov }
48770c2ee30SGeorge Karpenkov
isSmartPtrField(const MemRegion * MR)4882c2d0b6eSGeorge Karpenkov static bool isSmartPtrField(const MemRegion *MR) {
4892c2d0b6eSGeorge Karpenkov const auto *TR = dyn_cast<TypedValueRegion>(
4902c2d0b6eSGeorge Karpenkov cast<SubRegion>(MR)->getSuperRegion());
4912c2d0b6eSGeorge Karpenkov return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType());
4922c2d0b6eSGeorge Karpenkov }
4932c2d0b6eSGeorge Karpenkov
4942c2d0b6eSGeorge Karpenkov
49503391514SGeorge Karpenkov /// A value escapes in these possible cases:
49603391514SGeorge Karpenkov ///
49703391514SGeorge Karpenkov /// - binding to something that is not a memory region.
49803391514SGeorge Karpenkov /// - binding to a memregion that does not have stack storage
49903391514SGeorge Karpenkov /// - binding to a variable that has a destructor attached using CleanupAttr
50003391514SGeorge Karpenkov ///
50103391514SGeorge Karpenkov /// We do not currently model what happens when a symbol is
5022c2d0b6eSGeorge Karpenkov /// assigned to a struct field, unless it is a known smart pointer
5032c2d0b6eSGeorge Karpenkov /// implementation, about which we know that it is inlined.
50403391514SGeorge Karpenkov /// FIXME: This could definitely be improved upon.
shouldEscapeRegion(const MemRegion * R)5055be959c8SGeorge Karpenkov static bool shouldEscapeRegion(const MemRegion *R) {
5062c2d0b6eSGeorge Karpenkov if (isSmartPtrField(R))
5072c2d0b6eSGeorge Karpenkov return false;
5082c2d0b6eSGeorge Karpenkov
50903391514SGeorge Karpenkov const auto *VR = dyn_cast<VarRegion>(R);
5102c2d0b6eSGeorge Karpenkov
51103391514SGeorge Karpenkov if (!R->hasStackStorage() || !VR)
51203391514SGeorge Karpenkov return true;
5135be959c8SGeorge Karpenkov
51403391514SGeorge Karpenkov const VarDecl *VD = VR->getDecl();
51503391514SGeorge Karpenkov if (!VD->hasAttr<CleanupAttr>())
51603391514SGeorge Karpenkov return false; // CleanupAttr attaches destructors, which cause escaping.
51703391514SGeorge Karpenkov return true;
5185be959c8SGeorge Karpenkov }
5195be959c8SGeorge Karpenkov
5205be959c8SGeorge Karpenkov static SmallVector<ProgramStateRef, 2>
updateOutParameters(ProgramStateRef State,const RetainSummary & Summ,const CallEvent & CE)5215be959c8SGeorge Karpenkov updateOutParameters(ProgramStateRef State, const RetainSummary &Summ,
5225be959c8SGeorge Karpenkov const CallEvent &CE) {
5235be959c8SGeorge Karpenkov
5245be959c8SGeorge Karpenkov SVal L = CE.getReturnValue();
5255be959c8SGeorge Karpenkov
5265be959c8SGeorge Karpenkov // Splitting is required to support out parameters,
5275be959c8SGeorge Karpenkov // as out parameters might be created only on the "success" branch.
5285be959c8SGeorge Karpenkov // We want to avoid eagerly splitting unless out parameters are actually
5295be959c8SGeorge Karpenkov // needed.
5305be959c8SGeorge Karpenkov bool SplitNecessary = false;
5315be959c8SGeorge Karpenkov for (auto &P : Summ.getArgEffects())
5325be959c8SGeorge Karpenkov if (P.second.getKind() == RetainedOutParameterOnNonZero ||
5335be959c8SGeorge Karpenkov P.second.getKind() == RetainedOutParameterOnZero)
5345be959c8SGeorge Karpenkov SplitNecessary = true;
5355be959c8SGeorge Karpenkov
5365be959c8SGeorge Karpenkov ProgramStateRef AssumeNonZeroReturn = State;
5375be959c8SGeorge Karpenkov ProgramStateRef AssumeZeroReturn = State;
5385be959c8SGeorge Karpenkov
5395be959c8SGeorge Karpenkov if (SplitNecessary) {
54007c7257cSArtem Dergachev if (!CE.getResultType()->isScalarType()) {
54107c7257cSArtem Dergachev // Structures cannot be assumed. This probably deserves
54207c7257cSArtem Dergachev // a compiler warning for invalid annotations.
54307c7257cSArtem Dergachev return {State};
54407c7257cSArtem Dergachev }
5455be959c8SGeorge Karpenkov if (auto DL = L.getAs<DefinedOrUnknownSVal>()) {
5465be959c8SGeorge Karpenkov AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true);
5475be959c8SGeorge Karpenkov AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false);
5485be959c8SGeorge Karpenkov }
5495be959c8SGeorge Karpenkov }
5505be959c8SGeorge Karpenkov
5515be959c8SGeorge Karpenkov for (unsigned idx = 0, e = CE.getNumArgs(); idx != e; ++idx) {
5525be959c8SGeorge Karpenkov SVal ArgVal = CE.getArgSVal(idx);
5535be959c8SGeorge Karpenkov ArgEffect AE = Summ.getArg(idx);
5545be959c8SGeorge Karpenkov
55570c2ee30SGeorge Karpenkov auto *ArgRegion = dyn_cast_or_null<TypedValueRegion>(ArgVal.getAsRegion());
55670c2ee30SGeorge Karpenkov if (!ArgRegion)
5575be959c8SGeorge Karpenkov continue;
55870c2ee30SGeorge Karpenkov
55970c2ee30SGeorge Karpenkov QualType PointeeTy = ArgRegion->getValueType();
56070c2ee30SGeorge Karpenkov SVal PointeeVal = State->getSVal(ArgRegion);
56170c2ee30SGeorge Karpenkov SymbolRef Pointee = PointeeVal.getAsLocSymbol();
56270c2ee30SGeorge Karpenkov if (!Pointee)
5635be959c8SGeorge Karpenkov continue;
56470c2ee30SGeorge Karpenkov
5655be959c8SGeorge Karpenkov if (shouldEscapeRegion(ArgRegion))
5665be959c8SGeorge Karpenkov continue;
5675be959c8SGeorge Karpenkov
5685be959c8SGeorge Karpenkov auto makeNotOwnedParameter = [&](ProgramStateRef St) {
5695be959c8SGeorge Karpenkov return setRefBinding(St, Pointee,
5705be959c8SGeorge Karpenkov RefVal::makeNotOwned(AE.getObjKind(), PointeeTy));
5715be959c8SGeorge Karpenkov };
5725be959c8SGeorge Karpenkov auto makeOwnedParameter = [&](ProgramStateRef St) {
5735be959c8SGeorge Karpenkov return setRefBinding(St, Pointee,
5745be959c8SGeorge Karpenkov RefVal::makeOwned(ObjKind::OS, PointeeTy));
5755be959c8SGeorge Karpenkov };
5765be959c8SGeorge Karpenkov
5775be959c8SGeorge Karpenkov switch (AE.getKind()) {
57870c2ee30SGeorge Karpenkov case UnretainedOutParameter:
5795be959c8SGeorge Karpenkov AssumeNonZeroReturn = makeNotOwnedParameter(AssumeNonZeroReturn);
5805be959c8SGeorge Karpenkov AssumeZeroReturn = makeNotOwnedParameter(AssumeZeroReturn);
58170c2ee30SGeorge Karpenkov break;
58270c2ee30SGeorge Karpenkov case RetainedOutParameter:
5835be959c8SGeorge Karpenkov AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn);
5845be959c8SGeorge Karpenkov AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn);
58570c2ee30SGeorge Karpenkov break;
5865be959c8SGeorge Karpenkov case RetainedOutParameterOnNonZero:
5875be959c8SGeorge Karpenkov AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn);
5885be959c8SGeorge Karpenkov break;
5895be959c8SGeorge Karpenkov case RetainedOutParameterOnZero:
5905be959c8SGeorge Karpenkov AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn);
5915be959c8SGeorge Karpenkov break;
59270c2ee30SGeorge Karpenkov default:
5935be959c8SGeorge Karpenkov break;
5945be959c8SGeorge Karpenkov }
59570c2ee30SGeorge Karpenkov }
59670c2ee30SGeorge Karpenkov
5975be959c8SGeorge Karpenkov if (SplitNecessary) {
5985be959c8SGeorge Karpenkov return {AssumeNonZeroReturn, AssumeZeroReturn};
5995be959c8SGeorge Karpenkov } else {
6005be959c8SGeorge Karpenkov assert(AssumeZeroReturn == AssumeNonZeroReturn);
6015be959c8SGeorge Karpenkov return {AssumeZeroReturn};
6025be959c8SGeorge Karpenkov }
60370c2ee30SGeorge Karpenkov }
60470c2ee30SGeorge Karpenkov
checkSummary(const RetainSummary & Summ,const CallEvent & CallOrMsg,CheckerContext & C) const60570c2ee30SGeorge Karpenkov void RetainCountChecker::checkSummary(const RetainSummary &Summ,
60670c2ee30SGeorge Karpenkov const CallEvent &CallOrMsg,
60770c2ee30SGeorge Karpenkov CheckerContext &C) const {
60870c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
60970c2ee30SGeorge Karpenkov
61070c2ee30SGeorge Karpenkov // Evaluate the effect of the arguments.
61170c2ee30SGeorge Karpenkov RefVal::Kind hasErr = (RefVal::Kind) 0;
61270c2ee30SGeorge Karpenkov SourceRange ErrorRange;
61370c2ee30SGeorge Karpenkov SymbolRef ErrorSym = nullptr;
61470c2ee30SGeorge Karpenkov
615717c4c0eSGeorge Karpenkov // Helper tag for providing diagnostics: indicate whether dealloc was sent
616717c4c0eSGeorge Karpenkov // at this location.
617717c4c0eSGeorge Karpenkov bool DeallocSent = false;
618717c4c0eSGeorge Karpenkov
61970c2ee30SGeorge Karpenkov for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
62070c2ee30SGeorge Karpenkov SVal V = CallOrMsg.getArgSVal(idx);
62170c2ee30SGeorge Karpenkov
6229cbcc21aSGeorge Karpenkov ArgEffect Effect = Summ.getArg(idx);
6235be959c8SGeorge Karpenkov if (SymbolRef Sym = V.getAsLocSymbol()) {
62470c2ee30SGeorge Karpenkov if (const RefVal *T = getRefBinding(state, Sym)) {
625041c9fa8SGeorge Karpenkov
6269cbcc21aSGeorge Karpenkov if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T))
6279cbcc21aSGeorge Karpenkov Effect = ArgEffect(StopTrackingHard, ObjKind::OS);
628041c9fa8SGeorge Karpenkov
62970c2ee30SGeorge Karpenkov state = updateSymbol(state, Sym, *T, Effect, hasErr, C);
63070c2ee30SGeorge Karpenkov if (hasErr) {
63170c2ee30SGeorge Karpenkov ErrorRange = CallOrMsg.getArgSourceRange(idx);
63270c2ee30SGeorge Karpenkov ErrorSym = Sym;
63370c2ee30SGeorge Karpenkov break;
634717c4c0eSGeorge Karpenkov } else if (Effect.getKind() == Dealloc) {
635717c4c0eSGeorge Karpenkov DeallocSent = true;
63670c2ee30SGeorge Karpenkov }
63770c2ee30SGeorge Karpenkov }
63870c2ee30SGeorge Karpenkov }
63970c2ee30SGeorge Karpenkov }
64070c2ee30SGeorge Karpenkov
641ab0011ebSGeorge Karpenkov // Evaluate the effect on the message receiver / `this` argument.
64270c2ee30SGeorge Karpenkov bool ReceiverIsTracked = false;
64370c2ee30SGeorge Karpenkov if (!hasErr) {
644ab0011ebSGeorge Karpenkov if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
64570c2ee30SGeorge Karpenkov if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
64670c2ee30SGeorge Karpenkov if (const RefVal *T = getRefBinding(state, Sym)) {
64770c2ee30SGeorge Karpenkov ReceiverIsTracked = true;
648585a210eSGeorge Karpenkov state = updateSymbol(state, Sym, *T,
6499cbcc21aSGeorge Karpenkov Summ.getReceiverEffect(), hasErr, C);
65070c2ee30SGeorge Karpenkov if (hasErr) {
65170c2ee30SGeorge Karpenkov ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
65270c2ee30SGeorge Karpenkov ErrorSym = Sym;
653717c4c0eSGeorge Karpenkov } else if (Summ.getReceiverEffect().getKind() == Dealloc) {
654717c4c0eSGeorge Karpenkov DeallocSent = true;
65570c2ee30SGeorge Karpenkov }
65670c2ee30SGeorge Karpenkov }
65770c2ee30SGeorge Karpenkov }
658ab0011ebSGeorge Karpenkov } else if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) {
659ab0011ebSGeorge Karpenkov if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) {
660ab0011ebSGeorge Karpenkov if (const RefVal *T = getRefBinding(state, Sym)) {
6619cbcc21aSGeorge Karpenkov state = updateSymbol(state, Sym, *T, Summ.getThisEffect(),
662ab0011ebSGeorge Karpenkov hasErr, C);
663ab0011ebSGeorge Karpenkov if (hasErr) {
664ab0011ebSGeorge Karpenkov ErrorRange = MCall->getOriginExpr()->getSourceRange();
665ab0011ebSGeorge Karpenkov ErrorSym = Sym;
666ab0011ebSGeorge Karpenkov }
667ab0011ebSGeorge Karpenkov }
668ab0011ebSGeorge Karpenkov }
66970c2ee30SGeorge Karpenkov }
67070c2ee30SGeorge Karpenkov }
67170c2ee30SGeorge Karpenkov
67270c2ee30SGeorge Karpenkov // Process any errors.
67370c2ee30SGeorge Karpenkov if (hasErr) {
67470c2ee30SGeorge Karpenkov processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C);
67570c2ee30SGeorge Karpenkov return;
67670c2ee30SGeorge Karpenkov }
67770c2ee30SGeorge Karpenkov
67870c2ee30SGeorge Karpenkov // Consult the summary for the return value.
67970c2ee30SGeorge Karpenkov RetEffect RE = Summ.getRetEffect();
68070c2ee30SGeorge Karpenkov
68170c2ee30SGeorge Karpenkov if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
68270c2ee30SGeorge Karpenkov if (ReceiverIsTracked)
68370c2ee30SGeorge Karpenkov RE = getSummaryManager(C).getObjAllocRetEffect();
68470c2ee30SGeorge Karpenkov else
68570c2ee30SGeorge Karpenkov RE = RetEffect::MakeNoRet();
68670c2ee30SGeorge Karpenkov }
68770c2ee30SGeorge Karpenkov
68880c9e78eSGeorge Karpenkov if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
68970c2ee30SGeorge Karpenkov QualType ResultTy = CallOrMsg.getResultType();
69080c9e78eSGeorge Karpenkov if (RE.notOwned()) {
69170c2ee30SGeorge Karpenkov const Expr *Ex = CallOrMsg.getOriginExpr();
69270c2ee30SGeorge Karpenkov assert(Ex);
69380c9e78eSGeorge Karpenkov ResultTy = GetReturnType(Ex, C.getASTContext());
69470c2ee30SGeorge Karpenkov }
69580c9e78eSGeorge Karpenkov if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
69680c9e78eSGeorge Karpenkov state = setRefBinding(state, Sym, *updatedRefVal);
69770c2ee30SGeorge Karpenkov }
69870c2ee30SGeorge Karpenkov
6995be959c8SGeorge Karpenkov SmallVector<ProgramStateRef, 2> Out =
7005be959c8SGeorge Karpenkov updateOutParameters(state, Summ, CallOrMsg);
7015be959c8SGeorge Karpenkov
7025be959c8SGeorge Karpenkov for (ProgramStateRef St : Out) {
703717c4c0eSGeorge Karpenkov if (DeallocSent) {
7045192783bSKirstóf Umann C.addTransition(St, C.getPredecessor(), &getDeallocSentTag());
70570c2ee30SGeorge Karpenkov } else {
7065be959c8SGeorge Karpenkov C.addTransition(St);
7075be959c8SGeorge Karpenkov }
70870c2ee30SGeorge Karpenkov }
70970c2ee30SGeorge Karpenkov }
71070c2ee30SGeorge Karpenkov
updateSymbol(ProgramStateRef state,SymbolRef sym,RefVal V,ArgEffect AE,RefVal::Kind & hasErr,CheckerContext & C) const711585a210eSGeorge Karpenkov ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
712585a210eSGeorge Karpenkov SymbolRef sym, RefVal V,
7139cbcc21aSGeorge Karpenkov ArgEffect AE,
714585a210eSGeorge Karpenkov RefVal::Kind &hasErr,
71570c2ee30SGeorge Karpenkov CheckerContext &C) const {
71670c2ee30SGeorge Karpenkov bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
7179cbcc21aSGeorge Karpenkov if (AE.getObjKind() == ObjKind::ObjC && IgnoreRetainMsg) {
7189cbcc21aSGeorge Karpenkov switch (AE.getKind()) {
71970c2ee30SGeorge Karpenkov default:
72070c2ee30SGeorge Karpenkov break;
7219cbcc21aSGeorge Karpenkov case IncRef:
7229cbcc21aSGeorge Karpenkov AE = AE.withKind(DoNothing);
72370c2ee30SGeorge Karpenkov break;
7249cbcc21aSGeorge Karpenkov case DecRef:
7259cbcc21aSGeorge Karpenkov AE = AE.withKind(DoNothing);
72670c2ee30SGeorge Karpenkov break;
7279cbcc21aSGeorge Karpenkov case DecRefAndStopTrackingHard:
7289cbcc21aSGeorge Karpenkov AE = AE.withKind(StopTracking);
72970c2ee30SGeorge Karpenkov break;
73070c2ee30SGeorge Karpenkov }
7319cbcc21aSGeorge Karpenkov }
73270c2ee30SGeorge Karpenkov
73370c2ee30SGeorge Karpenkov // Handle all use-after-releases.
73470c2ee30SGeorge Karpenkov if (V.getKind() == RefVal::Released) {
73570c2ee30SGeorge Karpenkov V = V ^ RefVal::ErrorUseAfterRelease;
73670c2ee30SGeorge Karpenkov hasErr = V.getKind();
73770c2ee30SGeorge Karpenkov return setRefBinding(state, sym, V);
73870c2ee30SGeorge Karpenkov }
73970c2ee30SGeorge Karpenkov
7409cbcc21aSGeorge Karpenkov switch (AE.getKind()) {
74170c2ee30SGeorge Karpenkov case UnretainedOutParameter:
74270c2ee30SGeorge Karpenkov case RetainedOutParameter:
7435be959c8SGeorge Karpenkov case RetainedOutParameterOnZero:
7445be959c8SGeorge Karpenkov case RetainedOutParameterOnNonZero:
74570c2ee30SGeorge Karpenkov llvm_unreachable("Applies to pointer-to-pointer parameters, which should "
74670c2ee30SGeorge Karpenkov "not have ref state.");
74770c2ee30SGeorge Karpenkov
748717c4c0eSGeorge Karpenkov case Dealloc: // NB. we only need to add a note in a non-error case.
74970c2ee30SGeorge Karpenkov switch (V.getKind()) {
75070c2ee30SGeorge Karpenkov default:
75170c2ee30SGeorge Karpenkov llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
75270c2ee30SGeorge Karpenkov case RefVal::Owned:
75370c2ee30SGeorge Karpenkov // The object immediately transitions to the released state.
75470c2ee30SGeorge Karpenkov V = V ^ RefVal::Released;
75570c2ee30SGeorge Karpenkov V.clearCounts();
75670c2ee30SGeorge Karpenkov return setRefBinding(state, sym, V);
75770c2ee30SGeorge Karpenkov case RefVal::NotOwned:
75870c2ee30SGeorge Karpenkov V = V ^ RefVal::ErrorDeallocNotOwned;
75970c2ee30SGeorge Karpenkov hasErr = V.getKind();
76070c2ee30SGeorge Karpenkov break;
76170c2ee30SGeorge Karpenkov }
76270c2ee30SGeorge Karpenkov break;
76370c2ee30SGeorge Karpenkov
76470c2ee30SGeorge Karpenkov case MayEscape:
76570c2ee30SGeorge Karpenkov if (V.getKind() == RefVal::Owned) {
76670c2ee30SGeorge Karpenkov V = V ^ RefVal::NotOwned;
76770c2ee30SGeorge Karpenkov break;
76870c2ee30SGeorge Karpenkov }
76970c2ee30SGeorge Karpenkov
7704dc0b1acSReid Kleckner LLVM_FALLTHROUGH;
77170c2ee30SGeorge Karpenkov
77270c2ee30SGeorge Karpenkov case DoNothing:
77370c2ee30SGeorge Karpenkov return state;
77470c2ee30SGeorge Karpenkov
77570c2ee30SGeorge Karpenkov case Autorelease:
77670c2ee30SGeorge Karpenkov // Update the autorelease counts.
77770c2ee30SGeorge Karpenkov V = V.autorelease();
77870c2ee30SGeorge Karpenkov break;
77970c2ee30SGeorge Karpenkov
78070c2ee30SGeorge Karpenkov case StopTracking:
78170c2ee30SGeorge Karpenkov case StopTrackingHard:
78270c2ee30SGeorge Karpenkov return removeRefBinding(state, sym);
78370c2ee30SGeorge Karpenkov
78470c2ee30SGeorge Karpenkov case IncRef:
78570c2ee30SGeorge Karpenkov switch (V.getKind()) {
78670c2ee30SGeorge Karpenkov default:
78770c2ee30SGeorge Karpenkov llvm_unreachable("Invalid RefVal state for a retain.");
78870c2ee30SGeorge Karpenkov case RefVal::Owned:
78970c2ee30SGeorge Karpenkov case RefVal::NotOwned:
79070c2ee30SGeorge Karpenkov V = V + 1;
79170c2ee30SGeorge Karpenkov break;
79270c2ee30SGeorge Karpenkov }
79370c2ee30SGeorge Karpenkov break;
79470c2ee30SGeorge Karpenkov
79570c2ee30SGeorge Karpenkov case DecRef:
79670c2ee30SGeorge Karpenkov case DecRefBridgedTransferred:
79770c2ee30SGeorge Karpenkov case DecRefAndStopTrackingHard:
79870c2ee30SGeorge Karpenkov switch (V.getKind()) {
79970c2ee30SGeorge Karpenkov default:
80070c2ee30SGeorge Karpenkov // case 'RefVal::Released' handled above.
80170c2ee30SGeorge Karpenkov llvm_unreachable("Invalid RefVal state for a release.");
80270c2ee30SGeorge Karpenkov
80370c2ee30SGeorge Karpenkov case RefVal::Owned:
80470c2ee30SGeorge Karpenkov assert(V.getCount() > 0);
80570c2ee30SGeorge Karpenkov if (V.getCount() == 1) {
8069cbcc21aSGeorge Karpenkov if (AE.getKind() == DecRefBridgedTransferred ||
80770c2ee30SGeorge Karpenkov V.getIvarAccessHistory() ==
80870c2ee30SGeorge Karpenkov RefVal::IvarAccessHistory::AccessedDirectly)
80970c2ee30SGeorge Karpenkov V = V ^ RefVal::NotOwned;
81070c2ee30SGeorge Karpenkov else
81170c2ee30SGeorge Karpenkov V = V ^ RefVal::Released;
8129cbcc21aSGeorge Karpenkov } else if (AE.getKind() == DecRefAndStopTrackingHard) {
81370c2ee30SGeorge Karpenkov return removeRefBinding(state, sym);
81470c2ee30SGeorge Karpenkov }
81570c2ee30SGeorge Karpenkov
81670c2ee30SGeorge Karpenkov V = V - 1;
81770c2ee30SGeorge Karpenkov break;
81870c2ee30SGeorge Karpenkov
81970c2ee30SGeorge Karpenkov case RefVal::NotOwned:
82070c2ee30SGeorge Karpenkov if (V.getCount() > 0) {
8219cbcc21aSGeorge Karpenkov if (AE.getKind() == DecRefAndStopTrackingHard)
82270c2ee30SGeorge Karpenkov return removeRefBinding(state, sym);
82370c2ee30SGeorge Karpenkov V = V - 1;
82470c2ee30SGeorge Karpenkov } else if (V.getIvarAccessHistory() ==
82570c2ee30SGeorge Karpenkov RefVal::IvarAccessHistory::AccessedDirectly) {
82670c2ee30SGeorge Karpenkov // Assume that the instance variable was holding on the object at
82770c2ee30SGeorge Karpenkov // +1, and we just didn't know.
8289cbcc21aSGeorge Karpenkov if (AE.getKind() == DecRefAndStopTrackingHard)
82970c2ee30SGeorge Karpenkov return removeRefBinding(state, sym);
83070c2ee30SGeorge Karpenkov V = V.releaseViaIvar() ^ RefVal::Released;
83170c2ee30SGeorge Karpenkov } else {
83270c2ee30SGeorge Karpenkov V = V ^ RefVal::ErrorReleaseNotOwned;
83370c2ee30SGeorge Karpenkov hasErr = V.getKind();
83470c2ee30SGeorge Karpenkov }
83570c2ee30SGeorge Karpenkov break;
83670c2ee30SGeorge Karpenkov }
83770c2ee30SGeorge Karpenkov break;
83870c2ee30SGeorge Karpenkov }
83970c2ee30SGeorge Karpenkov return setRefBinding(state, sym, V);
84070c2ee30SGeorge Karpenkov }
84170c2ee30SGeorge Karpenkov
8422c2d0b6eSGeorge Karpenkov const RefCountBug &
errorKindToBugKind(RefVal::Kind ErrorKind,SymbolRef Sym) const8432c2d0b6eSGeorge Karpenkov RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind,
8442c2d0b6eSGeorge Karpenkov SymbolRef Sym) const {
8452c2d0b6eSGeorge Karpenkov switch (ErrorKind) {
8462c2d0b6eSGeorge Karpenkov case RefVal::ErrorUseAfterRelease:
8475192783bSKirstóf Umann return *UseAfterRelease;
8482c2d0b6eSGeorge Karpenkov case RefVal::ErrorReleaseNotOwned:
8495192783bSKirstóf Umann return *ReleaseNotOwned;
8502c2d0b6eSGeorge Karpenkov case RefVal::ErrorDeallocNotOwned:
8512c2d0b6eSGeorge Karpenkov if (Sym->getType()->getPointeeCXXRecordDecl())
8525192783bSKirstóf Umann return *FreeNotOwned;
8535192783bSKirstóf Umann return *DeallocNotOwned;
8542c2d0b6eSGeorge Karpenkov default:
8552c2d0b6eSGeorge Karpenkov llvm_unreachable("Unhandled error.");
8562c2d0b6eSGeorge Karpenkov }
8572c2d0b6eSGeorge Karpenkov }
8582c2d0b6eSGeorge Karpenkov
processNonLeakError(ProgramStateRef St,SourceRange ErrorRange,RefVal::Kind ErrorKind,SymbolRef Sym,CheckerContext & C) const85970c2ee30SGeorge Karpenkov void RetainCountChecker::processNonLeakError(ProgramStateRef St,
86070c2ee30SGeorge Karpenkov SourceRange ErrorRange,
86170c2ee30SGeorge Karpenkov RefVal::Kind ErrorKind,
86270c2ee30SGeorge Karpenkov SymbolRef Sym,
86370c2ee30SGeorge Karpenkov CheckerContext &C) const {
86470c2ee30SGeorge Karpenkov // HACK: Ignore retain-count issues on values accessed through ivars,
86570c2ee30SGeorge Karpenkov // because of cases like this:
86670c2ee30SGeorge Karpenkov // [_contentView retain];
86770c2ee30SGeorge Karpenkov // [_contentView removeFromSuperview];
86870c2ee30SGeorge Karpenkov // [self addSubview:_contentView]; // invalidates 'self'
86970c2ee30SGeorge Karpenkov // [_contentView release];
87070c2ee30SGeorge Karpenkov if (const RefVal *RV = getRefBinding(St, Sym))
87170c2ee30SGeorge Karpenkov if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
87270c2ee30SGeorge Karpenkov return;
87370c2ee30SGeorge Karpenkov
87470c2ee30SGeorge Karpenkov ExplodedNode *N = C.generateErrorNode(St);
87570c2ee30SGeorge Karpenkov if (!N)
87670c2ee30SGeorge Karpenkov return;
87770c2ee30SGeorge Karpenkov
8782b3d49b6SJonas Devlieghere auto report = std::make_unique<RefCountReport>(
8792c2d0b6eSGeorge Karpenkov errorKindToBugKind(ErrorKind, Sym),
8802c2d0b6eSGeorge Karpenkov C.getASTContext().getLangOpts(), N, Sym);
88170c2ee30SGeorge Karpenkov report->addRange(ErrorRange);
88270c2ee30SGeorge Karpenkov C.emitReport(std::move(report));
88370c2ee30SGeorge Karpenkov }
88470c2ee30SGeorge Karpenkov
88570c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
88670c2ee30SGeorge Karpenkov // Handle the return values of retain-count-related functions.
88770c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
88870c2ee30SGeorge Karpenkov
evalCall(const CallEvent & Call,CheckerContext & C) const88944820630SArtem Dergachev bool RetainCountChecker::evalCall(const CallEvent &Call,
89044820630SArtem Dergachev CheckerContext &C) const {
89170c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
89244820630SArtem Dergachev const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
89370c2ee30SGeorge Karpenkov if (!FD)
89470c2ee30SGeorge Karpenkov return false;
89570c2ee30SGeorge Karpenkov
89644820630SArtem Dergachev const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
89744820630SArtem Dergachev if (!CE)
89844820630SArtem Dergachev return false;
89944820630SArtem Dergachev
900c4d6b93dSGeorge Karpenkov RetainSummaryManager &SmrMgr = getSummaryManager(C);
90144820630SArtem Dergachev QualType ResultTy = Call.getResultType();
90270c2ee30SGeorge Karpenkov
90370c2ee30SGeorge Karpenkov // See if the function has 'rc_ownership_trusted_implementation'
90470c2ee30SGeorge Karpenkov // annotate attribute. If it does, we will not inline it.
90570c2ee30SGeorge Karpenkov bool hasTrustedImplementationAnnotation = false;
90670c2ee30SGeorge Karpenkov
90741dc8de6SGeorge Karpenkov const LocationContext *LCtx = C.getLocationContext();
90841dc8de6SGeorge Karpenkov
9093c2ed8f3SGeorge Karpenkov using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
9103c2ed8f3SGeorge Karpenkov Optional<BehaviorSummary> BSmr =
9113c2ed8f3SGeorge Karpenkov SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
9123c2ed8f3SGeorge Karpenkov
913c4d6b93dSGeorge Karpenkov // See if it's one of the specific functions we know how to eval.
9143c2ed8f3SGeorge Karpenkov if (!BSmr)
91570c2ee30SGeorge Karpenkov return false;
91670c2ee30SGeorge Karpenkov
91770c2ee30SGeorge Karpenkov // Bind the return value.
9183c2ed8f3SGeorge Karpenkov if (BSmr == BehaviorSummary::Identity ||
919db0c66eeSGeorge Karpenkov BSmr == BehaviorSummary::IdentityOrZero ||
920db0c66eeSGeorge Karpenkov BSmr == BehaviorSummary::IdentityThis) {
921db0c66eeSGeorge Karpenkov
922db0c66eeSGeorge Karpenkov const Expr *BindReturnTo =
923db0c66eeSGeorge Karpenkov (BSmr == BehaviorSummary::IdentityThis)
924db0c66eeSGeorge Karpenkov ? cast<CXXMemberCallExpr>(CE)->getImplicitObjectArgument()
925db0c66eeSGeorge Karpenkov : CE->getArg(0);
926db0c66eeSGeorge Karpenkov SVal RetVal = state->getSVal(BindReturnTo, LCtx);
92748de582fSGeorge Karpenkov
92870c2ee30SGeorge Karpenkov // If the receiver is unknown or the function has
92970c2ee30SGeorge Karpenkov // 'rc_ownership_trusted_implementation' annotate attribute, conjure a
93070c2ee30SGeorge Karpenkov // return value.
931db0c66eeSGeorge Karpenkov // FIXME: this branch is very strange.
93248de582fSGeorge Karpenkov if (RetVal.isUnknown() ||
93348de582fSGeorge Karpenkov (hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
93470c2ee30SGeorge Karpenkov SValBuilder &SVB = C.getSValBuilder();
93548de582fSGeorge Karpenkov RetVal =
93648de582fSGeorge Karpenkov SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
93770c2ee30SGeorge Karpenkov }
938db0c66eeSGeorge Karpenkov
939db0c66eeSGeorge Karpenkov // Bind the value.
9403c2ed8f3SGeorge Karpenkov state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
9413c2ed8f3SGeorge Karpenkov
9423c2ed8f3SGeorge Karpenkov if (BSmr == BehaviorSummary::IdentityOrZero) {
9433c2ed8f3SGeorge Karpenkov // Add a branch where the output is zero.
9443c2ed8f3SGeorge Karpenkov ProgramStateRef NullOutputState = C.getState();
9453c2ed8f3SGeorge Karpenkov
9463c2ed8f3SGeorge Karpenkov // Assume that output is zero on the other branch.
9473c2ed8f3SGeorge Karpenkov NullOutputState = NullOutputState->BindExpr(
94898588841SVince Bridgers CE, LCtx, C.getSValBuilder().makeNullWithType(ResultTy),
94998588841SVince Bridgers /*Invalidate=*/false);
9505192783bSKirstóf Umann C.addTransition(NullOutputState, &getCastFailTag());
9513c2ed8f3SGeorge Karpenkov
9523c2ed8f3SGeorge Karpenkov // And on the original branch assume that both input and
9533c2ed8f3SGeorge Karpenkov // output are non-zero.
9543c2ed8f3SGeorge Karpenkov if (auto L = RetVal.getAs<DefinedOrUnknownSVal>())
95549a3ad21SRui Ueyama state = state->assume(*L, /*assumption=*/true);
9563c2ed8f3SGeorge Karpenkov
9573c2ed8f3SGeorge Karpenkov }
95848de582fSGeorge Karpenkov }
95970c2ee30SGeorge Karpenkov
96070c2ee30SGeorge Karpenkov C.addTransition(state);
96170c2ee30SGeorge Karpenkov return true;
96270c2ee30SGeorge Karpenkov }
96370c2ee30SGeorge Karpenkov
processReturn(const ReturnStmt * S,CheckerContext & C) const96404553e53SGeorge Karpenkov ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
96570c2ee30SGeorge Karpenkov CheckerContext &C) const {
96604553e53SGeorge Karpenkov ExplodedNode *Pred = C.getPredecessor();
96770c2ee30SGeorge Karpenkov
96870c2ee30SGeorge Karpenkov // Only adjust the reference count if this is the top-level call frame,
96970c2ee30SGeorge Karpenkov // and not the result of inlining. In the future, we should do
97070c2ee30SGeorge Karpenkov // better checking even for inlined calls, and see if they match
97170c2ee30SGeorge Karpenkov // with their expected semantics (e.g., the method should return a retained
97270c2ee30SGeorge Karpenkov // object, etc.).
97370c2ee30SGeorge Karpenkov if (!C.inTopFrame())
97404553e53SGeorge Karpenkov return Pred;
97504553e53SGeorge Karpenkov
97604553e53SGeorge Karpenkov if (!S)
97704553e53SGeorge Karpenkov return Pred;
97870c2ee30SGeorge Karpenkov
97970c2ee30SGeorge Karpenkov const Expr *RetE = S->getRetValue();
98070c2ee30SGeorge Karpenkov if (!RetE)
98104553e53SGeorge Karpenkov return Pred;
98270c2ee30SGeorge Karpenkov
98370c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
984e264ac6aSArtem Dergachev // We need to dig down to the symbolic base here because various
985e264ac6aSArtem Dergachev // custom allocators do sometimes return the symbol with an offset.
986e264ac6aSArtem Dergachev SymbolRef Sym = state->getSValAsScalarOrLoc(RetE, C.getLocationContext())
987e264ac6aSArtem Dergachev .getAsLocSymbol(/*IncludeBaseRegions=*/true);
98870c2ee30SGeorge Karpenkov if (!Sym)
98904553e53SGeorge Karpenkov return Pred;
99070c2ee30SGeorge Karpenkov
99170c2ee30SGeorge Karpenkov // Get the reference count binding (if any).
99270c2ee30SGeorge Karpenkov const RefVal *T = getRefBinding(state, Sym);
99370c2ee30SGeorge Karpenkov if (!T)
99404553e53SGeorge Karpenkov return Pred;
99570c2ee30SGeorge Karpenkov
99670c2ee30SGeorge Karpenkov // Change the reference count.
99770c2ee30SGeorge Karpenkov RefVal X = *T;
99870c2ee30SGeorge Karpenkov
99970c2ee30SGeorge Karpenkov switch (X.getKind()) {
100070c2ee30SGeorge Karpenkov case RefVal::Owned: {
100170c2ee30SGeorge Karpenkov unsigned cnt = X.getCount();
100270c2ee30SGeorge Karpenkov assert(cnt > 0);
100370c2ee30SGeorge Karpenkov X.setCount(cnt - 1);
100470c2ee30SGeorge Karpenkov X = X ^ RefVal::ReturnedOwned;
100570c2ee30SGeorge Karpenkov break;
100670c2ee30SGeorge Karpenkov }
100770c2ee30SGeorge Karpenkov
100870c2ee30SGeorge Karpenkov case RefVal::NotOwned: {
100970c2ee30SGeorge Karpenkov unsigned cnt = X.getCount();
101070c2ee30SGeorge Karpenkov if (cnt) {
101170c2ee30SGeorge Karpenkov X.setCount(cnt - 1);
101270c2ee30SGeorge Karpenkov X = X ^ RefVal::ReturnedOwned;
101304553e53SGeorge Karpenkov } else {
101470c2ee30SGeorge Karpenkov X = X ^ RefVal::ReturnedNotOwned;
101570c2ee30SGeorge Karpenkov }
101670c2ee30SGeorge Karpenkov break;
101770c2ee30SGeorge Karpenkov }
101870c2ee30SGeorge Karpenkov
101970c2ee30SGeorge Karpenkov default:
102004553e53SGeorge Karpenkov return Pred;
102170c2ee30SGeorge Karpenkov }
102270c2ee30SGeorge Karpenkov
102370c2ee30SGeorge Karpenkov // Update the binding.
102470c2ee30SGeorge Karpenkov state = setRefBinding(state, Sym, X);
102504553e53SGeorge Karpenkov Pred = C.addTransition(state);
102670c2ee30SGeorge Karpenkov
102770c2ee30SGeorge Karpenkov // At this point we have updated the state properly.
102870c2ee30SGeorge Karpenkov // Everything after this is merely checking to see if the return value has
102970c2ee30SGeorge Karpenkov // been over- or under-retained.
103070c2ee30SGeorge Karpenkov
103170c2ee30SGeorge Karpenkov // Did we cache out?
103270c2ee30SGeorge Karpenkov if (!Pred)
103304553e53SGeorge Karpenkov return nullptr;
103470c2ee30SGeorge Karpenkov
103570c2ee30SGeorge Karpenkov // Update the autorelease counts.
103670c2ee30SGeorge Karpenkov static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease");
103704553e53SGeorge Karpenkov state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X, S);
103870c2ee30SGeorge Karpenkov
103904553e53SGeorge Karpenkov // Have we generated a sink node?
104070c2ee30SGeorge Karpenkov if (!state)
104104553e53SGeorge Karpenkov return nullptr;
104270c2ee30SGeorge Karpenkov
104370c2ee30SGeorge Karpenkov // Get the updated binding.
104470c2ee30SGeorge Karpenkov T = getRefBinding(state, Sym);
104570c2ee30SGeorge Karpenkov assert(T);
104670c2ee30SGeorge Karpenkov X = *T;
104770c2ee30SGeorge Karpenkov
104870c2ee30SGeorge Karpenkov // Consult the summary of the enclosing method.
104970c2ee30SGeorge Karpenkov RetainSummaryManager &Summaries = getSummaryManager(C);
105070c2ee30SGeorge Karpenkov const Decl *CD = &Pred->getCodeDecl();
105170c2ee30SGeorge Karpenkov RetEffect RE = RetEffect::MakeNoRet();
105270c2ee30SGeorge Karpenkov
105370c2ee30SGeorge Karpenkov // FIXME: What is the convention for blocks? Is there one?
105470c2ee30SGeorge Karpenkov if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
10552e466678SGeorge Karpenkov const RetainSummary *Summ = Summaries.getSummary(AnyCall(MD));
105670c2ee30SGeorge Karpenkov RE = Summ->getRetEffect();
105770c2ee30SGeorge Karpenkov } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
105870c2ee30SGeorge Karpenkov if (!isa<CXXMethodDecl>(FD)) {
10592e466678SGeorge Karpenkov const RetainSummary *Summ = Summaries.getSummary(AnyCall(FD));
106070c2ee30SGeorge Karpenkov RE = Summ->getRetEffect();
106170c2ee30SGeorge Karpenkov }
106270c2ee30SGeorge Karpenkov }
106370c2ee30SGeorge Karpenkov
106404553e53SGeorge Karpenkov return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
106570c2ee30SGeorge Karpenkov }
106670c2ee30SGeorge Karpenkov
checkReturnWithRetEffect(const ReturnStmt * S,CheckerContext & C,ExplodedNode * Pred,RetEffect RE,RefVal X,SymbolRef Sym,ProgramStateRef state) const106704553e53SGeorge Karpenkov ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
106870c2ee30SGeorge Karpenkov CheckerContext &C,
106970c2ee30SGeorge Karpenkov ExplodedNode *Pred,
107070c2ee30SGeorge Karpenkov RetEffect RE, RefVal X,
107170c2ee30SGeorge Karpenkov SymbolRef Sym,
107270c2ee30SGeorge Karpenkov ProgramStateRef state) const {
107370c2ee30SGeorge Karpenkov // HACK: Ignore retain-count issues on values accessed through ivars,
107470c2ee30SGeorge Karpenkov // because of cases like this:
107570c2ee30SGeorge Karpenkov // [_contentView retain];
107670c2ee30SGeorge Karpenkov // [_contentView removeFromSuperview];
107770c2ee30SGeorge Karpenkov // [self addSubview:_contentView]; // invalidates 'self'
107870c2ee30SGeorge Karpenkov // [_contentView release];
107970c2ee30SGeorge Karpenkov if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
108004553e53SGeorge Karpenkov return Pred;
108170c2ee30SGeorge Karpenkov
108270c2ee30SGeorge Karpenkov // Any leaks or other errors?
108370c2ee30SGeorge Karpenkov if (X.isReturnedOwned() && X.getCount() == 0) {
108470c2ee30SGeorge Karpenkov if (RE.getKind() != RetEffect::NoRet) {
108570c2ee30SGeorge Karpenkov if (!RE.isOwned()) {
108604553e53SGeorge Karpenkov
108770c2ee30SGeorge Karpenkov // The returning type is a CF, we expect the enclosing method should
108870c2ee30SGeorge Karpenkov // return ownership.
108970c2ee30SGeorge Karpenkov X = X ^ RefVal::ErrorLeakReturned;
109070c2ee30SGeorge Karpenkov
109170c2ee30SGeorge Karpenkov // Generate an error node.
109270c2ee30SGeorge Karpenkov state = setRefBinding(state, Sym, X);
109370c2ee30SGeorge Karpenkov
109470c2ee30SGeorge Karpenkov static CheckerProgramPointTag ReturnOwnLeakTag(this, "ReturnsOwnLeak");
109570c2ee30SGeorge Karpenkov ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
109670c2ee30SGeorge Karpenkov if (N) {
109770c2ee30SGeorge Karpenkov const LangOptions &LOpts = C.getASTContext().getLangOpts();
10982c2d0b6eSGeorge Karpenkov auto R =
10995192783bSKirstóf Umann std::make_unique<RefLeakReport>(*LeakAtReturn, LOpts, N, Sym, C);
110004553e53SGeorge Karpenkov C.emitReport(std::move(R));
110170c2ee30SGeorge Karpenkov }
110204553e53SGeorge Karpenkov return N;
110370c2ee30SGeorge Karpenkov }
110470c2ee30SGeorge Karpenkov }
110570c2ee30SGeorge Karpenkov } else if (X.isReturnedNotOwned()) {
110670c2ee30SGeorge Karpenkov if (RE.isOwned()) {
110770c2ee30SGeorge Karpenkov if (X.getIvarAccessHistory() ==
110870c2ee30SGeorge Karpenkov RefVal::IvarAccessHistory::AccessedDirectly) {
110970c2ee30SGeorge Karpenkov // Assume the method was trying to transfer a +1 reference from a
111070c2ee30SGeorge Karpenkov // strong ivar to the caller.
111170c2ee30SGeorge Karpenkov state = setRefBinding(state, Sym,
111270c2ee30SGeorge Karpenkov X.releaseViaIvar() ^ RefVal::ReturnedOwned);
111370c2ee30SGeorge Karpenkov } else {
111470c2ee30SGeorge Karpenkov // Trying to return a not owned object to a caller expecting an
111570c2ee30SGeorge Karpenkov // owned object.
111670c2ee30SGeorge Karpenkov state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
111770c2ee30SGeorge Karpenkov
111870c2ee30SGeorge Karpenkov static CheckerProgramPointTag
111970c2ee30SGeorge Karpenkov ReturnNotOwnedTag(this, "ReturnNotOwnedForOwned");
112070c2ee30SGeorge Karpenkov
112170c2ee30SGeorge Karpenkov ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
112270c2ee30SGeorge Karpenkov if (N) {
11232b3d49b6SJonas Devlieghere auto R = std::make_unique<RefCountReport>(
11245192783bSKirstóf Umann *ReturnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
112504553e53SGeorge Karpenkov C.emitReport(std::move(R));
112604553e53SGeorge Karpenkov }
112704553e53SGeorge Karpenkov return N;
112870c2ee30SGeorge Karpenkov }
112970c2ee30SGeorge Karpenkov }
113070c2ee30SGeorge Karpenkov }
113104553e53SGeorge Karpenkov return Pred;
113270c2ee30SGeorge Karpenkov }
113370c2ee30SGeorge Karpenkov
113470c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
113570c2ee30SGeorge Karpenkov // Check various ways a symbol can be invalidated.
113670c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
113770c2ee30SGeorge Karpenkov
checkBind(SVal loc,SVal val,const Stmt * S,CheckerContext & C) const113870c2ee30SGeorge Karpenkov void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
113970c2ee30SGeorge Karpenkov CheckerContext &C) const {
114070c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
114103391514SGeorge Karpenkov const MemRegion *MR = loc.getAsRegion();
114270c2ee30SGeorge Karpenkov
114303391514SGeorge Karpenkov // Find all symbols referenced by 'val' that we are tracking
114470c2ee30SGeorge Karpenkov // and stop tracking them.
114503391514SGeorge Karpenkov if (MR && shouldEscapeRegion(MR)) {
114670c2ee30SGeorge Karpenkov state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
114770c2ee30SGeorge Karpenkov C.addTransition(state);
114870c2ee30SGeorge Karpenkov }
114903391514SGeorge Karpenkov }
115070c2ee30SGeorge Karpenkov
evalAssume(ProgramStateRef state,SVal Cond,bool Assumption) const115170c2ee30SGeorge Karpenkov ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
115270c2ee30SGeorge Karpenkov SVal Cond,
115370c2ee30SGeorge Karpenkov bool Assumption) const {
115470c2ee30SGeorge Karpenkov // FIXME: We may add to the interface of evalAssume the list of symbols
115570c2ee30SGeorge Karpenkov // whose assumptions have changed. For now we just iterate through the
115670c2ee30SGeorge Karpenkov // bindings and check if any of the tracked symbols are NULL. This isn't
115770c2ee30SGeorge Karpenkov // too bad since the number of symbols we will track in practice are
115870c2ee30SGeorge Karpenkov // probably small and evalAssume is only called at branches and a few
115970c2ee30SGeorge Karpenkov // other places.
116070c2ee30SGeorge Karpenkov RefBindingsTy B = state->get<RefBindings>();
116170c2ee30SGeorge Karpenkov
116270c2ee30SGeorge Karpenkov if (B.isEmpty())
116370c2ee30SGeorge Karpenkov return state;
116470c2ee30SGeorge Karpenkov
116570c2ee30SGeorge Karpenkov bool changed = false;
116670c2ee30SGeorge Karpenkov RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>();
116770c2ee30SGeorge Karpenkov ConstraintManager &CMgr = state->getConstraintManager();
116803391514SGeorge Karpenkov
116903391514SGeorge Karpenkov for (auto &I : B) {
117003391514SGeorge Karpenkov // Check if the symbol is null stop tracking the symbol.
117103391514SGeorge Karpenkov ConditionTruthVal AllocFailed = CMgr.isNull(state, I.first);
117270c2ee30SGeorge Karpenkov if (AllocFailed.isConstrainedTrue()) {
117370c2ee30SGeorge Karpenkov changed = true;
117403391514SGeorge Karpenkov B = RefBFactory.remove(B, I.first);
117570c2ee30SGeorge Karpenkov }
117670c2ee30SGeorge Karpenkov }
117770c2ee30SGeorge Karpenkov
117870c2ee30SGeorge Karpenkov if (changed)
117970c2ee30SGeorge Karpenkov state = state->set<RefBindings>(B);
118070c2ee30SGeorge Karpenkov
118170c2ee30SGeorge Karpenkov return state;
118270c2ee30SGeorge Karpenkov }
118370c2ee30SGeorge Karpenkov
checkRegionChanges(ProgramStateRef state,const InvalidatedSymbols * invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const1184f153cdfbSGeorge Karpenkov ProgramStateRef RetainCountChecker::checkRegionChanges(
1185f153cdfbSGeorge Karpenkov ProgramStateRef state, const InvalidatedSymbols *invalidated,
118670c2ee30SGeorge Karpenkov ArrayRef<const MemRegion *> ExplicitRegions,
1187f153cdfbSGeorge Karpenkov ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
118870c2ee30SGeorge Karpenkov const CallEvent *Call) const {
118970c2ee30SGeorge Karpenkov if (!invalidated)
119070c2ee30SGeorge Karpenkov return state;
119170c2ee30SGeorge Karpenkov
11928659b241SZarko Todorovski llvm::SmallPtrSet<SymbolRef, 8> AllowedSymbols;
119370c2ee30SGeorge Karpenkov
1194f153cdfbSGeorge Karpenkov for (const MemRegion *I : ExplicitRegions)
1195f153cdfbSGeorge Karpenkov if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>())
11968659b241SZarko Todorovski AllowedSymbols.insert(SR->getSymbol());
1197f153cdfbSGeorge Karpenkov
1198f153cdfbSGeorge Karpenkov for (SymbolRef sym : *invalidated) {
11998659b241SZarko Todorovski if (AllowedSymbols.count(sym))
120070c2ee30SGeorge Karpenkov continue;
120170c2ee30SGeorge Karpenkov // Remove any existing reference-count binding.
120270c2ee30SGeorge Karpenkov state = removeRefBinding(state, sym);
120370c2ee30SGeorge Karpenkov }
120470c2ee30SGeorge Karpenkov return state;
120570c2ee30SGeorge Karpenkov }
120670c2ee30SGeorge Karpenkov
120770c2ee30SGeorge Karpenkov ProgramStateRef
handleAutoreleaseCounts(ProgramStateRef state,ExplodedNode * Pred,const ProgramPointTag * Tag,CheckerContext & Ctx,SymbolRef Sym,RefVal V,const ReturnStmt * S) const120870c2ee30SGeorge Karpenkov RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
120970c2ee30SGeorge Karpenkov ExplodedNode *Pred,
121070c2ee30SGeorge Karpenkov const ProgramPointTag *Tag,
121170c2ee30SGeorge Karpenkov CheckerContext &Ctx,
121204553e53SGeorge Karpenkov SymbolRef Sym,
121304553e53SGeorge Karpenkov RefVal V,
121404553e53SGeorge Karpenkov const ReturnStmt *S) const {
121570c2ee30SGeorge Karpenkov unsigned ACnt = V.getAutoreleaseCount();
121670c2ee30SGeorge Karpenkov
121770c2ee30SGeorge Karpenkov // No autorelease counts? Nothing to be done.
121870c2ee30SGeorge Karpenkov if (!ACnt)
121970c2ee30SGeorge Karpenkov return state;
122070c2ee30SGeorge Karpenkov
122170c2ee30SGeorge Karpenkov unsigned Cnt = V.getCount();
122270c2ee30SGeorge Karpenkov
122370c2ee30SGeorge Karpenkov // FIXME: Handle sending 'autorelease' to already released object.
122470c2ee30SGeorge Karpenkov
122570c2ee30SGeorge Karpenkov if (V.getKind() == RefVal::ReturnedOwned)
122670c2ee30SGeorge Karpenkov ++Cnt;
122770c2ee30SGeorge Karpenkov
122870c2ee30SGeorge Karpenkov // If we would over-release here, but we know the value came from an ivar,
122970c2ee30SGeorge Karpenkov // assume it was a strong ivar that's just been relinquished.
123070c2ee30SGeorge Karpenkov if (ACnt > Cnt &&
123170c2ee30SGeorge Karpenkov V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) {
123270c2ee30SGeorge Karpenkov V = V.releaseViaIvar();
123370c2ee30SGeorge Karpenkov --ACnt;
123470c2ee30SGeorge Karpenkov }
123570c2ee30SGeorge Karpenkov
123670c2ee30SGeorge Karpenkov if (ACnt <= Cnt) {
123770c2ee30SGeorge Karpenkov if (ACnt == Cnt) {
123870c2ee30SGeorge Karpenkov V.clearCounts();
123904553e53SGeorge Karpenkov if (V.getKind() == RefVal::ReturnedOwned) {
124070c2ee30SGeorge Karpenkov V = V ^ RefVal::ReturnedNotOwned;
124104553e53SGeorge Karpenkov } else {
124270c2ee30SGeorge Karpenkov V = V ^ RefVal::NotOwned;
124304553e53SGeorge Karpenkov }
124470c2ee30SGeorge Karpenkov } else {
124570c2ee30SGeorge Karpenkov V.setCount(V.getCount() - ACnt);
124670c2ee30SGeorge Karpenkov V.setAutoreleaseCount(0);
124770c2ee30SGeorge Karpenkov }
124870c2ee30SGeorge Karpenkov return setRefBinding(state, Sym, V);
124970c2ee30SGeorge Karpenkov }
125070c2ee30SGeorge Karpenkov
125170c2ee30SGeorge Karpenkov // HACK: Ignore retain-count issues on values accessed through ivars,
125270c2ee30SGeorge Karpenkov // because of cases like this:
125370c2ee30SGeorge Karpenkov // [_contentView retain];
125470c2ee30SGeorge Karpenkov // [_contentView removeFromSuperview];
125570c2ee30SGeorge Karpenkov // [self addSubview:_contentView]; // invalidates 'self'
125670c2ee30SGeorge Karpenkov // [_contentView release];
125770c2ee30SGeorge Karpenkov if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
125870c2ee30SGeorge Karpenkov return state;
125970c2ee30SGeorge Karpenkov
126070c2ee30SGeorge Karpenkov // Woah! More autorelease counts then retain counts left.
126170c2ee30SGeorge Karpenkov // Emit hard error.
126270c2ee30SGeorge Karpenkov V = V ^ RefVal::ErrorOverAutorelease;
126370c2ee30SGeorge Karpenkov state = setRefBinding(state, Sym, V);
126470c2ee30SGeorge Karpenkov
126570c2ee30SGeorge Karpenkov ExplodedNode *N = Ctx.generateSink(state, Pred, Tag);
126670c2ee30SGeorge Karpenkov if (N) {
126770c2ee30SGeorge Karpenkov SmallString<128> sbuf;
126870c2ee30SGeorge Karpenkov llvm::raw_svector_ostream os(sbuf);
126970c2ee30SGeorge Karpenkov os << "Object was autoreleased ";
127070c2ee30SGeorge Karpenkov if (V.getAutoreleaseCount() > 1)
127170c2ee30SGeorge Karpenkov os << V.getAutoreleaseCount() << " times but the object ";
127270c2ee30SGeorge Karpenkov else
127370c2ee30SGeorge Karpenkov os << "but ";
127470c2ee30SGeorge Karpenkov os << "has a +" << V.getCount() << " retain count";
127570c2ee30SGeorge Karpenkov
127670c2ee30SGeorge Karpenkov const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
12775192783bSKirstóf Umann auto R = std::make_unique<RefCountReport>(*OverAutorelease, LOpts, N, Sym,
1278717c4c0eSGeorge Karpenkov os.str());
127904553e53SGeorge Karpenkov Ctx.emitReport(std::move(R));
128070c2ee30SGeorge Karpenkov }
128170c2ee30SGeorge Karpenkov
128270c2ee30SGeorge Karpenkov return nullptr;
128370c2ee30SGeorge Karpenkov }
128470c2ee30SGeorge Karpenkov
128570c2ee30SGeorge Karpenkov ProgramStateRef
handleSymbolDeath(ProgramStateRef state,SymbolRef sid,RefVal V,SmallVectorImpl<SymbolRef> & Leaked) const128670c2ee30SGeorge Karpenkov RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
128770c2ee30SGeorge Karpenkov SymbolRef sid, RefVal V,
128870c2ee30SGeorge Karpenkov SmallVectorImpl<SymbolRef> &Leaked) const {
128970c2ee30SGeorge Karpenkov bool hasLeak;
129070c2ee30SGeorge Karpenkov
129170c2ee30SGeorge Karpenkov // HACK: Ignore retain-count issues on values accessed through ivars,
129270c2ee30SGeorge Karpenkov // because of cases like this:
129370c2ee30SGeorge Karpenkov // [_contentView retain];
129470c2ee30SGeorge Karpenkov // [_contentView removeFromSuperview];
129570c2ee30SGeorge Karpenkov // [self addSubview:_contentView]; // invalidates 'self'
129670c2ee30SGeorge Karpenkov // [_contentView release];
129770c2ee30SGeorge Karpenkov if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
129870c2ee30SGeorge Karpenkov hasLeak = false;
129970c2ee30SGeorge Karpenkov else if (V.isOwned())
130070c2ee30SGeorge Karpenkov hasLeak = true;
130170c2ee30SGeorge Karpenkov else if (V.isNotOwned() || V.isReturnedOwned())
130270c2ee30SGeorge Karpenkov hasLeak = (V.getCount() > 0);
130370c2ee30SGeorge Karpenkov else
130470c2ee30SGeorge Karpenkov hasLeak = false;
130570c2ee30SGeorge Karpenkov
130670c2ee30SGeorge Karpenkov if (!hasLeak)
130770c2ee30SGeorge Karpenkov return removeRefBinding(state, sid);
130870c2ee30SGeorge Karpenkov
130970c2ee30SGeorge Karpenkov Leaked.push_back(sid);
131070c2ee30SGeorge Karpenkov return setRefBinding(state, sid, V ^ RefVal::ErrorLeak);
131170c2ee30SGeorge Karpenkov }
131270c2ee30SGeorge Karpenkov
131370c2ee30SGeorge Karpenkov ExplodedNode *
processLeaks(ProgramStateRef state,SmallVectorImpl<SymbolRef> & Leaked,CheckerContext & Ctx,ExplodedNode * Pred) const131470c2ee30SGeorge Karpenkov RetainCountChecker::processLeaks(ProgramStateRef state,
131570c2ee30SGeorge Karpenkov SmallVectorImpl<SymbolRef> &Leaked,
131670c2ee30SGeorge Karpenkov CheckerContext &Ctx,
131770c2ee30SGeorge Karpenkov ExplodedNode *Pred) const {
131870c2ee30SGeorge Karpenkov // Generate an intermediate node representing the leak point.
131970c2ee30SGeorge Karpenkov ExplodedNode *N = Ctx.addTransition(state, Pred);
1320f153cdfbSGeorge Karpenkov const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
132170c2ee30SGeorge Karpenkov
132270c2ee30SGeorge Karpenkov if (N) {
1323f153cdfbSGeorge Karpenkov for (SymbolRef L : Leaked) {
13245192783bSKirstóf Umann const RefCountBug &BT = Pred ? *LeakWithinFunction : *LeakAtReturn;
13252b3d49b6SJonas Devlieghere Ctx.emitReport(std::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx));
132670c2ee30SGeorge Karpenkov }
132770c2ee30SGeorge Karpenkov }
132870c2ee30SGeorge Karpenkov
132970c2ee30SGeorge Karpenkov return N;
133070c2ee30SGeorge Karpenkov }
133170c2ee30SGeorge Karpenkov
checkBeginFunction(CheckerContext & Ctx) const133270c2ee30SGeorge Karpenkov void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
133370c2ee30SGeorge Karpenkov if (!Ctx.inTopFrame())
133470c2ee30SGeorge Karpenkov return;
133570c2ee30SGeorge Karpenkov
1336c4d6b93dSGeorge Karpenkov RetainSummaryManager &SmrMgr = getSummaryManager(Ctx);
133770c2ee30SGeorge Karpenkov const LocationContext *LCtx = Ctx.getLocationContext();
133877eae6d4SGeorge Karpenkov const Decl *D = LCtx->getDecl();
133977eae6d4SGeorge Karpenkov Optional<AnyCall> C = AnyCall::forDecl(D);
134070c2ee30SGeorge Karpenkov
134177eae6d4SGeorge Karpenkov if (!C || SmrMgr.isTrustedReferenceCountImplementation(D))
134270c2ee30SGeorge Karpenkov return;
134370c2ee30SGeorge Karpenkov
134470c2ee30SGeorge Karpenkov ProgramStateRef state = Ctx.getState();
134577eae6d4SGeorge Karpenkov const RetainSummary *FunctionSummary = SmrMgr.getSummary(*C);
134670c2ee30SGeorge Karpenkov ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
134770c2ee30SGeorge Karpenkov
134877eae6d4SGeorge Karpenkov for (unsigned idx = 0, e = C->param_size(); idx != e; ++idx) {
134977eae6d4SGeorge Karpenkov const ParmVarDecl *Param = C->parameters()[idx];
135070c2ee30SGeorge Karpenkov SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol();
135170c2ee30SGeorge Karpenkov
135270c2ee30SGeorge Karpenkov QualType Ty = Param->getType();
135370c2ee30SGeorge Karpenkov const ArgEffect *AE = CalleeSideArgEffects.lookup(idx);
1354d37ff4e8SGeorge Karpenkov if (AE) {
1355d37ff4e8SGeorge Karpenkov ObjKind K = AE->getObjKind();
1356d37ff4e8SGeorge Karpenkov if (K == ObjKind::Generalized || K == ObjKind::OS ||
1357d37ff4e8SGeorge Karpenkov (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) {
1358d37ff4e8SGeorge Karpenkov RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(K, Ty)
1359d37ff4e8SGeorge Karpenkov : RefVal::makeNotOwned(K, Ty);
1360d37ff4e8SGeorge Karpenkov state = setRefBinding(state, Sym, NewVal);
1361d37ff4e8SGeorge Karpenkov }
136270c2ee30SGeorge Karpenkov }
136370c2ee30SGeorge Karpenkov }
136470c2ee30SGeorge Karpenkov
136570c2ee30SGeorge Karpenkov Ctx.addTransition(state);
136670c2ee30SGeorge Karpenkov }
136770c2ee30SGeorge Karpenkov
checkEndFunction(const ReturnStmt * RS,CheckerContext & Ctx) const136870c2ee30SGeorge Karpenkov void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
136970c2ee30SGeorge Karpenkov CheckerContext &Ctx) const {
137004553e53SGeorge Karpenkov ExplodedNode *Pred = processReturn(RS, Ctx);
137104553e53SGeorge Karpenkov
137204553e53SGeorge Karpenkov // Created state cached out.
137304553e53SGeorge Karpenkov if (!Pred) {
137404553e53SGeorge Karpenkov return;
137504553e53SGeorge Karpenkov }
137604553e53SGeorge Karpenkov
137704553e53SGeorge Karpenkov ProgramStateRef state = Pred->getState();
137870c2ee30SGeorge Karpenkov RefBindingsTy B = state->get<RefBindings>();
137970c2ee30SGeorge Karpenkov
138070c2ee30SGeorge Karpenkov // Don't process anything within synthesized bodies.
138170c2ee30SGeorge Karpenkov const LocationContext *LCtx = Pred->getLocationContext();
138270c2ee30SGeorge Karpenkov if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) {
138370c2ee30SGeorge Karpenkov assert(!LCtx->inTopFrame());
138470c2ee30SGeorge Karpenkov return;
138570c2ee30SGeorge Karpenkov }
138670c2ee30SGeorge Karpenkov
138703391514SGeorge Karpenkov for (auto &I : B) {
138870c2ee30SGeorge Karpenkov state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx,
138903391514SGeorge Karpenkov I.first, I.second);
139070c2ee30SGeorge Karpenkov if (!state)
139170c2ee30SGeorge Karpenkov return;
139270c2ee30SGeorge Karpenkov }
139370c2ee30SGeorge Karpenkov
139470c2ee30SGeorge Karpenkov // If the current LocationContext has a parent, don't check for leaks.
139570c2ee30SGeorge Karpenkov // We will do that later.
139670c2ee30SGeorge Karpenkov // FIXME: we should instead check for imbalances of the retain/releases,
139770c2ee30SGeorge Karpenkov // and suggest annotations.
139870c2ee30SGeorge Karpenkov if (LCtx->getParent())
139970c2ee30SGeorge Karpenkov return;
140070c2ee30SGeorge Karpenkov
140170c2ee30SGeorge Karpenkov B = state->get<RefBindings>();
140270c2ee30SGeorge Karpenkov SmallVector<SymbolRef, 10> Leaked;
140370c2ee30SGeorge Karpenkov
140403391514SGeorge Karpenkov for (auto &I : B)
140503391514SGeorge Karpenkov state = handleSymbolDeath(state, I.first, I.second, Leaked);
140670c2ee30SGeorge Karpenkov
140770c2ee30SGeorge Karpenkov processLeaks(state, Leaked, Ctx, Pred);
140870c2ee30SGeorge Karpenkov }
140970c2ee30SGeorge Karpenkov
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const141070c2ee30SGeorge Karpenkov void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
141170c2ee30SGeorge Karpenkov CheckerContext &C) const {
141270c2ee30SGeorge Karpenkov ExplodedNode *Pred = C.getPredecessor();
141370c2ee30SGeorge Karpenkov
141470c2ee30SGeorge Karpenkov ProgramStateRef state = C.getState();
141570c2ee30SGeorge Karpenkov SmallVector<SymbolRef, 10> Leaked;
141670c2ee30SGeorge Karpenkov
141770c2ee30SGeorge Karpenkov // Update counts from autorelease pools
1418bbc6d682SArtem Dergachev for (const auto &I: state->get<RefBindings>()) {
1419bbc6d682SArtem Dergachev SymbolRef Sym = I.first;
1420bbc6d682SArtem Dergachev if (SymReaper.isDead(Sym)) {
142165b4d7ddSArtem Dergachev static CheckerProgramPointTag Tag(this, "DeadSymbolAutorelease");
1422bbc6d682SArtem Dergachev const RefVal &V = I.second;
1423bbc6d682SArtem Dergachev state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, V);
142470c2ee30SGeorge Karpenkov if (!state)
142570c2ee30SGeorge Karpenkov return;
142670c2ee30SGeorge Karpenkov
142770c2ee30SGeorge Karpenkov // Fetch the new reference count from the state, and use it to handle
142870c2ee30SGeorge Karpenkov // this symbol.
1429bbc6d682SArtem Dergachev state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked);
143070c2ee30SGeorge Karpenkov }
143170c2ee30SGeorge Karpenkov }
143270c2ee30SGeorge Karpenkov
143370c2ee30SGeorge Karpenkov if (Leaked.empty()) {
143470c2ee30SGeorge Karpenkov C.addTransition(state);
143570c2ee30SGeorge Karpenkov return;
143670c2ee30SGeorge Karpenkov }
143770c2ee30SGeorge Karpenkov
143870c2ee30SGeorge Karpenkov Pred = processLeaks(state, Leaked, C, Pred);
143970c2ee30SGeorge Karpenkov
144070c2ee30SGeorge Karpenkov // Did we cache out?
144170c2ee30SGeorge Karpenkov if (!Pred)
144270c2ee30SGeorge Karpenkov return;
144370c2ee30SGeorge Karpenkov
144470c2ee30SGeorge Karpenkov // Now generate a new node that nukes the old bindings.
144570c2ee30SGeorge Karpenkov // The only bindings left at this point are the leaked symbols.
144670c2ee30SGeorge Karpenkov RefBindingsTy::Factory &F = state->get_context<RefBindings>();
1447f153cdfbSGeorge Karpenkov RefBindingsTy B = state->get<RefBindings>();
144870c2ee30SGeorge Karpenkov
1449f153cdfbSGeorge Karpenkov for (SymbolRef L : Leaked)
1450f153cdfbSGeorge Karpenkov B = F.remove(B, L);
145170c2ee30SGeorge Karpenkov
145270c2ee30SGeorge Karpenkov state = state->set<RefBindings>(B);
145370c2ee30SGeorge Karpenkov C.addTransition(state, Pred);
145470c2ee30SGeorge Karpenkov }
145570c2ee30SGeorge Karpenkov
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const145670c2ee30SGeorge Karpenkov void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
145770c2ee30SGeorge Karpenkov const char *NL, const char *Sep) const {
145870c2ee30SGeorge Karpenkov
145970c2ee30SGeorge Karpenkov RefBindingsTy B = State->get<RefBindings>();
146070c2ee30SGeorge Karpenkov
146170c2ee30SGeorge Karpenkov if (B.isEmpty())
146270c2ee30SGeorge Karpenkov return;
146370c2ee30SGeorge Karpenkov
146470c2ee30SGeorge Karpenkov Out << Sep << NL;
146570c2ee30SGeorge Karpenkov
146603391514SGeorge Karpenkov for (auto &I : B) {
146703391514SGeorge Karpenkov Out << I.first << " : ";
146803391514SGeorge Karpenkov I.second.print(Out);
146970c2ee30SGeorge Karpenkov Out << NL;
147070c2ee30SGeorge Karpenkov }
147170c2ee30SGeorge Karpenkov }
147270c2ee30SGeorge Karpenkov
147370c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
147470c2ee30SGeorge Karpenkov // Checker registration.
147570c2ee30SGeorge Karpenkov //===----------------------------------------------------------------------===//
147670c2ee30SGeorge Karpenkov
14775192783bSKirstóf Umann std::unique_ptr<CheckerProgramPointTag> RetainCountChecker::DeallocSentTag;
14785192783bSKirstóf Umann std::unique_ptr<CheckerProgramPointTag> RetainCountChecker::CastFailTag;
14795192783bSKirstóf Umann
registerRetainCountBase(CheckerManager & Mgr)14808fd74ebfSKristof Umann void ento::registerRetainCountBase(CheckerManager &Mgr) {
14815192783bSKirstóf Umann auto *Chk = Mgr.registerChecker<RetainCountChecker>();
14825192783bSKirstóf Umann Chk->DeallocSentTag =
14835192783bSKirstóf Umann std::make_unique<CheckerProgramPointTag>(Chk, "DeallocSent");
14845192783bSKirstóf Umann Chk->CastFailTag =
14855192783bSKirstóf Umann std::make_unique<CheckerProgramPointTag>(Chk, "DynamicCastFail");
14868fd74ebfSKristof Umann }
14878fd74ebfSKristof Umann
shouldRegisterRetainCountBase(const CheckerManager & mgr)1488bda3dd0dSKirstóf Umann bool ento::shouldRegisterRetainCountBase(const CheckerManager &mgr) {
14898fd74ebfSKristof Umann return true;
14908fd74ebfSKristof Umann }
registerRetainCountChecker(CheckerManager & Mgr)149170c2ee30SGeorge Karpenkov void ento::registerRetainCountChecker(CheckerManager &Mgr) {
1492204bf2bbSKristof Umann auto *Chk = Mgr.getChecker<RetainCountChecker>();
149327db3307SGeorge Karpenkov Chk->TrackObjCAndCFObjects = true;
14946f543184SKirstóf Umann Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
14956f543184SKirstóf Umann Mgr.getCurrentCheckerName(), "TrackNSCFStartParam");
14965192783bSKirstóf Umann
14975192783bSKirstóf Umann #define INIT_BUGTYPE(KIND) \
14985192783bSKirstóf Umann Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \
14995192783bSKirstóf Umann RefCountBug::KIND);
15005192783bSKirstóf Umann // TODO: Ideally, we should have a checker for each of these bug types.
15015192783bSKirstóf Umann INIT_BUGTYPE(UseAfterRelease)
15025192783bSKirstóf Umann INIT_BUGTYPE(ReleaseNotOwned)
15035192783bSKirstóf Umann INIT_BUGTYPE(DeallocNotOwned)
15045192783bSKirstóf Umann INIT_BUGTYPE(FreeNotOwned)
15055192783bSKirstóf Umann INIT_BUGTYPE(OverAutorelease)
15065192783bSKirstóf Umann INIT_BUGTYPE(ReturnNotOwnedForOwned)
15075192783bSKirstóf Umann INIT_BUGTYPE(LeakWithinFunction)
15085192783bSKirstóf Umann INIT_BUGTYPE(LeakAtReturn)
15095192783bSKirstóf Umann #undef INIT_BUGTYPE
151027db3307SGeorge Karpenkov }
1511c83b0ddaSKristof Umann
shouldRegisterRetainCountChecker(const CheckerManager & mgr)1512bda3dd0dSKirstóf Umann bool ento::shouldRegisterRetainCountChecker(const CheckerManager &mgr) {
1513058a7a45SKristof Umann return true;
1514058a7a45SKristof Umann }
1515058a7a45SKristof Umann
registerOSObjectRetainCountChecker(CheckerManager & Mgr)151627db3307SGeorge Karpenkov void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
1517204bf2bbSKristof Umann auto *Chk = Mgr.getChecker<RetainCountChecker>();
151827db3307SGeorge Karpenkov Chk->TrackOSObjects = true;
15195192783bSKirstóf Umann
15205192783bSKirstóf Umann // FIXME: We want bug reports to always have the same checker name associated
15215192783bSKirstóf Umann // with them, yet here, if RetainCountChecker is disabled but
15225192783bSKirstóf Umann // OSObjectRetainCountChecker is enabled, the checker names will be different.
15235192783bSKirstóf Umann // This hack will make it so that the checker name depends on which checker is
15245192783bSKirstóf Umann // enabled rather than on the registration order.
15255192783bSKirstóf Umann // For the most part, we want **non-hidden checkers** to be associated with
15265192783bSKirstóf Umann // diagnostics, and **hidden checker options** with the fine-tuning of
15275192783bSKirstóf Umann // modeling. Following this logic, OSObjectRetainCountChecker should be the
15285192783bSKirstóf Umann // latter, but we can't just remove it for backward compatibility reasons.
15295192783bSKirstóf Umann #define LAZY_INIT_BUGTYPE(KIND) \
15305192783bSKirstóf Umann if (!Chk->KIND) \
15315192783bSKirstóf Umann Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \
15325192783bSKirstóf Umann RefCountBug::KIND);
15335192783bSKirstóf Umann LAZY_INIT_BUGTYPE(UseAfterRelease)
15345192783bSKirstóf Umann LAZY_INIT_BUGTYPE(ReleaseNotOwned)
15355192783bSKirstóf Umann LAZY_INIT_BUGTYPE(DeallocNotOwned)
15365192783bSKirstóf Umann LAZY_INIT_BUGTYPE(FreeNotOwned)
15375192783bSKirstóf Umann LAZY_INIT_BUGTYPE(OverAutorelease)
15385192783bSKirstóf Umann LAZY_INIT_BUGTYPE(ReturnNotOwnedForOwned)
15395192783bSKirstóf Umann LAZY_INIT_BUGTYPE(LeakWithinFunction)
15405192783bSKirstóf Umann LAZY_INIT_BUGTYPE(LeakAtReturn)
15415192783bSKirstóf Umann #undef LAZY_INIT_BUGTYPE
154270c2ee30SGeorge Karpenkov }
1543058a7a45SKristof Umann
shouldRegisterOSObjectRetainCountChecker(const CheckerManager & mgr)1544bda3dd0dSKirstóf Umann bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &mgr) {
1545058a7a45SKristof Umann return true;
1546058a7a45SKristof Umann }
1547