// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx -analyzer-output=text -verify %s struct OSMetaClass; #define OS_CONSUME __attribute__((os_consumed)) #define OS_RETURNS_RETAINED __attribute__((os_returns_retained)) #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; }; struct OSIterator : public OSObject { static const OSMetaClass * const metaClass; }; struct OSArray : public OSObject { unsigned int getCount(); OSIterator * getIterator(); OSObject *identity() override; virtual OSObject *generateObject(OSObject *input); virtual void consumeReference(OS_CONSUME OSArray *other); void putIntoArray(OSArray *array) OS_CONSUMES_THIS; template void putIntoT(T *owner) OS_CONSUMES_THIS; static OSArray *generateArrayHasCode() { return new OSArray; } 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 MyArray : public OSArray { void consumeReference(OSArray *other) override; OSObject *identity() override; OSObject *generateObject(OSObject *input) override; }; struct OtherStruct { static void doNothingToArray(OSArray *array); OtherStruct(OSArray *arr); }; struct OSMetaClassBase { static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); }; void escape(void *); void test_escaping_into_voidstar() { OSObject *obj = new OSObject; escape(obj); } void test_no_infinite_check_recursion(MyArray *arr) { OSObject *input = new OSObject; OSObject *o = arr->generateObject(input); o->release(); input->release(); } void check_param_attribute_propagation(MyArray *parent) { OSArray *arr = new OSArray; parent->consumeReference(arr); } unsigned int check_attribute_propagation(OSArray *arr) { OSObject *other = arr->identity(); OSArray *casted = OSDynamicCast(OSArray, other); if (casted) return casted->getCount(); return 0; } unsigned int check_attribute_indirect_propagation(MyArray *arr) { OSObject *other = arr->identity(); OSArray *casted = OSDynamicCast(OSArray, other); if (casted) return casted->getCount(); return 0; } void check_consumes_this(OSArray *owner) { OSArray *arr = new OSArray; arr->putIntoArray(owner); } void check_consumes_this_with_template(OSArray *owner) { OSArray *arr = new OSArray; arr->putIntoT(owner); } 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 method 'OSArray::withCapacity' 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->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 OSArray with a +1 retain count}} return arr->getCount(); // expected-note{{Object leaked: allocated object of type OSArray is not referenced later in this execution path and has a retain count of +1}} // expected-warning@-1{{Potential leak of an object of type OSArray}} } unsigned int check_leak_factory() { OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} return arr->getCount(); // 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 check_get_object() { OSObject::getObject(); } void check_Get_object() { OSObject::GetObject(); } void check_custom_iterator_rule(OSArray *arr) { OSIterator *it = arr->getIterator(); it->release(); } void check_iterator_leak(OSArray *arr) { arr->getIterator(); // expected-note{{Call to method 'OSArray::getIterator' returns an OSObject of type OSIterator with a +1 retain count}} } // expected-note{{Object leaked: allocated object of type OSIterator is not referenced later}} // expected-warning@-1{{Potential leak of an object of type OSIterator}} void check_no_invalidation() { OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 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_no_invalidation_other_struct() { OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} OtherStruct other(arr); // expected-warning{{Potential leak}} // expected-note@-1{{Object leaked}} } struct ArrayOwner : public OSObject { OSArray *arr; ArrayOwner(OSArray *arr) : arr(arr) {} static ArrayOwner* create(OSArray *arr) { return new ArrayOwner(arr); } OSArray *getArray() { return arr; } OSArray *createArray() { return OSArray::withCapacity(10); } OSArray *createArraySourceUnknown(); OSArray *getArraySourceUnknown(); }; OSArray *generateArray() { return OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} // expected-note@-1{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} } unsigned int check_leak_good_error_message() { unsigned int out; { OSArray *leaked = generateArray(); // expected-note{{Calling 'generateArray'}} // expected-note@-1{{Returning from 'generateArray'}} out = leaked->getCount(); // expected-warning{{Potential leak of an object stored into 'leaked'}} // expected-note@-1{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} } return out; } unsigned int check_leak_msg_temporary() { return generateArray()->getCount(); // expected-warning{{Potential leak of an object}} // expected-note@-1{{Calling 'generateArray'}} // expected-note@-2{{Returning from 'generateArray'}} // expected-note@-3{{Object leaked: allocated object of type OSArray is not referenced later in this execution path and has a retain count of +1}} } void check_confusing_getters() { OSArray *arr = OSArray::withCapacity(10); ArrayOwner *AO = ArrayOwner::create(arr); AO->getArray(); AO->release(); arr->release(); } 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(); } unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) { OSArray *arr = OSDynamicCast(OSArray, obj); if (arr) { return arr->getCount(); } else { // The fact that dynamic cast has failed should not imply that // the input object was null. return obj->foo(); // no-warning } } 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); if (!arr) // expected-note{{Taking true branch}} return; // expected-warning{{Potential leak of an object stored into 'arr1'}} // expected-note@-1{{Object leaked}} arr1->release(); } 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}} if (!arr) return; arr->release(); } void use_after_release() { OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type OSArray with a +1 retain count}} arr->release(); // expected-note{{Object released}} arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}} // expected-note@-1{{Reference-counted object is used after it is released}} } void potential_leak() { OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' 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}} arr->getCount(); } // expected-warning{{Potential leak of an object stored into 'arr'}} // expected-note@-1{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}} void proper_cleanup() { OSArray *arr = OSArray::withCapacity(10); // +1 arr->retain(); // +2 arr->release(); // +1 arr->getCount(); arr->release(); // 0 } unsigned int no_warning_on_getter(ArrayOwner *owner) { OSArray *arr = owner->getArray(); return arr->getCount(); } unsigned int warn_on_overrelease(ArrayOwner *owner) { // FIXME: summaries are not applied in case the source of the getter/setter // is known. // rdar://45681203 OSArray *arr = owner->getArray(); arr->release(); return arr->getCount(); } unsigned int nowarn_on_release_of_created(ArrayOwner *owner) { OSArray *arr = owner->createArray(); unsigned int out = arr->getCount(); arr->release(); return out; } unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) { OSArray *arr = owner->createArraySourceUnknown(); unsigned int out = arr->getCount(); arr->release(); return out; } unsigned int no_warn_ok_release(ArrayOwner *owner) { OSArray *arr = owner->getArray(); // +0 arr->retain(); // +1 arr->release(); // +0 return arr->getCount(); // no-warning } unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) { OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type OSArray with a +0 retain count}} arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} return arr->getCount(); } unsigned int ok_release_with_unknown_source(ArrayOwner *owner) { OSArray *arr = owner->getArraySourceUnknown(); // +0 arr->retain(); // +1 arr->release(); // +0 return arr->getCount(); }