diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index cdda327d6250..1ccf38295372 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -1,1508 +1,1510 @@ //==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the methods for RetainCountChecker, which implements // a reference count checker for Core Foundation and Cocoa on (Mac OS X). // //===----------------------------------------------------------------------===// #include "RetainCountChecker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; using namespace retaincountchecker; using llvm::StrInStrNoCase; REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal) namespace clang { namespace ento { namespace retaincountchecker { const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->get(Sym); } } // end namespace retaincountchecker } // end namespace ento } // end namespace clang static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, RefVal Val) { assert(Sym != nullptr); return State->set(Sym, Val); } static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->remove(Sym); } void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) Out << "Tracked " << T.getAsString() << " | "; switch (getKind()) { default: llvm_unreachable("Invalid RefVal kind"); case Owned: { Out << "Owned"; unsigned cnt = getCount(); if (cnt) Out << " (+ " << cnt << ")"; break; } case NotOwned: { Out << "NotOwned"; unsigned cnt = getCount(); if (cnt) Out << " (+ " << cnt << ")"; break; } case ReturnedOwned: { Out << "ReturnedOwned"; unsigned cnt = getCount(); if (cnt) Out << " (+ " << cnt << ")"; break; } case ReturnedNotOwned: { Out << "ReturnedNotOwned"; unsigned cnt = getCount(); if (cnt) Out << " (+ " << cnt << ")"; break; } case Released: Out << "Released"; break; case ErrorDeallocNotOwned: Out << "-dealloc (not-owned)"; break; case ErrorLeak: Out << "Leaked"; break; case ErrorLeakReturned: Out << "Leaked (Bad naming)"; break; case ErrorUseAfterRelease: Out << "Use-After-Release [ERROR]"; break; case ErrorReleaseNotOwned: Out << "Release of Not-Owned [ERROR]"; break; case RefVal::ErrorOverAutorelease: Out << "Over-autoreleased"; break; case RefVal::ErrorReturnedNotOwned: Out << "Non-owned object returned instead of owned"; break; } switch (getIvarAccessHistory()) { case IvarAccessHistory::None: break; case IvarAccessHistory::AccessedDirectly: Out << " [direct ivar access]"; break; case IvarAccessHistory::ReleasedAfterDirectAccess: Out << " [released after direct ivar access]"; } if (ACnt) { Out << " [autorelease -" << ACnt << ']'; } } namespace { class StopTrackingCallback final : public SymbolVisitor { ProgramStateRef state; public: StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {} ProgramStateRef getState() const { return state; } bool VisitSymbol(SymbolRef sym) override { state = removeRefBinding(state, sym); return true; } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // Handle statements that may have an effect on refcounts. //===----------------------------------------------------------------------===// void RetainCountChecker::checkPostStmt(const BlockExpr *BE, CheckerContext &C) const { // Scan the BlockDecRefExprs for any object the retain count checker // may be tracking. if (!BE->getBlockDecl()->hasCaptures()) return; ProgramStateRef state = C.getState(); auto *R = cast(C.getSVal(BE).getAsRegion()); BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), E = R->referenced_vars_end(); if (I == E) return; // FIXME: For now we invalidate the tracking of all symbols passed to blocks // via captured variables, even though captured variables result in a copy // and in implicit increment/decrement of a retain count. SmallVector Regions; const LocationContext *LC = C.getLocationContext(); MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); for ( ; I != E; ++I) { const VarRegion *VR = I.getCapturedRegion(); if (VR->getSuperRegion() == R) { VR = MemMgr.getVarRegion(VR->getDecl(), LC); } Regions.push_back(VR); } state = state->scanReachableSymbols(Regions).getState(); C.addTransition(state); } void RetainCountChecker::checkPostStmt(const CastExpr *CE, CheckerContext &C) const { const ObjCBridgedCastExpr *BE = dyn_cast(CE); if (!BE) return; QualType QT = CE->getType(); ObjKind K; if (QT->isObjCObjectPointerType()) { K = ObjKind::ObjC; } else { K = ObjKind::CF; } ArgEffect AE = ArgEffect(IncRef, K); switch (BE->getBridgeKind()) { case OBC_Bridge: // Do nothing. return; case OBC_BridgeRetained: AE = AE.withKind(IncRef); break; case OBC_BridgeTransfer: AE = AE.withKind(DecRefBridgedTransferred); break; } ProgramStateRef state = C.getState(); SymbolRef Sym = C.getSVal(CE).getAsLocSymbol(); if (!Sym) return; const RefVal* T = getRefBinding(state, Sym); if (!T) return; RefVal::Kind hasErr = (RefVal::Kind) 0; state = updateSymbol(state, Sym, *T, AE, hasErr, C); if (hasErr) { // FIXME: If we get an error during a bridge cast, should we report it? return; } C.addTransition(state); } void RetainCountChecker::processObjCLiterals(CheckerContext &C, const Expr *Ex) const { ProgramStateRef state = C.getState(); const ExplodedNode *pred = C.getPredecessor(); for (const Stmt *Child : Ex->children()) { SVal V = pred->getSVal(Child); if (SymbolRef sym = V.getAsSymbol()) if (const RefVal* T = getRefBinding(state, sym)) { RefVal::Kind hasErr = (RefVal::Kind) 0; state = updateSymbol(state, sym, *T, ArgEffect(MayEscape, ObjKind::ObjC), hasErr, C); if (hasErr) { processNonLeakError(state, Child->getSourceRange(), hasErr, sym, C); return; } } } // Return the object as autoreleased. // RetEffect RE = RetEffect::MakeNotOwned(ObjKind::ObjC); if (SymbolRef sym = state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { QualType ResultTy = Ex->getType(); state = setRefBinding(state, sym, RefVal::makeNotOwned(ObjKind::ObjC, ResultTy)); } C.addTransition(state); } void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const { // Apply the 'MayEscape' to all values. processObjCLiterals(C, AL); } void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const { // Apply the 'MayEscape' to all keys and values. processObjCLiterals(C, DL); } void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, CheckerContext &C) const { const ExplodedNode *Pred = C.getPredecessor(); ProgramStateRef State = Pred->getState(); if (SymbolRef Sym = Pred->getSVal(Ex).getAsSymbol()) { QualType ResultTy = Ex->getType(); State = setRefBinding(State, Sym, RefVal::makeNotOwned(ObjKind::ObjC, ResultTy)); } C.addTransition(State); } void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const { Optional IVarLoc = C.getSVal(IRE).getAs(); if (!IVarLoc) return; ProgramStateRef State = C.getState(); SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol(); if (!Sym || !dyn_cast_or_null(Sym->getOriginRegion())) return; // Accessing an ivar directly is unusual. If we've done that, be more // forgiving about what the surrounding code is allowed to do. QualType Ty = Sym->getType(); ObjKind Kind; if (Ty->isObjCRetainableType()) Kind = ObjKind::ObjC; else if (coreFoundation::isCFObjectRef(Ty)) Kind = ObjKind::CF; else return; // If the value is already known to be nil, don't bother tracking it. ConstraintManager &CMgr = State->getConstraintManager(); if (CMgr.isNull(State, Sym).isConstrainedTrue()) return; if (const RefVal *RV = getRefBinding(State, Sym)) { // If we've seen this symbol before, or we're only seeing it now because // of something the analyzer has synthesized, don't do anything. if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None || isSynthesizedAccessor(C.getStackFrame())) { return; } // Note that this value has been loaded from an ivar. C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess())); return; } RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty); // In a synthesized accessor, the effective retain count is +0. if (isSynthesizedAccessor(C.getStackFrame())) { C.addTransition(setRefBinding(State, Sym, PlusZero)); return; } State = setRefBinding(State, Sym, PlusZero.withIvarAccess()); C.addTransition(State); } static bool isReceiverUnconsumedSelf(const CallEvent &Call) { if (const auto *MC = dyn_cast(&Call)) { // Check if the message is not consumed, we know it will not be used in // an assignment, ex: "self = [super init]". return MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper() && !Call.getLocationContext() ->getAnalysisDeclContext() ->getParentMap() .isConsumedExpr(Call.getOriginExpr()); } return false; } const static RetainSummary *getSummary(RetainSummaryManager &Summaries, const CallEvent &Call, QualType ReceiverType) { const Expr *CE = Call.getOriginExpr(); AnyCall C = CE ? *AnyCall::forExpr(CE) : AnyCall(cast(Call.getDecl())); return Summaries.getSummary(C, Call.hasNonZeroCallbackArg(), isReceiverUnconsumedSelf(Call), ReceiverType); } void RetainCountChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { RetainSummaryManager &Summaries = getSummaryManager(C); // Leave null if no receiver. QualType ReceiverType; if (const auto *MC = dyn_cast(&Call)) { if (MC->isInstanceMessage()) { SVal ReceiverV = MC->getReceiverSVal(); if (SymbolRef Sym = ReceiverV.getAsLocSymbol()) if (const RefVal *T = getRefBinding(C.getState(), Sym)) ReceiverType = T->getType(); } } const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType); if (C.wasInlined) { processSummaryOfInlined(*Summ, Call, C); return; } checkSummary(*Summ, Call, C); } /// GetReturnType - Used to get the return type of a message expression or /// function call with the intention of affixing that type to a tracked symbol. /// While the return type can be queried directly from RetEx, when /// invoking class methods we augment to the return type to be that of /// a pointer to the class (as opposed it just being id). // FIXME: We may be able to do this with related result types instead. // This function is probably overestimating. static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { QualType RetTy = RetE->getType(); // If RetE is not a message expression just return its type. // If RetE is a message expression, return its types if it is something /// more specific than id. if (const ObjCMessageExpr *ME = dyn_cast(RetE)) if (const ObjCObjectPointerType *PT = RetTy->getAs()) if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || PT->isObjCClassType()) { // At this point we know the return type of the message expression is // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this // is a call to a class method whose type we can resolve. In such // cases, promote the return type to XXX* (where XXX is the class). const ObjCInterfaceDecl *D = ME->getReceiverInterface(); return !D ? RetTy : Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D)); } return RetTy; } static Optional refValFromRetEffect(RetEffect RE, QualType ResultTy) { if (RE.isOwned()) { return RefVal::makeOwned(RE.getObjKind(), ResultTy); } else if (RE.notOwned()) { return RefVal::makeNotOwned(RE.getObjKind(), ResultTy); } return None; } static bool isPointerToObject(QualType QT) { QualType PT = QT->getPointeeType(); if (!PT.isNull()) if (PT->getAsCXXRecordDecl()) return true; return false; } /// Whether the tracked value should be escaped on a given call. /// OSObjects are escaped when passed to void * / etc. static bool shouldEscapeOSArgumentOnCall(const CallEvent &CE, unsigned ArgIdx, const RefVal *TrackedValue) { if (TrackedValue->getObjKind() != ObjKind::OS) return false; if (ArgIdx >= CE.parameters().size()) return false; return !isPointerToObject(CE.parameters()[ArgIdx]->getType()); } // We don't always get the exact modeling of the function with regards to the // retain count checker even when the function is inlined. For example, we need // to stop tracking the symbols which were marked with StopTrackingHard. void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, const CallEvent &CallOrMsg, CheckerContext &C) const { ProgramStateRef state = C.getState(); // Evaluate the effect of the arguments. for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { SVal V = CallOrMsg.getArgSVal(idx); if (SymbolRef Sym = V.getAsLocSymbol()) { bool ShouldRemoveBinding = Summ.getArg(idx).getKind() == StopTrackingHard; if (const RefVal *T = getRefBinding(state, Sym)) if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T)) ShouldRemoveBinding = true; if (ShouldRemoveBinding) state = removeRefBinding(state, Sym); } } // Evaluate the effect on the message receiver. if (const auto *MsgInvocation = dyn_cast(&CallOrMsg)) { if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { if (Summ.getReceiverEffect().getKind() == StopTrackingHard) { state = removeRefBinding(state, Sym); } } } // Consult the summary for the return value. RetEffect RE = Summ.getRetEffect(); if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) { if (RE.getKind() == RetEffect::NoRetHard) state = removeRefBinding(state, Sym); } C.addTransition(state); } static bool isSmartPtrField(const MemRegion *MR) { const auto *TR = dyn_cast( cast(MR)->getSuperRegion()); return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType()); } /// A value escapes in these possible cases: /// /// - binding to something that is not a memory region. /// - binding to a memregion that does not have stack storage /// - binding to a variable that has a destructor attached using CleanupAttr /// /// We do not currently model what happens when a symbol is /// assigned to a struct field, unless it is a known smart pointer /// implementation, about which we know that it is inlined. /// FIXME: This could definitely be improved upon. static bool shouldEscapeRegion(const MemRegion *R) { if (isSmartPtrField(R)) return false; const auto *VR = dyn_cast(R); if (!R->hasStackStorage() || !VR) return true; const VarDecl *VD = VR->getDecl(); if (!VD->hasAttr()) return false; // CleanupAttr attaches destructors, which cause escaping. return true; } static SmallVector updateOutParameters(ProgramStateRef State, const RetainSummary &Summ, const CallEvent &CE) { SVal L = CE.getReturnValue(); // Splitting is required to support out parameters, // as out parameters might be created only on the "success" branch. // We want to avoid eagerly splitting unless out parameters are actually // needed. bool SplitNecessary = false; for (auto &P : Summ.getArgEffects()) if (P.second.getKind() == RetainedOutParameterOnNonZero || P.second.getKind() == RetainedOutParameterOnZero) SplitNecessary = true; ProgramStateRef AssumeNonZeroReturn = State; ProgramStateRef AssumeZeroReturn = State; if (SplitNecessary) { if (auto DL = L.getAs()) { AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true); AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false); } } for (unsigned idx = 0, e = CE.getNumArgs(); idx != e; ++idx) { SVal ArgVal = CE.getArgSVal(idx); ArgEffect AE = Summ.getArg(idx); auto *ArgRegion = dyn_cast_or_null(ArgVal.getAsRegion()); if (!ArgRegion) continue; QualType PointeeTy = ArgRegion->getValueType(); SVal PointeeVal = State->getSVal(ArgRegion); SymbolRef Pointee = PointeeVal.getAsLocSymbol(); if (!Pointee) continue; if (shouldEscapeRegion(ArgRegion)) continue; auto makeNotOwnedParameter = [&](ProgramStateRef St) { return setRefBinding(St, Pointee, RefVal::makeNotOwned(AE.getObjKind(), PointeeTy)); }; auto makeOwnedParameter = [&](ProgramStateRef St) { return setRefBinding(St, Pointee, RefVal::makeOwned(ObjKind::OS, PointeeTy)); }; switch (AE.getKind()) { case UnretainedOutParameter: AssumeNonZeroReturn = makeNotOwnedParameter(AssumeNonZeroReturn); AssumeZeroReturn = makeNotOwnedParameter(AssumeZeroReturn); break; case RetainedOutParameter: AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn); AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn); break; case RetainedOutParameterOnNonZero: AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn); break; case RetainedOutParameterOnZero: AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn); break; default: break; } } if (SplitNecessary) { return {AssumeNonZeroReturn, AssumeZeroReturn}; } else { assert(AssumeZeroReturn == AssumeNonZeroReturn); return {AssumeZeroReturn}; } } void RetainCountChecker::checkSummary(const RetainSummary &Summ, const CallEvent &CallOrMsg, CheckerContext &C) const { ProgramStateRef state = C.getState(); // Evaluate the effect of the arguments. RefVal::Kind hasErr = (RefVal::Kind) 0; SourceRange ErrorRange; SymbolRef ErrorSym = nullptr; // Helper tag for providing diagnostics: indicate whether dealloc was sent // at this location. bool DeallocSent = false; for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { SVal V = CallOrMsg.getArgSVal(idx); ArgEffect Effect = Summ.getArg(idx); if (SymbolRef Sym = V.getAsLocSymbol()) { if (const RefVal *T = getRefBinding(state, Sym)) { if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T)) Effect = ArgEffect(StopTrackingHard, ObjKind::OS); state = updateSymbol(state, Sym, *T, Effect, hasErr, C); if (hasErr) { ErrorRange = CallOrMsg.getArgSourceRange(idx); ErrorSym = Sym; break; } else if (Effect.getKind() == Dealloc) { DeallocSent = true; } } } } // Evaluate the effect on the message receiver / `this` argument. bool ReceiverIsTracked = false; if (!hasErr) { if (const auto *MsgInvocation = dyn_cast(&CallOrMsg)) { if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { if (const RefVal *T = getRefBinding(state, Sym)) { ReceiverIsTracked = true; state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), hasErr, C); if (hasErr) { ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange(); ErrorSym = Sym; } else if (Summ.getReceiverEffect().getKind() == Dealloc) { DeallocSent = true; } } } } else if (const auto *MCall = dyn_cast(&CallOrMsg)) { if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) { if (const RefVal *T = getRefBinding(state, Sym)) { state = updateSymbol(state, Sym, *T, Summ.getThisEffect(), hasErr, C); if (hasErr) { ErrorRange = MCall->getOriginExpr()->getSourceRange(); ErrorSym = Sym; } } } } } // Process any errors. if (hasErr) { processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C); return; } // Consult the summary for the return value. RetEffect RE = Summ.getRetEffect(); if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { if (ReceiverIsTracked) RE = getSummaryManager(C).getObjAllocRetEffect(); else RE = RetEffect::MakeNoRet(); } if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) { QualType ResultTy = CallOrMsg.getResultType(); if (RE.notOwned()) { const Expr *Ex = CallOrMsg.getOriginExpr(); assert(Ex); ResultTy = GetReturnType(Ex, C.getASTContext()); } if (Optional updatedRefVal = refValFromRetEffect(RE, ResultTy)) state = setRefBinding(state, Sym, *updatedRefVal); } SmallVector Out = updateOutParameters(state, Summ, CallOrMsg); for (ProgramStateRef St : Out) { if (DeallocSent) { C.addTransition(St, C.getPredecessor(), &DeallocSentTag); } else { C.addTransition(St); } } } ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, RefVal V, ArgEffect AE, RefVal::Kind &hasErr, CheckerContext &C) const { bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount; if (AE.getObjKind() == ObjKind::ObjC && IgnoreRetainMsg) { switch (AE.getKind()) { default: break; case IncRef: AE = AE.withKind(DoNothing); break; case DecRef: AE = AE.withKind(DoNothing); break; case DecRefAndStopTrackingHard: AE = AE.withKind(StopTracking); break; } } // Handle all use-after-releases. if (V.getKind() == RefVal::Released) { V = V ^ RefVal::ErrorUseAfterRelease; hasErr = V.getKind(); return setRefBinding(state, sym, V); } switch (AE.getKind()) { case UnretainedOutParameter: case RetainedOutParameter: case RetainedOutParameterOnZero: case RetainedOutParameterOnNonZero: llvm_unreachable("Applies to pointer-to-pointer parameters, which should " "not have ref state."); case Dealloc: // NB. we only need to add a note in a non-error case. switch (V.getKind()) { default: llvm_unreachable("Invalid RefVal state for an explicit dealloc."); case RefVal::Owned: // The object immediately transitions to the released state. V = V ^ RefVal::Released; V.clearCounts(); return setRefBinding(state, sym, V); case RefVal::NotOwned: V = V ^ RefVal::ErrorDeallocNotOwned; hasErr = V.getKind(); break; } break; case MayEscape: if (V.getKind() == RefVal::Owned) { V = V ^ RefVal::NotOwned; break; } LLVM_FALLTHROUGH; case DoNothing: return state; case Autorelease: // Update the autorelease counts. V = V.autorelease(); break; case StopTracking: case StopTrackingHard: return removeRefBinding(state, sym); case IncRef: switch (V.getKind()) { default: llvm_unreachable("Invalid RefVal state for a retain."); case RefVal::Owned: case RefVal::NotOwned: V = V + 1; break; } break; case DecRef: case DecRefBridgedTransferred: case DecRefAndStopTrackingHard: switch (V.getKind()) { default: // case 'RefVal::Released' handled above. llvm_unreachable("Invalid RefVal state for a release."); case RefVal::Owned: assert(V.getCount() > 0); if (V.getCount() == 1) { if (AE.getKind() == DecRefBridgedTransferred || V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) V = V ^ RefVal::NotOwned; else V = V ^ RefVal::Released; } else if (AE.getKind() == DecRefAndStopTrackingHard) { return removeRefBinding(state, sym); } V = V - 1; break; case RefVal::NotOwned: if (V.getCount() > 0) { if (AE.getKind() == DecRefAndStopTrackingHard) return removeRefBinding(state, sym); V = V - 1; } else if (V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) { // Assume that the instance variable was holding on the object at // +1, and we just didn't know. if (AE.getKind() == DecRefAndStopTrackingHard) return removeRefBinding(state, sym); V = V.releaseViaIvar() ^ RefVal::Released; } else { V = V ^ RefVal::ErrorReleaseNotOwned; hasErr = V.getKind(); } break; } break; } return setRefBinding(state, sym, V); } const RefCountBug & RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind, SymbolRef Sym) const { switch (ErrorKind) { case RefVal::ErrorUseAfterRelease: return useAfterRelease; case RefVal::ErrorReleaseNotOwned: return releaseNotOwned; case RefVal::ErrorDeallocNotOwned: if (Sym->getType()->getPointeeCXXRecordDecl()) return freeNotOwned; return deallocNotOwned; default: llvm_unreachable("Unhandled error."); } } void RetainCountChecker::processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const { // HACK: Ignore retain-count issues on values accessed through ivars, // because of cases like this: // [_contentView retain]; // [_contentView removeFromSuperview]; // [self addSubview:_contentView]; // invalidates 'self' // [_contentView release]; if (const RefVal *RV = getRefBinding(St, Sym)) if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None) return; ExplodedNode *N = C.generateErrorNode(St); if (!N) return; auto report = llvm::make_unique( errorKindToBugKind(ErrorKind, Sym), C.getASTContext().getLangOpts(), N, Sym); report->addRange(ErrorRange); C.emitReport(std::move(report)); } //===----------------------------------------------------------------------===// // Handle the return values of retain-count-related functions. //===----------------------------------------------------------------------===// bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { ProgramStateRef state = C.getState(); const FunctionDecl *FD = C.getCalleeDecl(CE); if (!FD) return false; RetainSummaryManager &SmrMgr = getSummaryManager(C); QualType ResultTy = CE->getCallReturnType(C.getASTContext()); // See if the function has 'rc_ownership_trusted_implementation' // annotate attribute. If it does, we will not inline it. bool hasTrustedImplementationAnnotation = false; const LocationContext *LCtx = C.getLocationContext(); using BehaviorSummary = RetainSummaryManager::BehaviorSummary; Optional BSmr = SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation); // See if it's one of the specific functions we know how to eval. if (!BSmr) return false; // Bind the return value. if (BSmr == BehaviorSummary::Identity || BSmr == BehaviorSummary::IdentityOrZero || BSmr == BehaviorSummary::IdentityThis) { const Expr *BindReturnTo = (BSmr == BehaviorSummary::IdentityThis) ? cast(CE)->getImplicitObjectArgument() : CE->getArg(0); SVal RetVal = state->getSVal(BindReturnTo, LCtx); // If the receiver is unknown or the function has // 'rc_ownership_trusted_implementation' annotate attribute, conjure a // return value. // FIXME: this branch is very strange. if (RetVal.isUnknown() || (hasTrustedImplementationAnnotation && !ResultTy.isNull())) { SValBuilder &SVB = C.getSValBuilder(); RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount()); } // Bind the value. state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false); if (BSmr == BehaviorSummary::IdentityOrZero) { // Add a branch where the output is zero. ProgramStateRef NullOutputState = C.getState(); // Assume that output is zero on the other branch. NullOutputState = NullOutputState->BindExpr( CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); C.addTransition(NullOutputState, &CastFailTag); // And on the original branch assume that both input and // output are non-zero. if (auto L = RetVal.getAs()) state = state->assume(*L, /*Assumption=*/true); } } C.addTransition(state); return true; } ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S, CheckerContext &C) const { ExplodedNode *Pred = C.getPredecessor(); // Only adjust the reference count if this is the top-level call frame, // and not the result of inlining. In the future, we should do // better checking even for inlined calls, and see if they match // with their expected semantics (e.g., the method should return a retained // object, etc.). if (!C.inTopFrame()) return Pred; if (!S) return Pred; const Expr *RetE = S->getRetValue(); if (!RetE) return Pred; ProgramStateRef state = C.getState(); - SymbolRef Sym = - state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol(); + // We need to dig down to the symbolic base here because various + // custom allocators do sometimes return the symbol with an offset. + SymbolRef Sym = state->getSValAsScalarOrLoc(RetE, C.getLocationContext()) + .getAsLocSymbol(/*IncludeBaseRegions=*/true); if (!Sym) return Pred; // Get the reference count binding (if any). const RefVal *T = getRefBinding(state, Sym); if (!T) return Pred; // Change the reference count. RefVal X = *T; switch (X.getKind()) { case RefVal::Owned: { unsigned cnt = X.getCount(); assert(cnt > 0); X.setCount(cnt - 1); X = X ^ RefVal::ReturnedOwned; break; } case RefVal::NotOwned: { unsigned cnt = X.getCount(); if (cnt) { X.setCount(cnt - 1); X = X ^ RefVal::ReturnedOwned; } else { X = X ^ RefVal::ReturnedNotOwned; } break; } default: return Pred; } // Update the binding. state = setRefBinding(state, Sym, X); Pred = C.addTransition(state); // At this point we have updated the state properly. // Everything after this is merely checking to see if the return value has // been over- or under-retained. // Did we cache out? if (!Pred) return nullptr; // Update the autorelease counts. static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease"); state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X, S); // Have we generated a sink node? if (!state) return nullptr; // Get the updated binding. T = getRefBinding(state, Sym); assert(T); X = *T; // Consult the summary of the enclosing method. RetainSummaryManager &Summaries = getSummaryManager(C); const Decl *CD = &Pred->getCodeDecl(); RetEffect RE = RetEffect::MakeNoRet(); // FIXME: What is the convention for blocks? Is there one? if (const ObjCMethodDecl *MD = dyn_cast(CD)) { const RetainSummary *Summ = Summaries.getSummary(AnyCall(MD)); RE = Summ->getRetEffect(); } else if (const FunctionDecl *FD = dyn_cast(CD)) { if (!isa(FD)) { const RetainSummary *Summ = Summaries.getSummary(AnyCall(FD)); RE = Summ->getRetEffect(); } } return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); } ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C, ExplodedNode *Pred, RetEffect RE, RefVal X, SymbolRef Sym, ProgramStateRef state) const { // HACK: Ignore retain-count issues on values accessed through ivars, // because of cases like this: // [_contentView retain]; // [_contentView removeFromSuperview]; // [self addSubview:_contentView]; // invalidates 'self' // [_contentView release]; if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None) return Pred; // Any leaks or other errors? if (X.isReturnedOwned() && X.getCount() == 0) { if (RE.getKind() != RetEffect::NoRet) { if (!RE.isOwned()) { // The returning type is a CF, we expect the enclosing method should // return ownership. X = X ^ RefVal::ErrorLeakReturned; // Generate an error node. state = setRefBinding(state, Sym, X); static CheckerProgramPointTag ReturnOwnLeakTag(this, "ReturnsOwnLeak"); ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); auto R = llvm::make_unique(leakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; } } } else if (X.isReturnedNotOwned()) { if (RE.isOwned()) { if (X.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) { // Assume the method was trying to transfer a +1 reference from a // strong ivar to the caller. state = setRefBinding(state, Sym, X.releaseViaIvar() ^ RefVal::ReturnedOwned); } else { // Trying to return a not owned object to a caller expecting an // owned object. state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned); static CheckerProgramPointTag ReturnNotOwnedTag(this, "ReturnNotOwnedForOwned"); ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { auto R = llvm::make_unique( returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; } } } return Pred; } //===----------------------------------------------------------------------===// // Check various ways a symbol can be invalidated. //===----------------------------------------------------------------------===// void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const { ProgramStateRef state = C.getState(); const MemRegion *MR = loc.getAsRegion(); // Find all symbols referenced by 'val' that we are tracking // and stop tracking them. if (MR && shouldEscapeRegion(MR)) { state = state->scanReachableSymbols(val).getState(); C.addTransition(state); } } ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const { // FIXME: We may add to the interface of evalAssume the list of symbols // whose assumptions have changed. For now we just iterate through the // bindings and check if any of the tracked symbols are NULL. This isn't // too bad since the number of symbols we will track in practice are // probably small and evalAssume is only called at branches and a few // other places. RefBindingsTy B = state->get(); if (B.isEmpty()) return state; bool changed = false; RefBindingsTy::Factory &RefBFactory = state->get_context(); ConstraintManager &CMgr = state->getConstraintManager(); for (auto &I : B) { // Check if the symbol is null stop tracking the symbol. ConditionTruthVal AllocFailed = CMgr.isNull(state, I.first); if (AllocFailed.isConstrainedTrue()) { changed = true; B = RefBFactory.remove(B, I.first); } } if (changed) state = state->set(B); return state; } ProgramStateRef RetainCountChecker::checkRegionChanges( ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call) const { if (!invalidated) return state; llvm::SmallPtrSet WhitelistedSymbols; for (const MemRegion *I : ExplicitRegions) if (const SymbolicRegion *SR = I->StripCasts()->getAs()) WhitelistedSymbols.insert(SR->getSymbol()); for (SymbolRef sym : *invalidated) { if (WhitelistedSymbols.count(sym)) continue; // Remove any existing reference-count binding. state = removeRefBinding(state, sym); } return state; } ProgramStateRef RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred, const ProgramPointTag *Tag, CheckerContext &Ctx, SymbolRef Sym, RefVal V, const ReturnStmt *S) const { unsigned ACnt = V.getAutoreleaseCount(); // No autorelease counts? Nothing to be done. if (!ACnt) return state; unsigned Cnt = V.getCount(); // FIXME: Handle sending 'autorelease' to already released object. if (V.getKind() == RefVal::ReturnedOwned) ++Cnt; // If we would over-release here, but we know the value came from an ivar, // assume it was a strong ivar that's just been relinquished. if (ACnt > Cnt && V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) { V = V.releaseViaIvar(); --ACnt; } if (ACnt <= Cnt) { if (ACnt == Cnt) { V.clearCounts(); if (V.getKind() == RefVal::ReturnedOwned) { V = V ^ RefVal::ReturnedNotOwned; } else { V = V ^ RefVal::NotOwned; } } else { V.setCount(V.getCount() - ACnt); V.setAutoreleaseCount(0); } return setRefBinding(state, Sym, V); } // HACK: Ignore retain-count issues on values accessed through ivars, // because of cases like this: // [_contentView retain]; // [_contentView removeFromSuperview]; // [self addSubview:_contentView]; // invalidates 'self' // [_contentView release]; if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None) return state; // Woah! More autorelease counts then retain counts left. // Emit hard error. V = V ^ RefVal::ErrorOverAutorelease; state = setRefBinding(state, Sym, V); ExplodedNode *N = Ctx.generateSink(state, Pred, Tag); if (N) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Object was autoreleased "; if (V.getAutoreleaseCount() > 1) os << V.getAutoreleaseCount() << " times but the object "; else os << "but "; os << "has a +" << V.getCount() << " retain count"; const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); auto R = llvm::make_unique(overAutorelease, LOpts, N, Sym, os.str()); Ctx.emitReport(std::move(R)); } return nullptr; } ProgramStateRef RetainCountChecker::handleSymbolDeath(ProgramStateRef state, SymbolRef sid, RefVal V, SmallVectorImpl &Leaked) const { bool hasLeak; // HACK: Ignore retain-count issues on values accessed through ivars, // because of cases like this: // [_contentView retain]; // [_contentView removeFromSuperview]; // [self addSubview:_contentView]; // invalidates 'self' // [_contentView release]; if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None) hasLeak = false; else if (V.isOwned()) hasLeak = true; else if (V.isNotOwned() || V.isReturnedOwned()) hasLeak = (V.getCount() > 0); else hasLeak = false; if (!hasLeak) return removeRefBinding(state, sid); Leaked.push_back(sid); return setRefBinding(state, sid, V ^ RefVal::ErrorLeak); } ExplodedNode * RetainCountChecker::processLeaks(ProgramStateRef state, SmallVectorImpl &Leaked, CheckerContext &Ctx, ExplodedNode *Pred) const { // Generate an intermediate node representing the leak point. ExplodedNode *N = Ctx.addTransition(state, Pred); const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); if (N) { for (SymbolRef L : Leaked) { const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; Ctx.emitReport(llvm::make_unique(BT, LOpts, N, L, Ctx)); } } return N; } void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const { if (!Ctx.inTopFrame()) return; RetainSummaryManager &SmrMgr = getSummaryManager(Ctx); const LocationContext *LCtx = Ctx.getLocationContext(); const Decl *D = LCtx->getDecl(); Optional C = AnyCall::forDecl(D); if (!C || SmrMgr.isTrustedReferenceCountImplementation(D)) return; ProgramStateRef state = Ctx.getState(); const RetainSummary *FunctionSummary = SmrMgr.getSummary(*C); ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects(); for (unsigned idx = 0, e = C->param_size(); idx != e; ++idx) { const ParmVarDecl *Param = C->parameters()[idx]; SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol(); QualType Ty = Param->getType(); const ArgEffect *AE = CalleeSideArgEffects.lookup(idx); if (AE) { ObjKind K = AE->getObjKind(); if (K == ObjKind::Generalized || K == ObjKind::OS || (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) { RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(K, Ty) : RefVal::makeNotOwned(K, Ty); state = setRefBinding(state, Sym, NewVal); } } } Ctx.addTransition(state); } void RetainCountChecker::checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const { ExplodedNode *Pred = processReturn(RS, Ctx); // Created state cached out. if (!Pred) { return; } ProgramStateRef state = Pred->getState(); RefBindingsTy B = state->get(); // Don't process anything within synthesized bodies. const LocationContext *LCtx = Pred->getLocationContext(); if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) { assert(!LCtx->inTopFrame()); return; } for (auto &I : B) { state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx, I.first, I.second); if (!state) return; } // If the current LocationContext has a parent, don't check for leaks. // We will do that later. // FIXME: we should instead check for imbalances of the retain/releases, // and suggest annotations. if (LCtx->getParent()) return; B = state->get(); SmallVector Leaked; for (auto &I : B) state = handleSymbolDeath(state, I.first, I.second, Leaked); processLeaks(state, Leaked, Ctx, Pred); } void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ExplodedNode *Pred = C.getPredecessor(); ProgramStateRef state = C.getState(); SmallVector Leaked; // Update counts from autorelease pools for (const auto &I: state->get()) { SymbolRef Sym = I.first; if (SymReaper.isDead(Sym)) { static CheckerProgramPointTag Tag(this, "DeadSymbolAutorelease"); const RefVal &V = I.second; state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, V); if (!state) return; // Fetch the new reference count from the state, and use it to handle // this symbol. state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked); } } if (Leaked.empty()) { C.addTransition(state); return; } Pred = processLeaks(state, Leaked, C, Pred); // Did we cache out? if (!Pred) return; // Now generate a new node that nukes the old bindings. // The only bindings left at this point are the leaked symbols. RefBindingsTy::Factory &F = state->get_context(); RefBindingsTy B = state->get(); for (SymbolRef L : Leaked) B = F.remove(B, L); state = state->set(B); C.addTransition(state, Pred); } void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { RefBindingsTy B = State->get(); if (B.isEmpty()) return; Out << Sep << NL; for (auto &I : B) { Out << I.first << " : "; I.second.print(Out); Out << NL; } } //===----------------------------------------------------------------------===// // Checker registration. //===----------------------------------------------------------------------===// void ento::registerRetainCountBase(CheckerManager &Mgr) { Mgr.registerChecker(); } bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) { return true; } // FIXME: remove this, hack for backwards compatibility: // it should be possible to enable the NS/CF retain count checker as // osx.cocoa.RetainCount, and it should be possible to disable // osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false. static bool getOption(AnalyzerOptions &Options, StringRef Postfix, StringRef Value) { auto I = Options.Config.find( (StringRef("osx.cocoa.RetainCount:") + Postfix).str()); if (I != Options.Config.end()) return I->getValue() == Value; return false; } void ento::registerRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker(); Chk->TrackObjCAndCFObjects = true; Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(), "TrackNSCFStartParam", "true"); } bool ento::shouldRegisterRetainCountChecker(const LangOptions &LO) { return true; } void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker(); if (!getOption(Mgr.getAnalyzerOptions(), "CheckOSObject", "false")) Chk->TrackOSObjects = true; } bool ento::shouldRegisterOSObjectRetainCountChecker(const LangOptions &LO) { return true; } diff --git a/clang/test/Analysis/retain-release.mm b/clang/test/Analysis/retain-release.mm index ba864f817c5e..1c0c1999d7fd 100644 --- a/clang/test/Analysis/retain-release.mm +++ b/clang/test/Analysis/retain-release.mm @@ -1,517 +1,549 @@ // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -fblocks -verify %s #if __has_feature(attribute_ns_returns_retained) #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) #endif #if __has_feature(attribute_cf_returns_retained) #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) #endif #if __has_feature(attribute_ns_returns_not_retained) #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) #endif #if __has_feature(attribute_cf_returns_not_retained) #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) #endif #if __has_feature(attribute_ns_consumes_self) #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) #endif #if __has_feature(attribute_ns_consumed) #define NS_CONSUMED __attribute__((ns_consumed)) #endif #if __has_feature(attribute_cf_consumed) #define CF_CONSUMED __attribute__((cf_consumed)) #endif //===----------------------------------------------------------------------===// // The following code is reduced using delta-debugging from Mac OS X headers: // // #include // #include // #include // #include // #include // #include // // It includes the basic definitions for the test cases below. //===----------------------------------------------------------------------===// typedef unsigned int __darwin_natural_t; typedef unsigned long uintptr_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; typedef unsigned int UInt32; typedef signed long CFIndex; typedef struct { CFIndex location; CFIndex length; } CFRange; static __inline__ __attribute__((always_inline)) CFRange CFRangeMake(CFIndex loc, CFIndex len) { CFRange range; range.location = loc; range.length = len; return range; } typedef const void * CFTypeRef; typedef const struct __CFString * CFStringRef; typedef const struct __CFAllocator * CFAllocatorRef; extern const CFAllocatorRef kCFAllocatorDefault; extern CFTypeRef CFRetain(CFTypeRef cf); extern void CFRelease(CFTypeRef cf); typedef struct { } CFArrayCallBacks; extern const CFArrayCallBacks kCFTypeArrayCallBacks; typedef const struct __CFArray * CFArrayRef; typedef struct __CFArray * CFMutableArrayRef; extern CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks); void abort(void) __attribute__((noreturn)); CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks); extern const void *CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx); extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value); typedef struct { } CFDictionaryKeyCallBacks; extern const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks; typedef struct { } CFDictionaryValueCallBacks; extern const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks; typedef const struct __CFDictionary * CFDictionaryRef; typedef struct __CFDictionary * CFMutableDictionaryRef; extern CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks); typedef UInt32 CFStringEncoding; enum { kCFStringEncodingMacRoman = 0, kCFStringEncodingWindowsLatin1 = 0x0500, kCFStringEncodingISOLatin1 = 0x0201, kCFStringEncodingNextStepLatin = 0x0B01, kCFStringEncodingASCII = 0x0600, kCFStringEncodingUnicode = 0x0100, kCFStringEncodingUTF8 = 0x08000100, kCFStringEncodingNonLossyASCII = 0x0BFF , kCFStringEncodingUTF16 = 0x0100, kCFStringEncodingUTF16BE = 0x10000100, kCFStringEncodingUTF16LE = 0x14000100, kCFStringEncodingUTF32 = 0x0c000100, kCFStringEncodingUTF32BE = 0x18000100, kCFStringEncodingUTF32LE = 0x1c000100 }; extern CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); extern CFStringRef CFStringCreateCopy(CFAllocatorRef alloc, CFStringRef theString); typedef double CFTimeInterval; typedef CFTimeInterval CFAbsoluteTime; extern CFAbsoluteTime CFAbsoluteTimeGetCurrent(void); typedef const struct __CFDate * CFDateRef; extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); extern CFAbsoluteTime CFDateGetAbsoluteTime(CFDateRef theDate); typedef __darwin_natural_t natural_t; typedef natural_t mach_port_name_t; typedef mach_port_name_t mach_port_t; typedef int kern_return_t; typedef kern_return_t mach_error_t; enum { kCFNumberSInt8Type = 1, kCFNumberSInt16Type = 2, kCFNumberSInt32Type = 3, kCFNumberSInt64Type = 4, kCFNumberFloat32Type = 5, kCFNumberFloat64Type = 6, kCFNumberCharType = 7, kCFNumberShortType = 8, kCFNumberIntType = 9, kCFNumberLongType = 10, kCFNumberLongLongType = 11, kCFNumberFloatType = 12, kCFNumberDoubleType = 13, kCFNumberCFIndexType = 14, kCFNumberNSIntegerType = 15, kCFNumberCGFloatType = 16, kCFNumberMaxType = 16 }; typedef CFIndex CFNumberType; typedef const struct __CFNumber * CFNumberRef; extern CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr); typedef const struct __CFAttributedString *CFAttributedStringRef; typedef struct __CFAttributedString *CFMutableAttributedStringRef; extern CFAttributedStringRef CFAttributedStringCreate(CFAllocatorRef alloc, CFStringRef str, CFDictionaryRef attributes) ; extern CFMutableAttributedStringRef CFAttributedStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFAttributedStringRef aStr) ; extern void CFAttributedStringSetAttribute(CFMutableAttributedStringRef aStr, CFRange range, CFStringRef attrName, CFTypeRef value) ; typedef signed char BOOL; typedef unsigned long NSUInteger; @class NSString, Protocol; extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); typedef struct _NSZone NSZone; @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; @protocol NSObject - (BOOL)isEqual:(id)object; - (id)retain; - (id)copy; - (oneway void)release; - (id)autorelease; @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end @interface NSObject {} + (id)allocWithZone:(NSZone *)zone; + (id)alloc; - (void)dealloc; - (id)init; @end @interface NSObject (NSCoderMethods) - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder; @end extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); typedef struct { } NSFastEnumerationState; @protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; @end @class NSString, NSDictionary; @interface NSValue : NSObject - (void)getValue:(void *)value; @end @interface NSNumber : NSValue - (char)charValue; - (id)initWithInt:(int)value; @end @class NSString; @interface NSArray : NSObject - (NSUInteger)count; @end @interface NSArray (NSArrayCreation) + (id)array; @end @interface NSAutoreleasePool : NSObject { } - (void)drain; @end extern NSString * const NSBundleDidLoadNotification; typedef double NSTimeInterval; @interface NSDate : NSObject - (NSTimeInterval)timeIntervalSinceReferenceDate; @end typedef unsigned short unichar; @interface NSString : NSObject - (NSUInteger)length; - ( const char *)UTF8String; - (id)initWithUTF8String:(const char *)nullTerminatedCString; + (id)stringWithUTF8String:(const char *)nullTerminatedCString; @end @class NSString, NSURL, NSError; @interface NSData : NSObject - (NSUInteger)length; + (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length; + (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b; @end @class NSLocale, NSDate, NSCalendar, NSTimeZone, NSError, NSArray, NSMutableDictionary; @interface NSDictionary : NSObject - (NSUInteger)count; @end @interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey; - (void)setObject:(id)anObject forKey:(id)aKey; @end @interface NSMutableDictionary (NSMutableDictionaryCreation) + (id)dictionaryWithCapacity:(NSUInteger)numItems; @end typedef double CGFloat; struct CGSize { }; typedef struct CGSize CGSize; struct CGRect { }; typedef struct CGRect CGRect; typedef mach_port_t io_object_t; typedef char io_name_t[128]; typedef io_object_t io_iterator_t; typedef io_object_t io_service_t; typedef struct IONotificationPort * IONotificationPortRef; typedef void (*IOServiceMatchingCallback)( void * refcon, io_iterator_t iterator ); io_service_t IOServiceGetMatchingService( mach_port_t masterPort, CFDictionaryRef matching ); kern_return_t IOServiceGetMatchingServices( mach_port_t masterPort, CFDictionaryRef matching, io_iterator_t * existing ); kern_return_t IOServiceAddNotification( mach_port_t masterPort, const io_name_t notificationType, CFDictionaryRef matching, mach_port_t wakePort, uintptr_t reference, io_iterator_t * notification ) __attribute__((deprecated)); kern_return_t IOServiceAddMatchingNotification( IONotificationPortRef notifyPort, const io_name_t notificationType, CFDictionaryRef matching, IOServiceMatchingCallback callback, void * refCon, io_iterator_t * notification ); CFMutableDictionaryRef IOServiceMatching( const char * name ); CFMutableDictionaryRef IOServiceNameMatching( const char * name ); CFMutableDictionaryRef IOBSDNameMatching( mach_port_t masterPort, uint32_t options, const char * bsdName ); CFMutableDictionaryRef IOOpenFirmwarePathMatching( mach_port_t masterPort, uint32_t options, const char * path ); CFMutableDictionaryRef IORegistryEntryIDMatching( uint64_t entryID ); typedef struct __DASession * DASessionRef; extern DASessionRef DASessionCreate( CFAllocatorRef allocator ); typedef struct __DADisk * DADiskRef; extern DADiskRef DADiskCreateFromBSDName( CFAllocatorRef allocator, DASessionRef session, const char * name ); extern DADiskRef DADiskCreateFromIOMedia( CFAllocatorRef allocator, DASessionRef session, io_service_t media ); extern CFDictionaryRef DADiskCopyDescription( DADiskRef disk ); extern DADiskRef DADiskCopyWholeDisk( DADiskRef disk ); @interface NSTask : NSObject - (id)init; @end typedef struct CGColorSpace *CGColorSpaceRef; typedef struct CGImage *CGImageRef; typedef struct CGLayer *CGLayerRef; @interface NSResponder : NSObject { } @end @protocol NSAnimatablePropertyContainer - (id)animator; @end extern NSString *NSAnimationTriggerOrderIn ; @interface NSView : NSResponder { } @end @protocol NSValidatedUserInterfaceItem - (SEL)action; @end @protocol NSUserInterfaceValidations - (BOOL)validateUserInterfaceItem:(id )anItem; @end @class NSDate, NSDictionary, NSError, NSException, NSNotification; @interface NSApplication : NSResponder { } @end enum { NSTerminateCancel = 0, NSTerminateNow = 1, NSTerminateLater = 2 }; typedef NSUInteger NSApplicationTerminateReply; @protocol NSApplicationDelegate @optional - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; @end @class NSAttributedString, NSEvent, NSFont, NSFormatter, NSImage, NSMenu, NSText, NSView, NSTextView; @interface NSCell : NSObject { } @end @class NSTextField, NSPanel, NSArray, NSWindow, NSImage, NSButton, NSError; typedef struct { } CVTimeStamp; @interface CIImage : NSObject { } typedef int CIFormat; @end enum { kDAReturnSuccess = 0, kDAReturnError = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x01, kDAReturnBusy = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x02, kDAReturnBadArgument = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x03, kDAReturnExclusiveAccess = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x04, kDAReturnNoResources = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x05, kDAReturnNotFound = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x06, kDAReturnNotMounted = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x07, kDAReturnNotPermitted = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x08, kDAReturnNotPrivileged = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x09, kDAReturnNotReady = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0A, kDAReturnNotWritable = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0B, kDAReturnUnsupported = (((0x3eU)&0x3f)<<26) | (((0x368)&0xfff)<<14) | 0x0C }; typedef mach_error_t DAReturn; typedef const struct __DADissenter * DADissenterRef; extern DADissenterRef DADissenterCreate( CFAllocatorRef allocator, DAReturn status, CFStringRef string ); @interface CIContext: NSObject { } - (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r; - (CGImageRef)createCGImage:(CIImage *)im fromRect:(CGRect)r format:(CIFormat)f colorSpace:(CGColorSpaceRef)cs; - (CGLayerRef)createCGLayerWithSize:(CGSize)size info:(CFDictionaryRef)d; @end extern NSString* const QCRendererEventKey; @protocol QCCompositionRenderer - (NSDictionary*) attributes; @end @interface QCRenderer : NSObject { } - (id) createSnapshotImageOfType:(NSString*)type; @end extern NSString* const QCViewDidStartRenderingNotification; @interface QCView : NSView { } - (id) createSnapshotImageOfType:(NSString*)type; @end enum { ICEXIFOrientation1 = 1, ICEXIFOrientation2 = 2, ICEXIFOrientation3 = 3, ICEXIFOrientation4 = 4, ICEXIFOrientation5 = 5, ICEXIFOrientation6 = 6, ICEXIFOrientation7 = 7, ICEXIFOrientation8 = 8, }; @class ICDevice; @protocol ICDeviceDelegate @required - (void)didRemoveDevice:(ICDevice*)device; @end extern NSString *const ICScannerStatusWarmingUp; @class ICScannerDevice; @protocol ICScannerDeviceDelegate @optional - (void)scannerDeviceDidBecomeAvailable:(ICScannerDevice*)scanner; @end typedef long unsigned int __darwin_size_t; typedef __darwin_size_t size_t; typedef unsigned long CFTypeID; struct CGPoint { CGFloat x; CGFloat y; }; typedef struct CGPoint CGPoint; typedef struct CGGradient *CGGradientRef; typedef uint32_t CGGradientDrawingOptions; extern CFTypeID CGGradientGetTypeID(void); extern CGGradientRef CGGradientCreateWithColorComponents(CGColorSpaceRef space, const CGFloat components[], const CGFloat locations[], size_t count); extern CGGradientRef CGGradientCreateWithColors(CGColorSpaceRef space, CFArrayRef colors, const CGFloat locations[]); extern CGGradientRef CGGradientRetain(CGGradientRef gradient); extern void CGGradientRelease(CGGradientRef gradient); typedef struct CGContext *CGContextRef; extern void CGContextDrawLinearGradient(CGContextRef context, CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options); extern CGColorSpaceRef CGColorSpaceCreateDeviceRGB(void); //===----------------------------------------------------------------------===// // Test cases. //===----------------------------------------------------------------------===// class SmartPointer { id x; public: SmartPointer(id x) : x(x) {} ~SmartPointer() { [x release]; } void adopt(id x); void noAdopt(id x); }; void test_positive() { id x = [[NSObject alloc] init]; // expected-warning {{leak}} } void test_smartpointer_1() { id x = [[NSObject alloc] init]; // no-warning SmartPointer foo(x); } void test_smartpointer_2() { id x = [[NSObject alloc] init]; // no-warning SmartPointer foo(0); foo.adopt(x); } // FIXME: Eventually we want annotations to say whether or not // a C++ method claims ownership of an Objective-C object. void test_smartpointer_3() { id x = [[NSObject alloc] init]; // no-warning SmartPointer foo(0); foo.noAdopt(x); } void test_smartpointer_4() { id x = [[NSObject alloc] init]; // no-warning SmartPointer *foo = new SmartPointer(x); delete foo; } extern CFStringRef ElectronMicroscopyEngage(void); void test_microscopy() { NSString *token = (NSString*) ElectronMicroscopyEngage(); [token release]; // expected-warning {{object that is not owned}} } extern CFStringRef Scopy(void); void test_Scopy() { NSString *token = (NSString*) Scopy(); [token release]; // expected-warning {{object that is not owned}} } //===----------------------------------------------------------------------===// // Test handling of template functions used to do magic with // tracked retained pointers. //===----------------------------------------------------------------------===// template T static_objc_cast(U* value) { // ...debugging code omitted... return static_cast(value); } int rdar10553686(void) { NSObject* bar = static_objc_cast([[NSObject alloc] init]); [bar release]; return 0; } int rdar10553686_positive(void) { NSObject* bar = static_objc_cast([[NSObject alloc] init]); [bar release]; [bar retain]; // expected-warning {{used after it is released}} return 0; } @interface NSMapTable : NSObject @end extern void *NSMapGet(NSMapTable *table, const void *key); extern void NSMapInsert(NSMapTable *table, const void *key, const void *value); extern void NSMapInsertKnownAbsent(NSMapTable *table, const void *key, const void *value); char *strdup(const char *s); NSString * radar11152419(NSString *string1, NSString *key1, NSMapTable *map) { NSString *string = ( NSString *)NSMapGet(map, key1); if (!string) { string = [string1 copy]; NSString *key = [key1 copy]; NSMapInsert(map, (void*) key, (void*)string); // no warning NSMapInsertKnownAbsent(map, (void*)key, (void*)string); // no warning } return string; } //===----------------------------------------------------------------------===// // Don't crash on non-member functions with "callbacks" but without names. //===----------------------------------------------------------------------===// struct IntWrapper { int arg; }; int operator>> (const IntWrapper &W, int (*f)(int)) { return f(W.arg); } void testCallback() { IntWrapper val = { 42 }; extern int process(int); val >> process; } //===----------------------------------------------------------------------===// // Test handling static initializers. //===----------------------------------------------------------------------===// @interface radar13227740 : NSObject @end @implementation radar13227740 - (CFArrayRef)test { static CFArrayRef array = ::CFArrayCreate(0, 0, 0, 0); do { if (!((0 != array)/1)) { abort(); } } while (false); return array; } // Previously this reported a bogus leak. - (void)test2 { (void)[self test]; (void)[self test]; } @end //===----------------------------------------------------------------------===// // Don't crash on getting a null expression from CallEnter corresponding to a // destructor. //===----------------------------------------------------------------------===// template class Holder { public: Holder() throw(); ~Holder() throw() {} X* get() const throw(); void reset(X* p) throw(); private: X* ptr_; }; template inline Holder::Holder() throw() : ptr_(0){} template inline X* Holder::get() const throw() { return ptr_; } template inline void Holder::reset(X* p) throw() { if (ptr_ != p) { if (ptr_ != 0) { ::CFRelease( ptr_ ); } ptr_ = p; } } class radar13722286 { public: radar13722286() {} private: void PrepareBitmap(); Holder mStr; }; void radar13722286::PrepareBitmap() { if (mStr.get() != 0) { Holder str1; mStr.reset( CFStringCreateCopy( 0, str1.get() ) ); //expected-warning {{Potential leak of an object}} } } // rdar://34210609 void _() { _(); }; // no-warning // Do not assume that IOBSDNameMatching increments a reference counter, // unless return type is CFMutableDictionaryRef. void* IOBSDNameMatching(); void rdar33832412() { void* x = IOBSDNameMatching(); // no-warning } namespace member_CFRetains { class Foo { public: void CFRetain(const Foo &) {} void CFRetain(int) {} }; void bar() { Foo foo; foo.CFRetain(foo); // no-warning foo.CFRetain(0); // no-warning } } namespace cxx_method_escaping { struct S { static CFArrayRef testGetNoTracking(); CFArrayRef testGetNoTrackingMember(); }; void test_cxx_static_method_escaping() { CFArrayRef arr = S::testGetNoTracking(); CFRelease(arr); } void test_cxx_method_escaping(S *s) { CFArrayRef arr = s->testGetNoTrackingMember(); CFRelease(arr); } } namespace yet_another_unexpected_signature_crash { CFTypeRef CFSomethingSomethingRetain(); CFTypeRef CFSomethingSomethingAutorelease(); void foo() { CFSomethingSomethingRetain(); // no-crash CFSomethingSomethingAutorelease(); // no-crash } } + +namespace reinterpret_casts { + +void *foo() { + void *p = const_cast( + reinterpret_cast(CFArrayCreate(0, 0, 0, 0))); + void *q = reinterpret_cast( + reinterpret_cast(p) + 1); + // FIXME: Should warn about a leak here. The function should return at +0, + // but it returns at +1 instead. + return q; +} + +void *fooCreate() { + void *p = const_cast( + reinterpret_cast(CFArrayCreate(0, 0, 0, 0))); + void *q = reinterpret_cast( + reinterpret_cast(p) + 1); + // The function follows the Create Rule. + return q; // no-warning +} + +void *fooBar() CF_RETURNS_RETAINED { + void *p = const_cast( + reinterpret_cast(CFArrayCreate(0, 0, 0, 0))); + void *q = reinterpret_cast( + reinterpret_cast(p) + 1); + // The function follows the Create Rule. + return q; // no-warning +} + +}