// RUN: %clang_analyze_cc1 -std=c++14 -fblocks -analyze -analyzer-output=text\ // RUN: -analyzer-checker=core,osx,debug.ExprInspection -verify %s #include "os_object_base.h" #include "os_smart_ptr.h" void clang_analyzer_eval(bool); 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) { // 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(); static const OSMetaClass * const metaClass; }; struct MyArray : public OSArray { void consumeReference(OSArray *other) override; OSObject *identity() override; OSObject *generateObject(OSObject *input) override; }; // These are never refcounted. struct OSSymbol : OSObject {}; struct OtherStruct { static void doNothingToArray(OSArray *array); OtherStruct(OSArray *arr); }; 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) {} bool coin(); typedef int kern_return_t; typedef kern_return_t IOReturn; typedef kern_return_t OSReturn; #define kOSReturnSuccess 0 #define kIOReturnSuccess 0 bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj); void use_out_param() { OSObject *obj; if (write_into_out_param_on_success(&obj)) { obj->release(); } } void use_out_param_leak() { OSObject *obj; write_into_out_param_on_success(&obj); // expected-note-re{{Call to function 'write_into_out_param_on_success' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'obj' (assuming the call returns non-zero){{$}}}} } // 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}} bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj); void use_out_param_leak2() { OSObject *obj; write_into_out_param_on_failure(&obj); // expected-note-re{{Call to function 'write_into_out_param_on_failure' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'obj' (assuming the call returns zero){{$}}}} } // 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 use_out_param_on_failure() { OSObject *obj; if (!write_into_out_param_on_failure(&obj)) { obj->release(); } } IOReturn write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject **obj); void use_out_param_on_nonzero() { OSObject *obj; if (write_into_out_param_on_nonzero(&obj) != kIOReturnSuccess) { obj->release(); } } bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a, OS_RETURNS_RETAINED OSObject **b); void use_write_into_two_out_params() { OSObject *obj1; OSObject *obj2; if (write_into_two_out_params(&obj1, &obj2)) { obj1->release(); obj2->release(); } } void use_write_two_out_params_leak() { OSObject *obj1; OSObject *obj2; write_into_two_out_params(&obj1, &obj2); // expected-note-re{{Call to function 'write_into_two_out_params' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'a' (assuming the call returns non-zero){{$}}}} // expected-note-re@-1{{Call to function 'write_into_two_out_params' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'b' (assuming the call returns non-zero){{$}}}} } // expected-warning{{Potential leak of an object stored into 'obj1'}} // expected-warning@-1{{Potential leak of an object stored into 'obj2'}} // expected-note@-2{{Object leaked: object allocated and stored into 'obj1' is not referenced later in this execution path and has a retain count of +1}} // expected-note@-3{{Object leaked: object allocated and stored into 'obj2' is not referenced later in this execution path and has a retain count of +1}} void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a, OS_RETURNS_RETAINED OSObject **b); void use_always_write_into_two_out_params() { OSObject *obj1; OSObject *obj2; always_write_into_two_out_params(&obj1, &obj2); obj1->release(); obj2->release(); } void use_always_write_into_two_out_params_leak() { OSObject *obj1; OSObject *obj2; always_write_into_two_out_params(&obj1, &obj2); // expected-note-re{{Call to function 'always_write_into_two_out_params' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'a'{{$}}}} // expected-note-re@-1{{Call to function 'always_write_into_two_out_params' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'b'{{$}}}} } // expected-warning{{Potential leak of an object stored into 'obj1'}} // expected-warning@-1{{Potential leak of an object stored into 'obj2'}} // expected-note@-2{{Object leaked: object allocated and stored into 'obj1' is not referenced later in this execution path and has a retain count of +1}} // expected-note@-3{{Object leaked: object allocated and stored into 'obj2' is not referenced later in this execution path and has a retain count of +1}} char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject **obj); void use_out_param_osreturn_on_nonnull() { OSObject *obj; if (write_into_out_param_on_nonnull(&obj)) { obj->release(); } } void use_out_param_leak_osreturn_on_nonnull() { OSObject *obj; write_into_out_param_on_nonnull(&obj); // expected-note-re{{Call to function 'write_into_out_param_on_nonnull' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'obj' (assuming the call returns non-zero){{$}}}} } // 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}} bool write_optional_out_param(OS_RETURNS_RETAINED OSObject **obj=nullptr); void use_optional_out_param() { if (write_optional_out_param()) {}; } OSReturn write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject **obj); void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject **obj); void use_write_into_non_retained_out_param() { OSObject *obj; write_into_non_retained_out_param(&obj); } void use_write_into_non_retained_out_param_uaf() { OSObject *obj; write_into_non_retained_out_param(&obj); // expected-note-re{{Call to function 'write_into_non_retained_out_param' writes an OSObject of type 'OSObject' with a +0 retain count into an out parameter 'obj'{{$}}}} obj->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}} } void always_write_into_out_param(OS_RETURNS_RETAINED OSObject **obj); void pass_through_out_param(OSObject **obj) { always_write_into_out_param(obj); } void always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject **obj) { *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} } void use_always_write_into_out_param_has_source_leak() { OSObject *obj; always_write_into_out_param_has_source(&obj); // expected-note{{Calling 'always_write_into_out_param_has_source'}} // expected-note@-1{{Returning from 'always_write_into_out_param_has_source'}} } // 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 use_void_out_param_osreturn() { OSObject *obj; always_write_into_out_param(&obj); obj->release(); } void use_void_out_param_osreturn_leak() { OSObject *obj; always_write_into_out_param(&obj); // expected-note-re{{Call to function 'always_write_into_out_param' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'obj'{{$}}}} } // 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 use_out_param_osreturn() { OSObject *obj; if (write_into_out_param_on_os_success(&obj) == kOSReturnSuccess) { obj->release(); } } void use_out_param_leak_osreturn() { OSObject *obj; write_into_out_param_on_os_success(&obj); // expected-note-re{{Call to function 'write_into_out_param_on_os_success' writes an OSObject of type 'OSObject' with a +1 retain count into an out parameter 'obj' (assuming the call returns zero){{$}}}} } // 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; void initViaOutParamCall() { // no warning on writing into fields always_write_into_out_param(&obj); } }; bool os_consume_violation_two_args(OS_CONSUME OSObject *obj, bool extra) { if (coin()) { // expected-note{{Assuming the condition is false}} // expected-note@-1{{Taking false branch}} escape(obj); return true; } return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}} } bool os_consume_violation(OS_CONSUME OSObject *obj) { if (coin()) { // expected-note{{Assuming the condition is false}} // expected-note@-1{{Taking false branch}} escape(obj); return true; } return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}} } void os_consume_ok(OS_CONSUME OSObject *obj) { escape(obj); } void use_os_consume_violation() { OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} os_consume_violation(obj); // expected-note{{Calling 'os_consume_violation'}} // expected-note@-1{{Returning from 'os_consume_violation'}} } // expected-note{{Object leaked: object allocated and stored into 'obj' 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 'obj'}} void use_os_consume_violation_two_args() { OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} os_consume_violation_two_args(obj, coin()); // expected-note{{Calling 'os_consume_violation_two_args'}} // expected-note@-1{{Returning from 'os_consume_violation_two_args'}} } // expected-note{{Object leaked: object allocated and stored into 'obj' 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 'obj'}} void use_os_consume_ok() { OSObject *obj = new OSObject; os_consume_ok(obj); } void test_escaping_into_voidstar() { OSObject *obj = new OSObject; escape(obj); } void test_escape_has_source() { OSObject *obj = new OSObject; if (obj) escape_with_source(obj); return; } 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: 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'}} } 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(); } void check_required_cast() { OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1)); arr->release(); // no-warning } void check_cast_behavior(OSObject *obj) { OSArray *arr1 = OSDynamicCast(OSArray, obj); clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}} // expected-note@-1{{TRUE}} // expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}} // expected-warning@-3{{FALSE}} // expected-note@-4 {{FALSE}} OSArray *arr2 = OSRequiredCast(OSArray, obj); clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}} // expected-note@-1{{TRUE}} } 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); // expected-note{{Assuming dynamic cast returns null due to type mismatch}} if (!arr) // expected-note{{'arr' is null}} // expected-note@-1{{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}} // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}} 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(); } OSObject *getObject(); typedef bool (^Blk)(OSObject *); void test_escape_to_unknown_block(Blk blk) { blk(getObject()); // no-crash } using OSObjectPtr = os::smart_ptr; 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'}} // expected-note@-1{{Returning from constructor for 'smart_ptr'}} // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}} // 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{{Field 'pointer' is non-null}} // 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@-6{{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'}} // expected-note@-1{{Returning from constructor for 'smart_ptr'}} // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}} // 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{{Field 'pointer' is non-null}} // 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@-6{{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 } namespace weird_result { struct WeirdResult { int x, y, z; }; WeirdResult outParamWithWeirdResult(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj); WeirdResult testOutParamWithWeirdResult() { OSObject *obj; return outParamWithWeirdResult(&obj); // no-warning } } // namespace weird_result namespace inherited_constructor_crash { struct a { a(int); }; struct b : a { // This is an "inherited constructor". using a::a; }; void test() { // RetainCountChecker used to crash when looking for a summary // for the inherited constructor invocation. b(0); } } // namespace inherited_constructor_crash namespace ossymbol_suppression { OSSymbol *createSymbol(); void test() { OSSymbol *sym = createSymbol(); // no-warning } } // namespace ossymbol_suppression