#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) { 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);
};
bool test_meta_cast_no_leak(OSMetaClassBase *arg) {
return arg && arg->metaCast("blah") != nullptr;
}
static void consumedMismatch(OS_CONSUME OSObject *a,
OSObject *b) { a->release();
b->retain(); }
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); }
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); }
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); }
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); }
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); }
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); obj->release(); }
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; }
void use_always_write_into_out_param_has_source_leak() {
OSObject *obj;
always_write_into_out_param_has_source(&obj); }
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); }
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); }
void cleanup(OSObject **obj);
void test_cleanup_escaping() {
__attribute__((cleanup(cleanup))) OSObject *obj;
always_write_into_out_param(&obj); }
struct StructWithField {
OSObject *obj;
void initViaOutParamCall() { always_write_into_out_param(&obj);
}
};
bool os_consume_violation_two_args(OS_CONSUME OSObject *obj, bool extra) {
if (coin()) { escape(obj);
return true;
}
return false; }
bool os_consume_violation(OS_CONSUME OSObject *obj) {
if (coin()) { escape(obj);
return true;
}
return false; }
void os_consume_ok(OS_CONSUME OSObject *obj) {
escape(obj);
}
void use_os_consume_violation() {
OSObject *obj = new OSObject; os_consume_violation(obj); }
void use_os_consume_violation_two_args() {
OSObject *obj = new OSObject; os_consume_violation_two_args(obj, coin()); }
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); arr->retain(); arr->free(); arr->retain(); }
unsigned int check_leak_explicit_new() {
OSArray *arr = new OSArray; return arr->getCount(); }
unsigned int check_leak_factory() {
OSArray *arr = OSArray::withCapacity(10); return arr->getCount(); }
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(); }
void check_no_invalidation() {
OSArray *arr = OSArray::withCapacity(10); OtherStruct::doNothingToArray(arr);
}
void check_no_invalidation_other_struct() {
OSArray *arr = OSArray::withCapacity(10); OtherStruct other(arr); }
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); }
unsigned int check_leak_good_error_message() {
unsigned int out;
{
OSArray *leaked = generateArray(); out = leaked->getCount(); }
return out;
}
unsigned int check_leak_msg_temporary() {
return generateArray()->getCount(); }
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(); }
void check_cast_behavior(OSObject *obj) {
OSArray *arr1 = OSDynamicCast(OSArray, obj);
clang_analyzer_eval(arr1 == obj); OSArray *arr2 = OSRequiredCast(OSArray, obj);
clang_analyzer_eval(arr2 == obj); }
unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
OSArray *arr = OSDynamicCast(OSArray, obj);
if (arr) {
return arr->getCount();
} else {
return obj->foo(); }
}
void check_dynamic_cast_null_branch(OSObject *obj) {
OSArray *arr1 = OSArray::withCapacity(10); OSArray *arr = OSDynamicCast(OSArray, obj); if (!arr) return; arr1->release();
}
void check_dynamic_cast_null_check() {
OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); if (!arr)
return;
arr->release();
}
void check_dynamic_cast_alias() {
OSObject *originalPtr = OSObject::generateObject(1); OSArray *newPtr = OSDynamicCast(OSArray, originalPtr); if (newPtr) { originalPtr = OSObject::generateObject(42);
(void)newPtr;
}
originalPtr->release(); }
void check_dynamic_cast_alias_cond() {
OSObject *originalPtr = OSObject::generateObject(1); OSArray *newPtr = 0;
if ((newPtr = OSDynamicCast(OSArray, originalPtr))) { originalPtr = OSObject::generateObject(42);
(void)newPtr;
}
originalPtr->release(); }
void check_dynamic_cast_alias_intermediate() {
OSObject *originalPtr = OSObject::generateObject(1); OSObject *intermediate = originalPtr; OSArray *newPtr = 0;
if ((newPtr = OSDynamicCast(OSArray, intermediate))) { intermediate = OSObject::generateObject(42);
(void)newPtr;
}
intermediate->release(); }
void check_dynamic_cast_alias_intermediate_2() {
OSObject *originalPtr = OSObject::generateObject(1); OSObject *intermediate = originalPtr; OSArray *newPtr = 0;
if ((newPtr = OSDynamicCast(OSArray, intermediate))) { intermediate = OSObject::generateObject(42);
(void)originalPtr;
}
(void)newPtr;
intermediate->release(); }
void use_after_release() {
OSArray *arr = OSArray::withCapacity(10); arr->release(); arr->getCount(); }
void potential_leak() {
OSArray *arr = OSArray::withCapacity(10); arr->retain(); arr->release(); arr->getCount();
}
void proper_cleanup() {
OSArray *arr = OSArray::withCapacity(10); arr->retain(); arr->release(); arr->getCount();
arr->release(); }
unsigned int no_warning_on_getter(ArrayOwner *owner) {
OSArray *arr = owner->getArray();
return arr->getCount();
}
unsigned int warn_on_overrelease(ArrayOwner *owner) {
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(); arr->retain(); arr->release(); return arr->getCount(); }
unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
OSArray *arr = owner->getArraySourceUnknown(); arr->release(); return arr->getCount();
}
unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
OSArray *arr = owner->getArraySourceUnknown(); arr->retain(); arr->release(); return arr->getCount();
}
OSObject *getObject();
typedef bool (^Blk)(OSObject *);
void test_escape_to_unknown_block(Blk blk) {
blk(getObject()); }
using OSObjectPtr = os::smart_ptr<OSObject>;
void test_smart_ptr_uaf() {
OSObject *obj = new OSObject; {
OSObjectPtr p(obj); } obj->release(); obj->release(); }
void test_smart_ptr_leak() {
OSObject *obj = new OSObject; {
OSObjectPtr p(obj); } }
void test_smart_ptr_no_leak() {
OSObject *obj = new OSObject;
{
OSObjectPtr p(obj);
}
obj->release();
}
void test_osmetaclass_release() {
const char *name = "no_name";
const OSMetaClass *meta = OSMetaClass::copyMetaClassWithName(name);
if (!meta) {
return;
} else {
meta->releaseMetaClass();
}
}
OSObject *getRuleViolation() {
return new OSObject; }
OSObject *createRuleViolation(OSObject *param) { return param; }
void test_ostypealloc_correct_diagnostic_name() {
OSArray *arr = OSTypeAlloc(OSArray); arr->retain(); arr->release(); }
void escape_elsewhere(OSObject *obj);
void test_free_on_escaped_object_diagnostics() {
OSObject *obj = new OSObject; escape_elsewhere(obj); obj->free(); }
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) {
OSObject *ptr1 = svc->somethingMatching(table);
OSObject *ptr2 = svc->somethingMatching();
if (!table)
table = OSTypeAlloc(OSArray);
return table; }
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); }
}
namespace inherited_constructor_crash {
struct a {
a(int);
};
struct b : a {
using a::a;
};
void test() {
b(0);
}
}