From: George Karpenkov Date: Fri, 30 Nov 2018 02:19:16 +0000 (+0000) Subject: [analyzer] RetainCountChecker for OSObject model the "free" call X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9247506150afaeac2375e1bf300a80d036f123d8;p=clang [analyzer] RetainCountChecker for OSObject model the "free" call The "free" call frees the object immediately, ignoring the reference count. Sadly, it is actually used in a few places, so we need to model it. Differential Revision: https://reviews.llvm.org/D55092 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@347950 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h b/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h index 9b71011a54..dfcbd0c85c 100644 --- a/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h +++ b/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h @@ -530,6 +530,8 @@ class RetainSummaryManager { /// Decrement the reference count on OS object. const RetainSummary *getOSSummaryReleaseRule(const FunctionDecl *FD); + /// Free the OS object. + const RetainSummary *getOSSummaryFreeRule(const FunctionDecl *FD); enum UnaryFuncKind { cfretain, cfrelease, cfautorelease, cfmakecollectable }; diff --git a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp index 357bc67559..2595274f2d 100644 --- a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -303,6 +303,9 @@ RetainSummaryManager::generateSummary(const FunctionDecl *FD, if (FName == "retain") return getOSSummaryRetainRule(FD); + if (FName == "free") + return getOSSummaryFreeRule(FD); + if (MD->getOverloadedOperator() == OO_New) return getOSSummaryCreateRule(MD); } @@ -621,6 +624,14 @@ RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { /*ThisEff=*/DecRef); } +const RetainSummary * +RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) { + return getPersistentSummary(RetEffect::MakeNoRet(), + /*ReceiverEff=*/DoNothing, + /*DefaultEff=*/DoNothing, + /*ThisEff=*/Dealloc); +} + const RetainSummary * RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { return getPersistentSummary(RetEffect::MakeOwned(RetEffect::OS)); diff --git a/test/Analysis/osobject-retain-release.cpp b/test/Analysis/osobject-retain-release.cpp index 81cf2a1f21..f074e2b938 100644 --- a/test/Analysis/osobject-retain-release.cpp +++ b/test/Analysis/osobject-retain-release.cpp @@ -14,6 +14,7 @@ struct OSMetaClass; struct OSObject { virtual void retain(); virtual void release() {}; + virtual void free(); virtual ~OSObject(){} unsigned int foo() { return 42; } @@ -65,6 +66,22 @@ struct OSMetaClassBase { static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); }; +void check_free_no_error() { + OSArray *arr = OSArray::withCapacity(10); + arr->retain(); + arr->retain(); + arr->retain(); + arr->free(); +} + +void check_free_use_after_free() { + OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'OSArray::withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}} + arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} + arr->free(); // expected-note{{Object released}} + arr->retain(); // expected-warning{{Reference-counted object is used after it is released}} + // expected-note@-1{{Reference-counted object is used after it is released}} +} + unsigned int check_leak_explicit_new() { OSArray *arr = new OSArray; // expected-note{{Operator new returns an OSObject of type struct OSArray * with a +1 retain count}} return arr->getCount(); // expected-note{{Object leaked: allocated object of type struct OSArray * is not referenced later in this execution path and has a retain count of +1}}