Author: george.karpenkov Date: Fri Jan 18 11:24:55 2019 New Revision: 351575
URL: http://llvm.org/viewvc/llvm-project?rev=351575&view=rev Log: Revert "Fix failing MSan bots" This reverts commit 2cedaaef383d8d6142046074ffebc2bb5a914778. Revert with a fix. Added: cfe/trunk/test/Analysis/os_object_base.h cfe/trunk/test/Analysis/os_smart_ptr.h Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h cfe/trunk/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp cfe/trunk/lib/StaticAnalyzer/Checkers/ValistChecker.cpp cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp cfe/trunk/test/Analysis/osobject-retain-release.cpp cfe/trunk/test/Analysis/test-separate-retaincount.cpp Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h (original) +++ cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h Fri Jan 18 11:24:55 2019 @@ -95,7 +95,7 @@ protected: friend class BugReportEquivClass; friend class BugReporter; - BugType& BT; + const BugType& BT; const Decl *DeclWithIssue = nullptr; std::string ShortDescription; std::string Description; @@ -164,15 +164,15 @@ private: void popInterestingSymbolsAndRegions(); public: - BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode) + BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode) : BT(bt), Description(desc), ErrorNode(errornode) {} - BugReport(BugType& bt, StringRef shortDesc, StringRef desc, + BugReport(const BugType& bt, StringRef shortDesc, StringRef desc, const ExplodedNode *errornode) : BT(bt), ShortDescription(shortDesc), Description(desc), ErrorNode(errornode) {} - BugReport(BugType &bt, StringRef desc, PathDiagnosticLocation l) + BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) : BT(bt), Description(desc), Location(l) {} /// Create a BugReport with a custom uniqueing location. @@ -190,7 +190,7 @@ public: virtual ~BugReport(); const BugType& getBugType() const { return BT; } - BugType& getBugType() { return BT; } + //BugType& getBugType() { return BT; } /// True when the report has an execution path associated with it. /// @@ -481,7 +481,7 @@ public: return {}; } - void Register(BugType *BT); + void Register(const BugType *BT); /// Add the given report to the set of reports tracked by BugReporter. /// Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h (original) +++ cfe/trunk/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h Fri Jan 18 11:24:55 2019 @@ -38,12 +38,14 @@ private: virtual void anchor(); public: - BugType(CheckName Check, StringRef Name, StringRef Cat) + BugType(CheckName Check, StringRef Name, StringRef Cat, + bool SuppressOnSink=false) : Check(Check), Name(Name), Category(Cat), Checker(nullptr), - SuppressOnSink(false) {} - BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat) + SuppressOnSink(SuppressOnSink) {} + BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat, + bool SuppressOnSink=false) : Check(Checker->getCheckName()), Name(Name), Category(Cat), - Checker(Checker), SuppressOnSink(false) {} + Checker(Checker), SuppressOnSink(SuppressOnSink) {} virtual ~BugType() = default; StringRef getName() const { return Name; } @@ -64,7 +66,6 @@ public: /// type should be suppressed if the end node of the report is post-dominated /// by a sink node. bool isSuppressOnSink() const { return SuppressOnSink; } - void setSuppressOnSink(bool x) { SuppressOnSink = x; } }; class BuiltinBug : public BugType { Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h (original) +++ cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h Fri Jan 18 11:24:55 2019 @@ -685,6 +685,10 @@ public: Optional<BehaviorSummary> canEval(const CallExpr *CE, const FunctionDecl *FD, bool &hasTrustedImplementationAnnotation); + /// \return Whether the type corresponds to a known smart pointer + /// implementation (that is, everything about it is inlineable). + static bool isKnownSmartPointer(QualType QT); + bool isTrustedReferenceCountImplementation(const FunctionDecl *FD); const RetainSummary *getSummary(const CallEvent &Call, Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp Fri Jan 18 11:24:55 2019 @@ -399,14 +399,14 @@ bool isZero(ProgramStateRef State, const IteratorChecker::IteratorChecker() { OutOfRangeBugType.reset( - new BugType(this, "Iterator out of range", "Misuse of STL APIs")); - OutOfRangeBugType->setSuppressOnSink(true); + new BugType(this, "Iterator out of range", "Misuse of STL APIs", + /*SuppressOnSink=*/true)); MismatchedBugType.reset( - new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs")); - MismatchedBugType->setSuppressOnSink(true); + new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", + /*SuppressOnSink=*/true)); InvalidatedBugType.reset( - new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); - InvalidatedBugType->setSuppressOnSink(true); + new BugType(this, "Iterator invalidated", "Misuse of STL APIs", + /*SuppressOnSink=*/true)); } void IteratorChecker::checkPreCall(const CallEvent &Call, Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Fri Jan 18 11:24:55 2019 @@ -2301,14 +2301,14 @@ void MallocChecker::reportLeak(SymbolRef assert(N); if (!BT_Leak[*CheckKind]) { - BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", - categories::MemoryError)); // Leaks should not be reported if they are post-dominated by a sink: // (1) Sinks are higher importance bugs. // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending // with __noreturn functions such as assert() or exit(). We choose not // to report leaks on such paths. - BT_Leak[*CheckKind]->setSuppressOnSink(true); + BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", + categories::MemoryError, + /*SuppressOnSink=*/true)); } // Most bug reports are cached at the location where they occurred. Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp Fri Jan 18 11:24:55 2019 @@ -29,6 +29,10 @@ const RefVal *getRefBinding(ProgramState return State->get<RefBindings>(Sym); } +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, RefVal Val) { assert(Sym != nullptr); @@ -39,73 +43,6 @@ static ProgramStateRef removeRefBinding( return State->remove<RefBindings>(Sym); } -class UseAfterRelease : public RefCountBug { -public: - UseAfterRelease(const CheckerBase *checker) - : RefCountBug(checker, "Use-after-release") {} - - const char *getDescription() const override { - return "Reference-counted object is used after it is released"; - } -}; - -class BadRelease : public RefCountBug { -public: - BadRelease(const CheckerBase *checker) : RefCountBug(checker, "Bad release") {} - - const char *getDescription() const override { - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - } -}; - -class DeallocNotOwned : public RefCountBug { -public: - DeallocNotOwned(const CheckerBase *checker) - : RefCountBug(checker, "-dealloc sent to non-exclusively owned object") {} - - const char *getDescription() const override { - return "-dealloc sent to object that may be referenced elsewhere"; - } -}; - -class OverAutorelease : public RefCountBug { -public: - OverAutorelease(const CheckerBase *checker) - : RefCountBug(checker, "Object autoreleased too many times") {} - - const char *getDescription() const override { - return "Object autoreleased too many times"; - } -}; - -class ReturnedNotOwnedForOwned : public RefCountBug { -public: - ReturnedNotOwnedForOwned(const CheckerBase *checker) - : RefCountBug(checker, "Method should return an owned object") {} - - const char *getDescription() const override { - return "Object with a +0 retain count returned to caller where a +1 " - "(owning) retain count is expected"; - } -}; - -class Leak : public RefCountBug { -public: - Leak(const CheckerBase *checker, StringRef name) : RefCountBug(checker, name) { - // Leaks should not be reported if they are post-dominated by a sink. - setSuppressOnSink(true); - } - - const char *getDescription() const override { return ""; } - - bool isLeak() const override { return true; } -}; - -} // end namespace retaincountchecker -} // end namespace ento -} // end namespace clang - void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) Out << "Tracked " << T.getAsString() << " | "; @@ -414,20 +351,6 @@ void RetainCountChecker::checkPostCall(c checkSummary(*Summ, Call, C); } -RefCountBug * -RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const { - if (!leakWithinFunction) - leakWithinFunction.reset(new Leak(this, "Leak")); - return leakWithinFunction.get(); -} - -RefCountBug * -RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const { - if (!leakAtReturn) - leakAtReturn.reset(new Leak(this, "Leak of returned object")); - return leakAtReturn.get(); -} - /// 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 @@ -529,6 +452,13 @@ void RetainCountChecker::processSummaryO C.addTransition(state); } +static bool isSmartPtrField(const MemRegion *MR) { + const auto *TR = dyn_cast<TypedValueRegion>( + cast<SubRegion>(MR)->getSuperRegion()); + return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType()); +} + + /// A value escapes in these possible cases: /// /// - binding to something that is not a memory region. @@ -536,10 +466,15 @@ void RetainCountChecker::processSummaryO /// - 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, so be conservative here and let the symbol go. +/// 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<VarRegion>(R); + if (!R->hasStackStorage() || !VR) return true; @@ -867,6 +802,23 @@ ProgramStateRef RetainCountChecker::upda 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, @@ -886,30 +838,9 @@ void RetainCountChecker::processNonLeakE if (!N) return; - RefCountBug *BT; - switch (ErrorKind) { - default: - llvm_unreachable("Unhandled error."); - case RefVal::ErrorUseAfterRelease: - if (!useAfterRelease) - useAfterRelease.reset(new UseAfterRelease(this)); - BT = useAfterRelease.get(); - break; - case RefVal::ErrorReleaseNotOwned: - if (!releaseNotOwned) - releaseNotOwned.reset(new BadRelease(this)); - BT = releaseNotOwned.get(); - break; - case RefVal::ErrorDeallocNotOwned: - if (!deallocNotOwned) - deallocNotOwned.reset(new DeallocNotOwned(this)); - BT = deallocNotOwned.get(); - break; - } - - assert(BT); auto report = llvm::make_unique<RefCountReport>( - *BT, C.getASTContext().getLangOpts(), N, Sym); + errorKindToBugKind(ErrorKind, Sym), + C.getASTContext().getLangOpts(), N, Sym); report->addRange(ErrorRange); C.emitReport(std::move(report)); } @@ -1112,8 +1043,8 @@ ExplodedNode * RetainCountChecker::check ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefLeakReport>( - *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C); + auto R = + llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1137,11 +1068,8 @@ ExplodedNode * RetainCountChecker::check ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { - if (!returnNotOwnedForOwned) - returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this)); - auto R = llvm::make_unique<RefCountReport>( - *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1293,12 +1221,9 @@ RetainCountChecker::handleAutoreleaseCou os << "but "; os << "has a +" << V.getCount() << " retain count"; - if (!overAutorelease) - overAutorelease.reset(new OverAutorelease(this)); - const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefCountReport>(*overAutorelease, LOpts, N, Sym, - os.str()); + auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym, + os.str()); Ctx.emitReport(std::move(R)); } @@ -1344,11 +1269,8 @@ RetainCountChecker::processLeaks(Program if (N) { for (SymbolRef L : Leaked) { - RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts) - : getLeakAtReturnBug(LOpts); - assert(BT && "BugType not initialized."); - - Ctx.emitReport(llvm::make_unique<RefLeakReport>(*BT, LOpts, N, L, Ctx)); + const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; + Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx)); } } Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h Fri Jan 18 11:24:55 2019 @@ -251,10 +251,15 @@ class RetainCountChecker check::RegionChanges, eval::Assume, eval::Call > { - mutable std::unique_ptr<RefCountBug> useAfterRelease, releaseNotOwned; - mutable std::unique_ptr<RefCountBug> deallocNotOwned; - mutable std::unique_ptr<RefCountBug> overAutorelease, returnNotOwnedForOwned; - mutable std::unique_ptr<RefCountBug> leakWithinFunction, leakAtReturn; + + RefCountBug useAfterRelease{this, RefCountBug::UseAfterRelease}; + RefCountBug releaseNotOwned{this, RefCountBug::ReleaseNotOwned}; + RefCountBug deallocNotOwned{this, RefCountBug::DeallocNotOwned}; + RefCountBug freeNotOwned{this, RefCountBug::FreeNotOwned}; + RefCountBug overAutorelease{this, RefCountBug::OverAutorelease}; + RefCountBug returnNotOwnedForOwned{this, RefCountBug::ReturnNotOwnedForOwned}; + RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction}; + RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn}; mutable std::unique_ptr<RetainSummaryManager> Summaries; public: @@ -266,11 +271,7 @@ public: /// Track sublcasses of OSObject. bool TrackOSObjects = false; - RetainCountChecker() {} - - RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const; - - RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const; + RetainCountChecker() {}; RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const { // FIXME: We don't support ARC being turned on and off during one analysis. @@ -336,6 +337,9 @@ public: RefVal V, ArgEffect E, RefVal::Kind &hasErr, CheckerContext &C) const; + const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind, + SymbolRef Sym) const; + void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp Fri Jan 18 11:24:55 2019 @@ -19,6 +19,54 @@ using namespace clang; using namespace ento; using namespace retaincountchecker; +StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { + switch (BT) { + case UseAfterRelease: + return "Use-after-release"; + case ReleaseNotOwned: + return "Bad release"; + case DeallocNotOwned: + return "-dealloc sent to non-exclusively owned object"; + case FreeNotOwned: + return "freeing non-exclusively owned object"; + case OverAutorelease: + return "Object autoreleased too many times"; + case ReturnNotOwnedForOwned: + return "Method should return an owned object"; + case LeakWithinFunction: + return "Leak"; + case LeakAtReturn: + return "Leak of returned object"; + } +} + +StringRef RefCountBug::getDescription() const { + switch (BT) { + case UseAfterRelease: + return "Reference-counted object is used after it is released"; + case ReleaseNotOwned: + return "Incorrect decrement of the reference count of an object that is " + "not owned at this point by the caller"; + case DeallocNotOwned: + return "-dealloc sent to object that may be referenced elsewhere"; + case FreeNotOwned: + return "'free' called on an object that may be referenced elsewhere"; + case OverAutorelease: + return "Object autoreleased too many times"; + case ReturnNotOwnedForOwned: + return "Object with a +0 retain count returned to caller where a +1 " + "(owning) retain count is expected"; + case LeakWithinFunction: + case LeakAtReturn: + return ""; + } +} + +RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) + : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, + /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), + BT(BT) {} + static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. return isa<IntegerLiteral>(E) || @@ -42,7 +90,8 @@ static std::string getPrettyTypeName(Qua /// Write information about the type state change to {@code os}, /// return whether the note should be generated. static bool shouldGenerateNote(llvm::raw_string_ostream &os, - const RefVal *PrevT, const RefVal &CurrV, + const RefVal *PrevT, + const RefVal &CurrV, bool DeallocSent) { // Get the previous type state. RefVal PrevV = *PrevT; @@ -132,6 +181,32 @@ static Optional<unsigned> findArgIdxOfSy return None; } +Optional<std::string> findMetaClassAlloc(const Expr *Callee) { + if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { + if (ME->getMemberDecl()->getNameAsString() != "alloc") + return None; + const Expr *This = ME->getBase()->IgnoreParenImpCasts(); + if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { + const ValueDecl *VD = DRE->getDecl(); + if (VD->getNameAsString() != "metaClass") + return None; + + if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) + return RD->getNameAsString(); + + } + } + return None; +} + +std::string findAllocatedObjectName(const Stmt *S, + QualType QT) { + if (const auto *CE = dyn_cast<CallExpr>(S)) + if (auto Out = findMetaClassAlloc(CE->getCallee())) + return *Out; + return getPrettyTypeName(QT); +} + static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, @@ -189,7 +264,7 @@ static void generateDiagnosticsForCallLi os << "a Core Foundation object of type '" << Sym->getType().getAsString() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { - os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) + os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { os << "an object of type '" << Sym->getType().getAsString() @@ -338,15 +413,7 @@ annotateConsumedSummaryMismatch(const Ex if (os.str().empty()) return nullptr; - // FIXME: remove the code duplication with NoStoreFuncVisitor. - PathDiagnosticLocation L; - if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { - L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext()); - } else { - L = PathDiagnosticLocation( - Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); - } - + PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM); return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } @@ -354,6 +421,11 @@ std::shared_ptr<PathDiagnosticPiece> RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { + const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); + + bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || + BT.getBugType() == RefCountBug::DeallocNotOwned; + const SourceManager &SM = BRC.getSourceManager(); CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); if (auto CE = N->getLocationAs<CallExitBegin>()) @@ -372,7 +444,8 @@ RefCountReportVisitor::VisitNode(const E const LocationContext *LCtx = N->getLocationContext(); const RefVal* CurrT = getRefBinding(CurrSt, Sym); - if (!CurrT) return nullptr; + if (!CurrT) + return nullptr; const RefVal &CurrV = *CurrT; const RefVal *PrevT = getRefBinding(PrevSt, Sym); @@ -382,6 +455,12 @@ RefCountReportVisitor::VisitNode(const E std::string sbuf; llvm::raw_string_ostream os(sbuf); + if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { + os << "Object is now not exclusively owned"; + auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); + return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); + } + // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { @@ -428,9 +507,9 @@ RefCountReportVisitor::VisitNode(const E // program point bool DeallocSent = false; - if (N->getLocation().getTag() && - N->getLocation().getTag()->getTagDescription().contains( - RetainCountChecker::DeallocTagDescription)) { + const ProgramPointTag *Tag = N->getLocation().getTag(); + if (Tag && Tag->getTagDescription().contains( + RetainCountChecker::DeallocTagDescription)) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); @@ -685,15 +764,15 @@ RefLeakReportVisitor::getEndPath(BugRepo return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } -RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, +RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, - bool registerVisitor) - : BugReport(D, D.getDescription(), n), Sym(sym) { - if (registerVisitor) + bool isLeak) + : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) { + if (!isLeak) addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); } -RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, +RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText) : BugReport(D, D.getDescription(), endText, n) { @@ -779,10 +858,10 @@ void RefLeakReport::createDescription(Ch } } -RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts, +RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx) - : RefCountReport(D, LOpts, n, sym, false) { + : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) { deriveAllocLocation(Ctx, sym); if (!AllocBinding) Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h Fri Jan 18 11:24:55 2019 @@ -25,32 +25,44 @@ namespace ento { namespace retaincountchecker { class RefCountBug : public BugType { -protected: - RefCountBug(const CheckerBase *checker, StringRef name) - : BugType(checker, name, categories::MemoryRefCount) {} - public: - virtual const char *getDescription() const = 0; + enum RefCountBugType { + UseAfterRelease, + ReleaseNotOwned, + DeallocNotOwned, + FreeNotOwned, + OverAutorelease, + ReturnNotOwnedForOwned, + LeakWithinFunction, + LeakAtReturn, + }; + RefCountBug(const CheckerBase *checker, RefCountBugType BT); + StringRef getDescription() const; + RefCountBugType getBugType() const { + return BT; + } - virtual bool isLeak() const { return false; } +private: + RefCountBugType BT; + static StringRef bugTypeToName(RefCountBugType BT); }; class RefCountReport : public BugReport { protected: SymbolRef Sym; + bool isLeak = false; public: - RefCountReport(RefCountBug &D, const LangOptions &LOpts, + RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, - bool registerVisitor = true); + bool isLeak=false); - RefCountReport(RefCountBug &D, const LangOptions &LOpts, + RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText); llvm::iterator_range<ranges_iterator> getRanges() override { - const RefCountBug& BugTy = static_cast<RefCountBug&>(getBugType()); - if (!BugTy.isLeak()) + if (!isLeak) return BugReport::getRanges(); return llvm::make_range(ranges_iterator(), ranges_iterator()); } @@ -69,7 +81,7 @@ class RefLeakReport : public RefCountRep void createDescription(CheckerContext &Ctx); public: - RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, + RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx); PathDiagnosticLocation getLocation(const SourceManager &SM) const override { Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp Fri Jan 18 11:24:55 2019 @@ -109,10 +109,10 @@ SimpleStreamChecker::SimpleStreamChecker DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); - LeakBugType.reset( - new BugType(this, "Resource Leak", "Unix Stream API Error")); // Sinks are higher importance bugs as well as calls to assert() or exit(0). - LeakBugType->setSuppressOnSink(true); + LeakBugType.reset( + new BugType(this, "Resource Leak", "Unix Stream API Error", + /*SuppressOnSink=*/true)); } void SimpleStreamChecker::checkPostCall(const CallEvent &Call, Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/ValistChecker.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ValistChecker.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ValistChecker.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ValistChecker.cpp Fri Jan 18 11:24:55 2019 @@ -276,8 +276,8 @@ void ValistChecker::reportLeakedVALists( new BugType(CheckNames[CK_Unterminated].getName().empty() ? CheckNames[CK_Uninitialized] : CheckNames[CK_Unterminated], - "Leaked va_list", categories::MemoryError)); - BT_leakedvalist->setSuppressOnSink(true); + "Leaked va_list", categories::MemoryError, + /*SuppressOnSink=*/true)); } const ExplodedNode *StartNode = getStartCallSite(N, Reg); Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp Fri Jan 18 11:24:55 2019 @@ -1247,7 +1247,7 @@ static void generatePathDiagnosticsForNo static std::unique_ptr<PathDiagnostic> generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { - BugType &BT = R->getBugType(); + const BugType &BT = R->getBugType(); return llvm::make_unique<PathDiagnostic>( R->getBugType().getCheckName(), R->getDeclWithIssue(), R->getBugType().getName(), R->getDescription(), @@ -2684,7 +2684,7 @@ GRBugReporter::generatePathDiagnostics( return Out; } -void BugReporter::Register(BugType *BT) { +void BugReporter::Register(const BugType *BT) { BugTypes = F.add(BugTypes, BT); } @@ -2718,7 +2718,7 @@ void BugReporter::emitReport(std::unique R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - BugType& BT = R->getBugType(); + const BugType& BT = R->getBugType(); Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2836,7 +2836,7 @@ FindReportInEquivalenceClass(BugReportEq SmallVectorImpl<BugReport*> &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - BugType& BT = I->getBugType(); + const BugType& BT = I->getBugType(); // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Fri Jan 18 11:24:55 2019 @@ -308,9 +308,8 @@ public: if (RegionOfInterest->isSubRegionOf(SelfRegion) && potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), IvarR->getDecl())) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion, - "self", /*FirstIsReferenceType=*/false, - 1); + return notModifiedDiagnostics(N, {}, SelfRegion, "self", + /*FirstIsReferenceType=*/false, 1); } } @@ -318,8 +317,7 @@ public: const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); if (RegionOfInterest->isSubRegionOf(ThisR) && !CCall->getDecl()->isImplicit()) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR, - "this", + return notModifiedDiagnostics(N, {}, ThisR, "this", /*FirstIsReferenceType=*/false, 1); // Do not generate diagnostics for not modified parameters in @@ -338,18 +336,17 @@ public: QualType T = PVD->getType(); while (const MemRegion *R = S.getAsRegion()) { if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T)) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R, - ParamName, ParamIsReferenceType, - IndirectionLevel); + return notModifiedDiagnostics(N, {}, R, ParamName, + ParamIsReferenceType, IndirectionLevel); QualType PT = T->getPointeeType(); if (PT.isNull() || PT->isVoidType()) break; if (const RecordDecl *RD = PT->getAsRecordDecl()) if (auto P = findRegionOfInterestInRecord(RD, State, R)) - return notModifiedDiagnostics( - Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName, - ParamIsReferenceType, IndirectionLevel); + return notModifiedDiagnostics(N, *P, RegionOfInterest, ParamName, + ParamIsReferenceType, + IndirectionLevel); S = State->getSVal(R, PT); T = PT; @@ -523,19 +520,12 @@ private: /// \return Diagnostics piece for region not modified in the current function. std::shared_ptr<PathDiagnosticPiece> - notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc, - CallEventRef<> Call, const RegionVector &FieldChain, + notModifiedDiagnostics(const ExplodedNode *N, const RegionVector &FieldChain, const MemRegion *MatchedRegion, StringRef FirstElement, bool FirstIsReferenceType, unsigned IndirectionLevel) { - PathDiagnosticLocation L; - if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { - L = PathDiagnosticLocation::createBegin(RS, SM, Ctx); - } else { - L = PathDiagnosticLocation( - Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), - SM); - } + PathDiagnosticLocation L = + PathDiagnosticLocation::create(N->getLocation(), SM); SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); Modified: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp Fri Jan 18 11:24:55 2019 @@ -735,6 +735,12 @@ PathDiagnosticLocation::create(const Pro return getLocationForCaller(CEE->getCalleeContext(), CEE->getLocationContext(), SMng); + } else if (auto CEB = P.getAs<CallExitBegin>()) { + if (const ReturnStmt *RS = CEB->getReturnStmt()) + return PathDiagnosticLocation::createBegin(RS, SMng, + CEB->getLocationContext()); + return PathDiagnosticLocation( + CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { CFGElement BlockFront = BE->getBlock()->front(); if (auto StmtElt = BlockFront.getAs<CFGStmt>()) { Modified: cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp (original) +++ cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp Fri Jan 18 11:24:55 2019 @@ -146,7 +146,7 @@ static bool isSubclass(const Decl *D, } static bool isOSObjectSubclass(const Decl *D) { - return isSubclass(D, "OSObject"); + return isSubclass(D, "OSMetaClassBase"); } static bool isOSObjectDynamicCast(StringRef S) { @@ -199,6 +199,20 @@ static bool isOSObjectRelated(const CXXM return false; } +bool +RetainSummaryManager::isKnownSmartPointer(QualType QT) { + QT = QT.getCanonicalType(); + const auto *RD = QT->getAsCXXRecordDecl(); + if (!RD) + return false; + const IdentifierInfo *II = RD->getIdentifier(); + if (II && II->getName() == "smart_ptr") + if (const auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext())) + if (ND->getNameAsString() == "os") + return true; + return false; +} + const RetainSummary * RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, StringRef FName, QualType RetTy) { Added: cfe/trunk/test/Analysis/os_object_base.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/os_object_base.h?rev=351575&view=auto ============================================================================== --- cfe/trunk/test/Analysis/os_object_base.h (added) +++ cfe/trunk/test/Analysis/os_object_base.h Fri Jan 18 11:24:55 2019 @@ -0,0 +1,54 @@ +#ifndef _OS_BASE_H +#define _OS_BASE_H + +#define OS_CONSUME __attribute__((os_consumed)) +#define OS_RETURNS_RETAINED __attribute__((os_returns_retained)) +#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero)) +#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero)) +#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained)) +#define OS_CONSUMES_THIS __attribute__((os_consumes_this)) + +#define OSTypeID(type) (type::metaClass) + +#define OSDynamicCast(type, inst) \ + ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type))) + +#define OSTypeAlloc(type) ((type *) ((type::metaClass)->alloc())) + +using size_t = decltype(sizeof(int)); + +struct OSMetaClass; + +struct OSMetaClassBase { + static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst, + const OSMetaClass *meta); + + virtual void retain() const; + virtual void release() const; + virtual void free(); + virtual ~OSMetaClassBase(){}; +}; + +struct OSObject : public OSMetaClassBase { + virtual ~OSObject(){} + + unsigned int foo() { return 42; } + + virtual OS_RETURNS_NOT_RETAINED OSObject *identity(); + + static OSObject *generateObject(int); + + static OSObject *getObject(); + static OSObject *GetObject(); + + static void * operator new(size_t size); + + static const OSMetaClass * const metaClass; +}; + +struct OSMetaClass : public OSMetaClassBase { + virtual OSObject * alloc() const; + virtual ~OSMetaClass(){} +}; + +#endif /* _OS_BASE_H */ Added: cfe/trunk/test/Analysis/os_smart_ptr.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/os_smart_ptr.h?rev=351575&view=auto ============================================================================== --- cfe/trunk/test/Analysis/os_smart_ptr.h (added) +++ cfe/trunk/test/Analysis/os_smart_ptr.h Fri Jan 18 11:24:55 2019 @@ -0,0 +1,89 @@ +#ifndef _OS_SMART_POINTER_H +#define _OS_SMART_POINTER_H + +#include "os_object_base.h" + +namespace os { + +template<class T> +struct smart_ptr { + smart_ptr() : pointer(nullptr) {} + + explicit smart_ptr(T *&p) : pointer(p) { + if (pointer) { + _retain(pointer); + } + } + + smart_ptr(smart_ptr const &rhs) : pointer(rhs.pointer) { + if (pointer) { + _retain(pointer); + } + } + + smart_ptr & operator=(T *&rhs) { + smart_ptr(rhs).swap(*this); + return *this; + } + + smart_ptr & operator=(smart_ptr &rhs) { + smart_ptr(rhs).swap(*this); + return *this; + } + + ~smart_ptr() { + if (pointer) { + _release(pointer); + } + } + + void reset() { + smart_ptr().swap(*this); + } + + T *get() const { + return pointer; + } + + T ** get_for_out_param() { + reset(); + return &pointer; + } + + T * operator->() const { + OSPTR_LOG("Dereference smart_ptr with %p\n", pointer); + return pointer; + } + + explicit + operator bool() const { + return pointer != nullptr; + } + + inline void + swap(smart_ptr &p) { + T *temp = pointer; + pointer = p.pointer; + p.pointer = temp; + } + + static inline void + _retain(T *obj) { + obj->retain(); + } + + static inline void + _release(T *obj) { + obj->release(); + } + + static inline T * + _alloc() { + return new T; + } + + T *pointer; +}; +} + +#endif /* _OS_SMART_POINTER_H */ Modified: cfe/trunk/test/Analysis/osobject-retain-release.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/osobject-retain-release.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/test/Analysis/osobject-retain-release.cpp (original) +++ cfe/trunk/test/Analysis/osobject-retain-release.cpp Fri Jan 18 11:24:55 2019 @@ -1,44 +1,10 @@ // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\ // RUN: -analyzer-checker=core,osx -verify %s -struct OSMetaClass; - -#define OS_CONSUME __attribute__((os_consumed)) -#define OS_RETURNS_RETAINED __attribute__((os_returns_retained)) -#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero)) -#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero)) -#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained)) -#define OS_CONSUMES_THIS __attribute__((os_consumes_this)) - -#define OSTypeID(type) (type::metaClass) - -#define OSDynamicCast(type, inst) \ - ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type))) - -using size_t = decltype(sizeof(int)); - -struct OSObject { - virtual void retain(); - virtual void release() {}; - virtual void free(); - virtual ~OSObject(){} - - unsigned int foo() { return 42; } - - virtual OS_RETURNS_NOT_RETAINED OSObject *identity(); - - static OSObject *generateObject(int); - - static OSObject *getObject(); - static OSObject *GetObject(); - - static void * operator new(size_t size); - - static const OSMetaClass * const metaClass; -}; +#include "os_object_base.h" +#include "os_smart_ptr.h" struct OSIterator : public OSObject { - static const OSMetaClass * const metaClass; }; @@ -88,9 +54,6 @@ struct OtherStruct { OtherStruct(OSArray *arr); }; -struct OSMetaClassBase { - static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); -}; void escape(void *); void escape_with_source(void *p) {} @@ -616,3 +579,68 @@ typedef bool (^Blk)(OSObject *); void test_escape_to_unknown_block(Blk blk) { blk(getObject()); // no-crash } + +using OSObjectPtr = os::smart_ptr<OSObject>; + +void test_smart_ptr_uaf() { + OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} + { + OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}} + // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}} + // expected-note@os_smart_ptr.h:13{{Taking true branch}} + // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} + // expected-note@os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}} + // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}} + } // expected-note{{Calling '~smart_ptr'}} + // expected-note@os_smart_ptr.h:35{{Taking true branch}} + // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} + // expected-note@os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}} + // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} + // expected-note@-5{{Returning from '~smart_ptr'}} + obj->release(); // expected-note{{Object released}} + obj->release(); // expected-warning{{Reference-counted object is used after it is released}} +// expected-note@-1{{Reference-counted object is used after it is released}} +} + +void test_smart_ptr_leak() { + OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} + { + OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}} + // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}} + // expected-note@os_smart_ptr.h:13{{Taking true branch}} + // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} + // expected-note@os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}} + // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}} + } // expected-note{{Calling '~smart_ptr'}} + // expected-note@os_smart_ptr.h:35{{Taking true branch}} + // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} + // expected-note@os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}} + // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} + // expected-note@-5{{Returning from '~smart_ptr'}} +} // expected-warning{{Potential leak of an object stored into 'obj'}} +// expected-note@-1{{Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1}} + +void test_smart_ptr_no_leak() { + OSObject *obj = new OSObject; + { + OSObjectPtr p(obj); + } + obj->release(); +} + +void test_ostypealloc_correct_diagnostic_name() { + OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}} + arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} + arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}} +} // expected-note{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}} + // expected-warning@-1{{Potential leak of an object stored into 'arr'}} + +void escape_elsewhere(OSObject *obj); + +void test_free_on_escaped_object_diagnostics() { + OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} + escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}} + obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}} + // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}} +} + Modified: cfe/trunk/test/Analysis/test-separate-retaincount.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/test-separate-retaincount.cpp?rev=351575&r1=351574&r2=351575&view=diff ============================================================================== --- cfe/trunk/test/Analysis/test-separate-retaincount.cpp (original) +++ cfe/trunk/test/Analysis/test-separate-retaincount.cpp Fri Jan 18 11:24:55 2019 @@ -2,6 +2,8 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.OSObjectRetainCount -DNO_OS_OBJECT -verify %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-config "osx.cocoa.RetainCount:CheckOSObject=false" -DNO_OS_OBJECT -verify %s +#include "os_object_base.h" + typedef const void * CFTypeRef; extern CFTypeRef CFRetain(CFTypeRef cf); extern void CFRelease(CFTypeRef cf); @@ -11,14 +13,6 @@ extern CFTypeRef CFCreate() CF_RETURNS_R using size_t = decltype(sizeof(int)); -struct OSObject { - virtual void retain(); - virtual void release(); - - static void * operator new(size_t size); - virtual ~OSObject(){} -}; - void cf_overrelease() { CFTypeRef cf = CFCreate(); CFRelease(cf); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits