From: George Karpenkov Date: Tue, 23 Oct 2018 23:11:50 +0000 (+0000) Subject: [analyzer] Do not stop tracking CXX methods touching OSObject. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a193197ff62995641b30b411deaa1208b72d29dd;p=clang [analyzer] Do not stop tracking CXX methods touching OSObject. Trust generalized annotations for OSObject. Differential Revision: https://reviews.llvm.org/D53550 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@345100 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 578f282a19..bb451cc8af 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -45,7 +45,7 @@ ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) - Out << "Tracked " << T.getAsString() << '/'; + Out << "Tracked " << T.getAsString() << " | "; switch (getKind()) { default: llvm_unreachable("Invalid RefVal kind"); diff --git a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp index 28097e30ca..09b744243b 100644 --- a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// // // This file defines summaries implementation for retain counting, which -// implements a reference count checker for Core Foundation and Cocoa -// on (Mac OS X). +// implements a reference count checker for Core Foundation, Cocoa +// and OSObject (on Mac OS X). // //===----------------------------------------------------------------------===// @@ -94,6 +94,22 @@ static bool isMakeCollectable(StringRef FName) { return FName.contains_lower("MakeCollectable"); } +/// A function is OSObject related if it is declared on a subclass +/// of OSObject, or any of the parameters is a subclass of an OSObject. +static bool isOSObjectRelated(const CXXMethodDecl *MD) { + if (isOSObjectSubclass(MD->getParent())) + return true; + + for (ParmVarDecl *Param : MD->parameters()) { + QualType PT = Param->getType(); + if (CXXRecordDecl *RD = PT->getPointeeType()->getAsCXXRecordDecl()) + if (isOSObjectSubclass(RD)) + return true; + } + + return false; +} + const RetainSummary * RetainSummaryManager::generateSummary(const FunctionDecl *FD, bool &AllowAnnotations) { @@ -322,12 +338,10 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, } } - if (isa(FD)) { - - // Stop tracking arguments passed to C++ methods, as those might be - // wrapping smart pointers. - return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, - DoNothing); + if (const auto *MD = dyn_cast(FD)) { + if (!(TrackOSObjects && isOSObjectRelated(MD))) + return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking, + DoNothing); } return getDefaultSummary(); @@ -642,6 +656,8 @@ RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, if (D->hasAttr()) return RetEffect::MakeNotOwned(RetEffect::CF); + else if (hasRCAnnotation(D, "rc_ownership_returns_not_retained")) + return RetEffect::MakeNotOwned(RetEffect::Generalized); return None; } diff --git a/test/Analysis/osobject-retain-release.cpp b/test/Analysis/osobject-retain-release.cpp index e9c9f698da..23e92ecaf6 100644 --- a/test/Analysis/osobject-retain-release.cpp +++ b/test/Analysis/osobject-retain-release.cpp @@ -2,6 +2,11 @@ struct OSMetaClass; +#define TRUSTED __attribute__((annotate("rc_ownership_trusted_implementation"))) +#define OS_CONSUME TRUSTED __attribute__((annotate("rc_ownership_consumed"))) +#define OS_RETURNS_RETAINED TRUSTED __attribute__((annotate("rc_ownership_returns_retained"))) +#define OS_RETURNS_NOT_RETAINED TRUSTED __attribute__((annotate("rc_ownership_returns_not_retained"))) + #define OSTypeID(type) (type::metaClass) #define OSDynamicCast(type, inst) \ @@ -21,14 +26,53 @@ struct OSArray : public OSObject { unsigned int getCount(); static OSArray *withCapacity(unsigned int capacity); + static void consumeArray(OS_CONSUME OSArray * array); + + static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) { + return nullptr; + } + + static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter(); + static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate(); + static const OSMetaClass * const metaClass; }; +struct OtherStruct { + static void doNothingToArray(OSArray *array); +}; + struct OSMetaClassBase { static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); }; +void check_no_invalidation() { + OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}} + OtherStruct::doNothingToArray(arr); +} // expected-warning{{Potential leak of an object stored into 'arr'}} + // expected-note@-1{{Object leaked}} + +void check_rc_consumed() { + OSArray *arr = OSArray::withCapacity(10); + OSArray::consumeArray(arr); +} + +void check_rc_consume_temporary() { + OSArray::consumeArray(OSArray::withCapacity(10)); +} + +void check_rc_getter() { + OSArray *arr = OSArray::MaskedGetter(); + (void)arr; +} + +void check_rc_create() { + OSArray *arr = OSArray::getOoopsActuallyCreate(); + arr->release(); +} + + void check_dynamic_cast() { OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); arr->release(); @@ -80,11 +124,6 @@ struct ArrayOwner { OSArray *getArraySourceUnknown(); }; -//unsigned int leak_on_create_no_release(ArrayOwner *owner) { - //OSArray *myArray = - -//} - unsigned int no_warning_on_getter(ArrayOwner *owner) { OSArray *arr = owner->getArray(); return arr->getCount();