diff options
Diffstat (limited to 'clang/lib')
4 files changed, 126 insertions, 16 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 30d4dd1bd8a..2b39ad6fa65 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -418,13 +418,18 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, } // Consult the summary for the return value. + SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol(); RetEffect RE = Summ.getRetEffect(); - if (RE.getKind() == RetEffect::NoRetHard) { - SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol(); - if (Sym) - state = removeRefBinding(state, Sym); + if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) { + if (Optional<RefVal> updatedRefVal = + refValFromRetEffect(RE, MCall->getResultType())) { + state = setRefBinding(state, Sym, *updatedRefVal); + } } + if (RE.getKind() == RetEffect::NoRetHard && Sym) + state = removeRefBinding(state, Sym); + C.addTransition(state); } @@ -490,11 +495,10 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, } } - // Evaluate the effect on the message receiver. + // Evaluate the effect on the message receiver / `this` argument. bool ReceiverIsTracked = false; if (!hasErr) { - const ObjCMethodCall *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg); - if (MsgInvocation) { + if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) { if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { if (const RefVal *T = getRefBinding(state, Sym)) { ReceiverIsTracked = true; @@ -506,6 +510,17 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, } } } + } else if (const auto *MCall = dyn_cast<CXXMemberCall>(&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; + } + } + } } } diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index 6bd5379e5f4..8683b23dd96 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -98,7 +98,7 @@ private: /// The kind of object being tracked (CF or ObjC), if known. /// /// See the RetEffect::ObjKind enum for possible values. - unsigned RawObjectKind : 2; + unsigned RawObjectKind : 3; /// True if the current state and/or retain count may turn out to not be the /// best possible approximation of the reference counting state. @@ -268,6 +268,8 @@ class RetainCountChecker mutable std::unique_ptr<RetainSummaryManager> Summaries; mutable SummaryLogTy SummaryLog; + + AnalyzerOptions &Options; mutable bool ShouldResetSummaryLog; /// Optional setting to indicate if leak reports should include @@ -275,12 +277,17 @@ class RetainCountChecker mutable bool IncludeAllocationLine; public: - RetainCountChecker(AnalyzerOptions &AO) - : ShouldResetSummaryLog(false), - IncludeAllocationLine(shouldIncludeAllocationSiteInLeakDiagnostics(AO)) {} + RetainCountChecker(AnalyzerOptions &Options) + : Options(Options), ShouldResetSummaryLog(false), + IncludeAllocationLine( + shouldIncludeAllocationSiteInLeakDiagnostics(Options)) {} ~RetainCountChecker() override { DeleteContainerSeconds(DeadSymbolTags); } + bool shouldCheckOSObjectRetainCount() const { + return Options.getBooleanOption("CheckOSObject", false, this); + } + void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) const { // FIXME: This is a hack to make sure the summary log gets cleared between @@ -333,10 +340,12 @@ public: // FIXME: We don't support ARC being turned on and off during one analysis. // (nor, for that matter, do we support changing ASTContexts) bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; - if (!Summaries) - Summaries.reset(new RetainSummaryManager(Ctx, ARCEnabled)); - else + if (!Summaries) { + Summaries.reset(new RetainSummaryManager( + Ctx, ARCEnabled, shouldCheckOSObjectRetainCount())); + } else { assert(Summaries->isARCEnabled() == ARCEnabled); + } return *Summaries; } diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index ef03470f908..2d41c4526fb 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -120,6 +120,9 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, if (CurrV.getObjKind() == RetEffect::CF) { os << " returns a Core Foundation object of type " << Sym->getType().getAsString() << " with a "; + } else if (CurrV.getObjKind() == RetEffect::OS) { + os << " returns an OSObject of type " + << Sym->getType().getAsString() << " with a "; } else if (CurrV.getObjKind() == RetEffect::Generalized) { os << " returns an object of type " << Sym->getType().getAsString() << " with a "; diff --git a/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp index a2e716f56f7..4370daa244e 100644 --- a/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -53,6 +53,31 @@ RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) { return Summ; } +static bool isOSObjectSubclass(QualType T); + +static bool isOSObjectSubclass(const CXXRecordDecl *RD) { + if (RD->getDeclName().getAsString() == "OSObject") + return true; + + const CXXRecordDecl *RDD = RD->getDefinition(); + if (!RDD) + return false; + + for (const CXXBaseSpecifier Spec : RDD->bases()) { + if (isOSObjectSubclass(Spec.getType())) + return true; + } + return false; +} + +/// \return Whether type represents an OSObject successor. +static bool isOSObjectSubclass(QualType T) { + if (const auto *RD = T->getAsCXXRecordDecl()) { + return isOSObjectSubclass(RD); + } + return false; +} + static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) { for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) { if (Ann->getAnnotation() == rcAnnotation) @@ -196,6 +221,17 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } if (RetTy->isPointerType()) { + if (TrackOSObjects && isOSObjectSubclass(RetTy->getPointeeType())) { + if (const IdentifierInfo *II = FD->getIdentifier()) { + StringRef FuncName = II->getName(); + if (FuncName.contains_lower("with") + || FuncName.contains_lower("create") + || FuncName.contains_lower("copy")) + return getOSSummaryCreateRule(FD); + } + return getOSSummaryGetRule(FD); + } + // For CoreFoundation ('CF') types. if (cocoa::isRefType(RetTy, "CF", FName)) { if (isRetain(FD, FName)) { @@ -241,6 +277,17 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } } + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + const CXXRecordDecl *Parent = MD->getParent(); + if (TrackOSObjects && isOSObjectSubclass(Parent)) { + if (isRelease(FD, FName)) + return getOSSummaryReleaseRule(FD); + + if (isRetain(FD, FName)) + return getOSSummaryRetainRule(FD); + } + } + // Check for release functions, the only kind of functions that we care // about that don't return a pointer type. if (FName.size() >= 2 && FName[0] == 'C' && @@ -279,6 +326,14 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } } + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + + // Stop tracking arguments passed to C++ methods, as those might be + // wrapping smart pointers. + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, + DoNothing); + } + return getDefaultSummary(); } @@ -411,6 +466,8 @@ RetainSummaryManager::getSummary(const CallEvent &Call, Summ = getFunctionSummary(cast<SimpleFunctionCall>(Call).getDecl()); break; case CE_CXXMember: + Summ = getFunctionSummary(cast<CXXMemberCall>(Call).getDecl()); + break; case CE_CXXMemberOperator: case CE_Block: case CE_CXXConstructor: @@ -514,6 +571,32 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT, } const RetainSummary * +RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeNoRet(), + /*ReceiverEff=*/DoNothing, + /*DefaultEff=*/DoNothing, + /*ThisEff=*/IncRef); +} + +const RetainSummary * +RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeNoRet(), + /*ReceiverEff=*/DoNothing, + /*DefaultEff=*/DoNothing, + /*ThisEff=*/DecRef); +} + +const RetainSummary * +RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeOwned(RetEffect::OS)); +} + +const RetainSummary * +RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::OS)); +} + +const RetainSummary * RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { assert (ScratchArgs.isEmpty()); @@ -877,7 +960,7 @@ void RetainSummaryManager::InitializeMethodSummaries() { CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { ASTContext &Ctx = MD->getASTContext(); LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount); + RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false); const RetainSummary *S = M.getMethodSummary(MD); CallEffects CE(S->getRetEffect()); CE.Receiver = S->getReceiverEffect(); @@ -891,7 +974,7 @@ CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { CallEffects CallEffects::getEffect(const FunctionDecl *FD) { ASTContext &Ctx = FD->getASTContext(); LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount); + RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, /*TrackOSObjects=*/false); const RetainSummary *S = M.getFunctionSummary(FD); CallEffects CE(S->getRetEffect()); unsigned N = FD->param_size(); |