diff options
Diffstat (limited to 'test/Analysis/osobject-retain-release.cpp')
-rw-r--r-- | test/Analysis/osobject-retain-release.cpp | 177 |
1 files changed, 135 insertions, 42 deletions
diff --git a/test/Analysis/osobject-retain-release.cpp b/test/Analysis/osobject-retain-release.cpp index 9d11a06231..35d91add1a 100644 --- a/test/Analysis/osobject-retain-release.cpp +++ b/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; }; @@ -65,10 +31,12 @@ struct OSArray : public OSObject { static OSArray *withCapacity(unsigned int capacity); static void consumeArray(OS_CONSUME OSArray * array); - static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) { - return nullptr; + static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) { // expected-note{{Parameter 'array' starts at +1, as it is marked as consuming}} + return nullptr; // expected-warning{{Potential leak of an object of type 'OSArray'}} +// expected-note@-1{{Object leaked: allocated object of type 'OSArray' is not referenced later in this execution path and has a retain count of +1}} } + static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter(); static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate(); @@ -88,9 +56,16 @@ struct OtherStruct { OtherStruct(OSArray *arr); }; -struct OSMetaClassBase { - static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); -}; +bool test_meta_cast_no_leak(OSMetaClassBase *arg) { + return arg && arg->metaCast("blah") != nullptr; +} + +static void consumedMismatch(OS_CONSUME OSObject *a, + OSObject *b) { // expected-note{{Parameter 'b' starts at +0}} + a->release(); + b->retain(); // expected-note{{Reference count incremented. The object now has a +1 retain count}} +} // expected-warning{{Potential leak of an object of type 'OSObject'}} +// expected-note@-1{{Object leaked: allocated object of type 'OSObject' is not referenced later in this execution path and has a retain count of +1}} void escape(void *); void escape_with_source(void *p) {} @@ -263,6 +238,13 @@ void use_out_param_leak_osreturn() { } // 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 cleanup(OSObject **obj); + +void test_cleanup_escaping() { + __attribute__((cleanup(cleanup))) OSObject *obj; + always_write_into_out_param(&obj); // no-warning, the value has escaped. +} + struct StructWithField { OSObject *obj; @@ -515,7 +497,7 @@ unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) { void check_dynamic_cast_null_branch(OSObject *obj) { OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}} - OSArray *arr = OSDynamicCast(OSArray, obj); + OSArray *arr = OSDynamicCast(OSArray, obj); // expected-note{{Assuming dynamic cast returns null due to type mismatch}} if (!arr) // expected-note{{Taking true branch}} return; // expected-warning{{Potential leak of an object stored into 'arr1'}} // expected-note@-1{{Object leaked}} @@ -526,6 +508,7 @@ void check_dynamic_cast_null_check() { OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}} // expected-warning@-1{{Potential leak of an object}} // expected-note@-2{{Object leaked}} + // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}} if (!arr) return; arr->release(); @@ -609,3 +592,113 @@ 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:71{{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:76{{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:71{{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:76{{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(); +} + +OSObject *getRuleViolation() { + return new OSObject; // expected-warning{{Potential leak of an object of type 'OSObject'}} +// expected-note@-1{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} +// expected-note@-2{{Object leaked: allocated object of type 'OSObject' is returned from a function whose name ('getRuleViolation') starts with 'get'}} +} + +OSObject *createRuleViolation(OSObject *param) { // expected-note{{Parameter 'param' starts at +0}} + return param; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} + // expected-note@-1{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} +} + +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}} +} + +void test_tagged_retain_no_leak() { + OSObject *obj = new OSObject; + obj->taggedRelease(); +} + +void test_tagged_retain_no_uaf() { + OSObject *obj = new OSObject; + obj->taggedRetain(); + obj->release(); + obj->release(); +} + +class IOService { +public: + OSObject *somethingMatching(OSObject *table = 0); +}; + +OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc, + OSObject *table = 0) { + // This probably just passes table through. We should probably not make + // ptr1 definitely equal to table, but we should not warn about leaks. + OSObject *ptr1 = svc->somethingMatching(table); // no-warning + + // FIXME: This, however, should follow the Create Rule regardless. + // We should warn about the leak here. + OSObject *ptr2 = svc->somethingMatching(); // no-warning + + if (!table) + table = OSTypeAlloc(OSArray); + + // This function itself ends with "Matching"! Do not warn when we're + // returning from it at +0. + return table; // no-warning +} |