forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			757 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			757 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
| // 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 <typename T>
 | |
|   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;
 | |
| };
 | |
| 
 | |
| 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<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{{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<OSObject>'}}
 | |
|    // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
 | |
|     // 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
 |