r351508 - [analyzer] [RetainCountChecker] Smart pointer support.
George Karpenkov via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 17 19:12:36 PST 2019
Author: george.karpenkov
Date: Thu Jan 17 19:12:35 2019
New Revision: 351508
URL: http://llvm.org/viewvc/llvm-project?rev=351508&view=rev
Log:
[analyzer] [RetainCountChecker] Smart pointer support.
rdar://47323216
Differential Revision: https://reviews.llvm.org/D56817
Added:
cfe/trunk/test/Analysis/os_object_base.h
cfe/trunk/test/Analysis/os_smart_ptr.h
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.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/RetainSummaryManager.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h Thu Jan 17 19:12:35 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/RetainCountChecker/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp Thu Jan 17 19:12:35 2019
@@ -529,6 +529,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 +543,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;
Modified: cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp Thu Jan 17 19:12:35 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=351508&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/os_object_base.h (added)
+++ cfe/trunk/test/Analysis/os_object_base.h Thu Jan 17 19:12:35 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();
+ 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=351508&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/os_smart_ptr.h (added)
+++ cfe/trunk/test/Analysis/os_smart_ptr.h Thu Jan 17 19:12:35 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=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/osobject-retain-release.cpp (original)
+++ cfe/trunk/test/Analysis/osobject-retain-release.cpp Thu Jan 17 19:12:35 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,51 @@ 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 at -1{{Returning from constructor for 'smart_ptr<OSObject>'}}
+ // expected-note at os_smart_ptr.h:13{{Taking true branch}}
+ // expected-note at os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
+ // expected-note at os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}}
+ // expected-note at os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
+ } // expected-note{{Calling '~smart_ptr'}}
+ // expected-note at os_smart_ptr.h:35{{Taking true branch}}
+ // expected-note at os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
+ // expected-note at os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}}
+ // expected-note at os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
+ // expected-note at -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 at -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 at -1{{Returning from constructor for 'smart_ptr<OSObject>'}}
+ // expected-note at os_smart_ptr.h:13{{Taking true branch}}
+ // expected-note at os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
+ // expected-note at os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}}
+ // expected-note at os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
+ } // expected-note{{Calling '~smart_ptr'}}
+ // expected-note at os_smart_ptr.h:35{{Taking true branch}}
+ // expected-note at os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
+ // expected-note at os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}}
+ // expected-note at os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
+ // expected-note at -5{{Returning from '~smart_ptr'}}
+} // expected-warning{{Potential leak of an object stored into 'obj'}}
+// expected-note at -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();
+}
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=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/test-separate-retaincount.cpp (original)
+++ cfe/trunk/test/Analysis/test-separate-retaincount.cpp Thu Jan 17 19:12:35 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);
More information about the cfe-commits
mailing list