summaryrefslogtreecommitdiffstats
path: root/test/Analysis/osobject-retain-release.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/Analysis/osobject-retain-release.cpp')
-rw-r--r--test/Analysis/osobject-retain-release.cpp244
1 files changed, 227 insertions, 17 deletions
diff --git a/test/Analysis/osobject-retain-release.cpp b/test/Analysis/osobject-retain-release.cpp
index b8eb462d20..9d11a06231 100644
--- a/test/Analysis/osobject-retain-release.cpp
+++ b/test/Analysis/osobject-retain-release.cpp
@@ -1,9 +1,12 @@
-// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=core,osx -analyzer-output=text -verify %s
+// 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))
@@ -90,15 +93,201 @@ struct OSMetaClassBase {
};
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}}
+
+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 does not consume the reference}}
+ 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) {
@@ -106,12 +295,19 @@ void os_consume_ok(OS_CONSUME OSObject *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}}
+ 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);
@@ -122,6 +318,13 @@ void test_escaping_into_voidstar() {
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);
@@ -170,7 +373,7 @@ void check_free_no_error() {
}
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}}
+ 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}}
@@ -178,13 +381,13 @@ void check_free_use_after_free() {
}
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}}
+ 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}}
+ 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'}}
}
@@ -203,18 +406,18 @@ void check_custom_iterator_rule(OSArray *arr) {
}
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}}
+ 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}}
+ 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}}
+ 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}}
}
@@ -241,8 +444,8 @@ struct ArrayOwner : public OSObject {
};
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}}
+ 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() {
@@ -260,7 +463,7 @@ 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}}
+ // 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() {
@@ -329,14 +532,14 @@ void check_dynamic_cast_null_check() {
}
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}}
+ 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}}
+ 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();
@@ -387,7 +590,7 @@ unsigned int no_warn_ok_release(ArrayOwner *owner) {
}
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}}
+ 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();
@@ -399,3 +602,10 @@ unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
arr->release(); // +0
return arr->getCount();
}
+
+OSObject *getObject();
+typedef bool (^Blk)(OSObject *);
+
+void test_escape_to_unknown_block(Blk blk) {
+ blk(getObject()); // no-crash
+}