diff options
Diffstat (limited to 'clang/test')
-rw-r--r-- | clang/test/Analysis/os_object_base.h | 54 | ||||
-rw-r--r-- | clang/test/Analysis/os_smart_ptr.h | 89 | ||||
-rw-r--r-- | clang/test/Analysis/osobject-retain-release.cpp | 106 | ||||
-rw-r--r-- | clang/test/Analysis/test-separate-retaincount.cpp | 10 |
4 files changed, 212 insertions, 47 deletions
diff --git a/clang/test/Analysis/os_object_base.h b/clang/test/Analysis/os_object_base.h new file mode 100644 index 00000000000..e388dddd58f --- /dev/null +++ b/clang/test/Analysis/os_object_base.h @@ -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 */ diff --git a/clang/test/Analysis/os_smart_ptr.h b/clang/test/Analysis/os_smart_ptr.h new file mode 100644 index 00000000000..8faf294f751 --- /dev/null +++ b/clang/test/Analysis/os_smart_ptr.h @@ -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 */ diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp index 1e44866eff3..9bc2e204545 100644 --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -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}} +} + diff --git a/clang/test/Analysis/test-separate-retaincount.cpp b/clang/test/Analysis/test-separate-retaincount.cpp index be6534f544b..0835c88cf91 100644 --- a/clang/test/Analysis/test-separate-retaincount.cpp +++ b/clang/test/Analysis/test-separate-retaincount.cpp @@ -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_RETAINED; 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); |