1 // RUN: %clang_analyze_cc1 -std=c++14 -fblocks -analyze -analyzer-output=text\
2 // RUN: -analyzer-checker=core,osx,debug.ExprInspection -verify %s
4 #include "os_object_base.h"
5 #include "os_smart_ptr.h"
7 void clang_analyzer_eval(bool);
9 struct OSIterator
: public OSObject
{
10 static const OSMetaClass
* const metaClass
;
13 struct OSArray
: public OSObject
{
14 unsigned int getCount();
16 OSIterator
* getIterator();
18 OSObject
*identity() override
;
20 virtual OSObject
*generateObject(OSObject
*input
);
22 virtual void consumeReference(OS_CONSUME OSArray
*other
);
24 void putIntoArray(OSArray
*array
) OS_CONSUMES_THIS
;
27 void putIntoT(T
*owner
) OS_CONSUMES_THIS
;
29 static OSArray
*generateArrayHasCode() {
33 static OSArray
*withCapacity(unsigned int capacity
);
34 static void consumeArray(OS_CONSUME OSArray
* array
);
36 static OSArray
* consumeArrayHasCode(OS_CONSUME OSArray
* array
) { // expected-note{{Parameter 'array' starts at +1, as it is marked as consuming}}
37 return nullptr; // expected-warning{{Potential leak of an object of type 'OSArray'}}
38 // 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}}
42 static OS_RETURNS_NOT_RETAINED OSArray
*MaskedGetter();
43 static OS_RETURNS_RETAINED OSArray
*getOoopsActuallyCreate();
45 static const OSMetaClass
* const metaClass
;
48 struct MyArray
: public OSArray
{
49 void consumeReference(OSArray
*other
) override
;
51 OSObject
*identity() override
;
53 OSObject
*generateObject(OSObject
*input
) override
;
57 static void doNothingToArray(OSArray
*array
);
58 OtherStruct(OSArray
*arr
);
61 bool test_meta_cast_no_leak(OSMetaClassBase
*arg
) {
62 return arg
&& arg
->metaCast("blah") != nullptr;
65 static void consumedMismatch(OS_CONSUME OSObject
*a
,
66 OSObject
*b
) { // expected-note{{Parameter 'b' starts at +0}}
68 b
->retain(); // expected-note{{Reference count incremented. The object now has a +1 retain count}}
69 } // expected-warning{{Potential leak of an object of type 'OSObject'}}
70 // 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}}
73 void escape_with_source(void *p
) {}
76 typedef int kern_return_t
;
77 typedef kern_return_t IOReturn
;
78 typedef kern_return_t OSReturn
;
79 #define kOSReturnSuccess 0
80 #define kIOReturnSuccess 0
82 bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject
**obj
);
84 void use_out_param() {
86 if (write_into_out_param_on_success(&obj
)) {
91 void use_out_param_leak() {
93 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){{$}}}}
94 } // expected-warning{{Potential leak of an object stored into 'obj'}}
95 // 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}}
97 bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject
**obj
);
99 void use_out_param_leak2() {
101 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){{$}}}}
102 } // expected-warning{{Potential leak of an object stored into 'obj'}}
103 // 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}}
105 void use_out_param_on_failure() {
107 if (!write_into_out_param_on_failure(&obj
)) {
112 IOReturn
write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject
**obj
);
114 void use_out_param_on_nonzero() {
116 if (write_into_out_param_on_nonzero(&obj
) != kIOReturnSuccess
) {
121 bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject
**a
,
122 OS_RETURNS_RETAINED OSObject
**b
);
124 void use_write_into_two_out_params() {
127 if (write_into_two_out_params(&obj1
, &obj2
)) {
133 void use_write_two_out_params_leak() {
136 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){{$}}}}
137 // 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){{$}}}}
138 } // expected-warning{{Potential leak of an object stored into 'obj1'}}
139 // expected-warning@-1{{Potential leak of an object stored into 'obj2'}}
140 // 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}}
141 // 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}}
143 void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject
**a
,
144 OS_RETURNS_RETAINED OSObject
**b
);
146 void use_always_write_into_two_out_params() {
149 always_write_into_two_out_params(&obj1
, &obj2
);
154 void use_always_write_into_two_out_params_leak() {
157 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'{{$}}}}
158 // 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'{{$}}}}
159 } // expected-warning{{Potential leak of an object stored into 'obj1'}}
160 // expected-warning@-1{{Potential leak of an object stored into 'obj2'}}
161 // 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}}
162 // 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}}
164 char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject
**obj
);
166 void use_out_param_osreturn_on_nonnull() {
168 if (write_into_out_param_on_nonnull(&obj
)) {
173 void use_out_param_leak_osreturn_on_nonnull() {
175 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){{$}}}}
176 } // expected-warning{{Potential leak of an object stored into 'obj'}}
177 // 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}}
179 bool write_optional_out_param(OS_RETURNS_RETAINED OSObject
**obj
=nullptr);
181 void use_optional_out_param() {
182 if (write_optional_out_param()) {};
185 OSReturn
write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject
**obj
);
187 void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject
**obj
);
189 void use_write_into_non_retained_out_param() {
191 write_into_non_retained_out_param(&obj
);
194 void use_write_into_non_retained_out_param_uaf() {
196 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'{{$}}}}
197 obj
->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
198 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
201 void always_write_into_out_param(OS_RETURNS_RETAINED OSObject
**obj
);
203 void pass_through_out_param(OSObject
**obj
) {
204 always_write_into_out_param(obj
);
207 void always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject
**obj
) {
208 *obj
= new OSObject
; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
211 void use_always_write_into_out_param_has_source_leak() {
213 always_write_into_out_param_has_source(&obj
); // expected-note{{Calling 'always_write_into_out_param_has_source'}}
214 // expected-note@-1{{Returning from 'always_write_into_out_param_has_source'}}
215 } // expected-warning{{Potential leak of an object stored into 'obj'}}
216 // 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}}
218 void use_void_out_param_osreturn() {
220 always_write_into_out_param(&obj
);
224 void use_void_out_param_osreturn_leak() {
226 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'{{$}}}}
227 } // expected-warning{{Potential leak of an object stored into 'obj'}}
228 // 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}}
230 void use_out_param_osreturn() {
232 if (write_into_out_param_on_os_success(&obj
) == kOSReturnSuccess
) {
237 void use_out_param_leak_osreturn() {
239 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){{$}}}}
240 } // expected-warning{{Potential leak of an object stored into 'obj'}}
241 // 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}}
243 void cleanup(OSObject
**obj
);
245 void test_cleanup_escaping() {
246 __attribute__((cleanup(cleanup
))) OSObject
*obj
;
247 always_write_into_out_param(&obj
); // no-warning, the value has escaped.
250 struct StructWithField
{
253 void initViaOutParamCall() { // no warning on writing into fields
254 always_write_into_out_param(&obj
);
259 bool os_consume_violation_two_args(OS_CONSUME OSObject
*obj
, bool extra
) {
260 if (coin()) { // expected-note{{Assuming the condition is false}}
261 // expected-note@-1{{Taking false branch}}
265 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
268 bool os_consume_violation(OS_CONSUME OSObject
*obj
) {
269 if (coin()) { // expected-note{{Assuming the condition is false}}
270 // expected-note@-1{{Taking false branch}}
274 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
277 void os_consume_ok(OS_CONSUME OSObject
*obj
) {
281 void use_os_consume_violation() {
282 OSObject
*obj
= new OSObject
; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
283 os_consume_violation(obj
); // expected-note{{Calling 'os_consume_violation'}}
284 // expected-note@-1{{Returning from 'os_consume_violation'}}
285 } // 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}}
286 // expected-warning@-1{{Potential leak of an object stored into 'obj'}}
288 void use_os_consume_violation_two_args() {
289 OSObject
*obj
= new OSObject
; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
290 os_consume_violation_two_args(obj
, coin()); // expected-note{{Calling 'os_consume_violation_two_args'}}
291 // expected-note@-1{{Returning from 'os_consume_violation_two_args'}}
292 } // 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}}
293 // expected-warning@-1{{Potential leak of an object stored into 'obj'}}
295 void use_os_consume_ok() {
296 OSObject
*obj
= new OSObject
;
300 void test_escaping_into_voidstar() {
301 OSObject
*obj
= new OSObject
;
305 void test_escape_has_source() {
306 OSObject
*obj
= new OSObject
;
308 escape_with_source(obj
);
312 void test_no_infinite_check_recursion(MyArray
*arr
) {
313 OSObject
*input
= new OSObject
;
314 OSObject
*o
= arr
->generateObject(input
);
320 void check_param_attribute_propagation(MyArray
*parent
) {
321 OSArray
*arr
= new OSArray
;
322 parent
->consumeReference(arr
);
325 unsigned int check_attribute_propagation(OSArray
*arr
) {
326 OSObject
*other
= arr
->identity();
327 OSArray
*casted
= OSDynamicCast(OSArray
, other
);
329 return casted
->getCount();
333 unsigned int check_attribute_indirect_propagation(MyArray
*arr
) {
334 OSObject
*other
= arr
->identity();
335 OSArray
*casted
= OSDynamicCast(OSArray
, other
);
337 return casted
->getCount();
341 void check_consumes_this(OSArray
*owner
) {
342 OSArray
*arr
= new OSArray
;
343 arr
->putIntoArray(owner
);
346 void check_consumes_this_with_template(OSArray
*owner
) {
347 OSArray
*arr
= new OSArray
;
348 arr
->putIntoT(owner
);
351 void check_free_no_error() {
352 OSArray
*arr
= OSArray::withCapacity(10);
359 void check_free_use_after_free() {
360 OSArray
*arr
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
361 arr
->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
362 arr
->free(); // expected-note{{Object released}}
363 arr
->retain(); // expected-warning{{Reference-counted object is used after it is released}}
364 // expected-note@-1{{Reference-counted object is used after it is released}}
367 unsigned int check_leak_explicit_new() {
368 OSArray
*arr
= new OSArray
; // expected-note{{Operator 'new' returns an OSObject of type 'OSArray' with a +1 retain count}}
369 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}}
370 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
373 unsigned int check_leak_factory() {
374 OSArray
*arr
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
375 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}}
376 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
379 void check_get_object() {
380 OSObject::getObject();
383 void check_Get_object() {
384 OSObject::GetObject();
387 void check_custom_iterator_rule(OSArray
*arr
) {
388 OSIterator
*it
= arr
->getIterator();
392 void check_iterator_leak(OSArray
*arr
) {
393 arr
->getIterator(); // expected-note{{Call to method 'OSArray::getIterator' returns an OSObject of type 'OSIterator' with a +1 retain count}}
394 } // expected-note{{Object leaked: allocated object of type 'OSIterator' is not referenced later}}
395 // expected-warning@-1{{Potential leak of an object of type 'OSIterator}}'
397 void check_no_invalidation() {
398 OSArray
*arr
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
399 OtherStruct::doNothingToArray(arr
);
400 } // expected-warning{{Potential leak of an object stored into 'arr'}}
401 // expected-note@-1{{Object leaked}}
403 void check_no_invalidation_other_struct() {
404 OSArray
*arr
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
405 OtherStruct
other(arr
); // expected-warning{{Potential leak}}
406 // expected-note@-1{{Object leaked}}
409 struct ArrayOwner
: public OSObject
{
411 ArrayOwner(OSArray
*arr
) : arr(arr
) {}
413 static ArrayOwner
* create(OSArray
*arr
) {
414 return new ArrayOwner(arr
);
417 OSArray
*getArray() {
421 OSArray
*createArray() {
422 return OSArray::withCapacity(10);
425 OSArray
*createArraySourceUnknown();
427 OSArray
*getArraySourceUnknown();
430 OSArray
*generateArray() {
431 return OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
432 // expected-note@-1{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
435 unsigned int check_leak_good_error_message() {
438 OSArray
*leaked
= generateArray(); // expected-note{{Calling 'generateArray'}}
439 // expected-note@-1{{Returning from 'generateArray'}}
440 out
= leaked
->getCount(); // expected-warning{{Potential leak of an object stored into 'leaked'}}
441 // 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}}
446 unsigned int check_leak_msg_temporary() {
447 return generateArray()->getCount(); // expected-warning{{Potential leak of an object}}
448 // expected-note@-1{{Calling 'generateArray'}}
449 // expected-note@-2{{Returning from 'generateArray'}}
450 // 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}}
453 void check_confusing_getters() {
454 OSArray
*arr
= OSArray::withCapacity(10);
456 ArrayOwner
*AO
= ArrayOwner::create(arr
);
463 void check_rc_consumed() {
464 OSArray
*arr
= OSArray::withCapacity(10);
465 OSArray::consumeArray(arr
);
468 void check_rc_consume_temporary() {
469 OSArray::consumeArray(OSArray::withCapacity(10));
472 void check_rc_getter() {
473 OSArray
*arr
= OSArray::MaskedGetter();
477 void check_rc_create() {
478 OSArray
*arr
= OSArray::getOoopsActuallyCreate();
483 void check_dynamic_cast() {
484 OSArray
*arr
= OSDynamicCast(OSArray
, OSObject::generateObject(1));
488 void check_required_cast() {
489 OSArray
*arr
= OSRequiredCast(OSArray
, OSObject::generateObject(1));
490 arr
->release(); // no-warning
493 void check_cast_behavior(OSObject
*obj
) {
494 OSArray
*arr1
= OSDynamicCast(OSArray
, obj
);
495 clang_analyzer_eval(arr1
== obj
); // expected-warning{{TRUE}}
496 // expected-note@-1{{TRUE}}
497 // expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}}
498 // expected-warning@-3{{FALSE}}
499 // expected-note@-4 {{FALSE}}
500 OSArray
*arr2
= OSRequiredCast(OSArray
, obj
);
501 clang_analyzer_eval(arr2
== obj
); // expected-warning{{TRUE}}
502 // expected-note@-1{{TRUE}}
505 unsigned int check_dynamic_cast_no_null_on_orig(OSObject
*obj
) {
506 OSArray
*arr
= OSDynamicCast(OSArray
, obj
);
508 return arr
->getCount();
511 // The fact that dynamic cast has failed should not imply that
512 // the input object was null.
513 return obj
->foo(); // no-warning
517 void check_dynamic_cast_null_branch(OSObject
*obj
) {
518 OSArray
*arr1
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}}
519 OSArray
*arr
= OSDynamicCast(OSArray
, obj
); // expected-note{{Assuming dynamic cast returns null due to type mismatch}}
520 if (!arr
) // expected-note{{'arr' is null}}
521 // expected-note@-1{{Taking true branch}}
522 return; // expected-warning{{Potential leak of an object stored into 'arr1'}}
523 // expected-note@-1{{Object leaked}}
527 void check_dynamic_cast_null_check() {
528 OSArray
*arr
= OSDynamicCast(OSArray
, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}}
529 // expected-warning@-1{{Potential leak of an object}}
530 // expected-note@-2{{Object leaked}}
531 // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}}
537 void check_dynamic_cast_alias() {
538 OSObject
*originalPtr
= OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject}}
539 OSArray
*newPtr
= OSDynamicCast(OSArray
, originalPtr
); // expected-note {{'newPtr' initialized to the value of 'originalPtr'}}
540 if (newPtr
) { // expected-note {{'newPtr' is non-null}}
541 // expected-note@-1 {{Taking true branch}}
542 originalPtr
= OSObject::generateObject(42);
545 originalPtr
->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}}
546 // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}}
549 void check_dynamic_cast_alias_cond() {
550 OSObject
*originalPtr
= OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject}}
552 if ((newPtr
= OSDynamicCast(OSArray
, originalPtr
))) { // expected-note {{The value of 'originalPtr' is assigned to 'newPtr'}}
553 // expected-note@-1 {{'newPtr' is non-null}}
554 // expected-note@-2 {{Taking true branch}}
555 originalPtr
= OSObject::generateObject(42);
558 originalPtr
->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}}
559 // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}}
562 void check_dynamic_cast_alias_intermediate() {
563 OSObject
*originalPtr
= OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject of type 'OSObject' with a +1 retain count}}
564 OSObject
*intermediate
= originalPtr
; // TODO: add note here as well
566 if ((newPtr
= OSDynamicCast(OSArray
, intermediate
))) { // expected-note {{The value of 'intermediate' is assigned to 'newPtr'}}
567 // expected-note@-1 {{'newPtr' is non-null}}
568 // expected-note@-2 {{Taking true branch}}
569 intermediate
= OSObject::generateObject(42);
572 intermediate
->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}}
573 // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}}
576 void check_dynamic_cast_alias_intermediate_2() {
577 OSObject
*originalPtr
= OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject of type 'OSObject' with a +1 retain count}}
578 OSObject
*intermediate
= originalPtr
; // TODO: add note here as well
580 if ((newPtr
= OSDynamicCast(OSArray
, intermediate
))) { // expected-note {{Value assigned to 'newPtr'}}
581 // expected-note@-1 {{'newPtr' is non-null}}
582 // expected-note@-2 {{Taking true branch}}
583 intermediate
= OSObject::generateObject(42);
587 intermediate
->release(); // expected-warning {{Potential leak of an object stored into 'newPtr'}}
588 // expected-note@-1 {{Object leaked: object allocated and stored into 'newPtr' is not referenced later in this execution path and has a retain count of +1}}
591 void use_after_release() {
592 OSArray
*arr
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
593 arr
->release(); // expected-note{{Object released}}
594 arr
->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
595 // expected-note@-1{{Reference-counted object is used after it is released}}
598 void potential_leak() {
599 OSArray
*arr
= OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
600 arr
->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
601 arr
->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
603 } // expected-warning{{Potential leak of an object stored into 'arr'}}
604 // 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}}
606 void proper_cleanup() {
607 OSArray
*arr
= OSArray::withCapacity(10); // +1
609 arr
->release(); // +1
614 unsigned int no_warning_on_getter(ArrayOwner
*owner
) {
615 OSArray
*arr
= owner
->getArray();
616 return arr
->getCount();
619 unsigned int warn_on_overrelease(ArrayOwner
*owner
) {
620 // FIXME: summaries are not applied in case the source of the getter/setter
623 OSArray
*arr
= owner
->getArray();
625 return arr
->getCount();
628 unsigned int nowarn_on_release_of_created(ArrayOwner
*owner
) {
629 OSArray
*arr
= owner
->createArray();
630 unsigned int out
= arr
->getCount();
635 unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner
*owner
) {
636 OSArray
*arr
= owner
->createArraySourceUnknown();
637 unsigned int out
= arr
->getCount();
642 unsigned int no_warn_ok_release(ArrayOwner
*owner
) {
643 OSArray
*arr
= owner
->getArray(); // +0
645 arr
->release(); // +0
646 return arr
->getCount(); // no-warning
649 unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner
*owner
) {
650 OSArray
*arr
= owner
->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type 'OSArray' with a +0 retain count}}
651 arr
->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
652 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
653 return arr
->getCount();
656 unsigned int ok_release_with_unknown_source(ArrayOwner
*owner
) {
657 OSArray
*arr
= owner
->getArraySourceUnknown(); // +0
659 arr
->release(); // +0
660 return arr
->getCount();
663 OSObject
*getObject();
664 typedef bool (^Blk
)(OSObject
*);
666 void test_escape_to_unknown_block(Blk blk
) {
667 blk(getObject()); // no-crash
670 using OSObjectPtr
= os::smart_ptr
<OSObject
>;
672 void test_smart_ptr_uaf() {
673 OSObject
*obj
= new OSObject
; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
675 OSObjectPtr
p(obj
); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
676 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
677 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
678 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
679 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
680 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
681 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
682 } // expected-note{{Calling '~smart_ptr'}}
683 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
684 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
685 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
686 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
687 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
688 // expected-note@-6{{Returning from '~smart_ptr'}}
689 obj
->release(); // expected-note{{Object released}}
690 obj
->release(); // expected-warning{{Reference-counted object is used after it is released}}
691 // expected-note@-1{{Reference-counted object is used after it is released}}
694 void test_smart_ptr_leak() {
695 OSObject
*obj
= new OSObject
; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
697 OSObjectPtr
p(obj
); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
698 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
699 // expected-note@-2 {{'p' initialized here}}
700 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
701 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
702 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
703 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
704 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
705 } // expected-note{{Calling '~smart_ptr'}}
706 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
707 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
708 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
709 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
710 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
711 // expected-note@-6{{Returning from '~smart_ptr'}}
712 } // expected-warning{{Potential leak of an object stored into 'p'}}
713 // expected-note@-1{{Object leaked: object allocated and stored into 'p' is not referenced later in this execution path and has a retain count of +1}}
715 void test_smart_ptr_no_leak() {
716 OSObject
*obj
= new OSObject
;
723 void test_osmetaclass_release() {
724 const char *name
= "no_name";
725 const OSMetaClass
*meta
= OSMetaClass::copyMetaClassWithName(name
);
729 meta
->releaseMetaClass();
733 OSObject
*getRuleViolation() {
734 return new OSObject
; // expected-warning{{Potential leak of an object of type 'OSObject'}}
735 // expected-note@-1{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
736 // expected-note@-2{{Object leaked: allocated object of type 'OSObject' is returned from a function whose name ('getRuleViolation') starts with 'get'}}
739 OSObject
*createRuleViolation(OSObject
*param
) { // expected-note{{Parameter 'param' starts at +0}}
740 return param
; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
741 // expected-note@-1{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
744 void test_ostypealloc_correct_diagnostic_name() {
745 OSArray
*arr
= OSTypeAlloc(OSArray
); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}}
746 arr
->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
747 arr
->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
748 } // 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}}
749 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
751 void escape_elsewhere(OSObject
*obj
);
753 void test_free_on_escaped_object_diagnostics() {
754 OSObject
*obj
= new OSObject
; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
755 escape_elsewhere(obj
); // expected-note{{Object is now not exclusively owned}}
756 obj
->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}}
757 // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}}
760 void test_tagged_retain_no_leak() {
761 OSObject
*obj
= new OSObject
;
762 obj
->taggedRelease();
765 void test_tagged_retain_no_uaf() {
766 OSObject
*obj
= new OSObject
;
774 OSObject
*somethingMatching(OSObject
*table
= 0);
777 OSObject
*testSuppressionForMethodsEndingWithMatching(IOService
*svc
,
778 OSObject
*table
= 0) {
779 // This probably just passes table through. We should probably not make
780 // ptr1 definitely equal to table, but we should not warn about leaks.
781 OSObject
*ptr1
= svc
->somethingMatching(table
); // no-warning
783 // FIXME: This, however, should follow the Create Rule regardless.
784 // We should warn about the leak here.
785 OSObject
*ptr2
= svc
->somethingMatching(); // no-warning
788 table
= OSTypeAlloc(OSArray
);
790 // This function itself ends with "Matching"! Do not warn when we're
791 // returning from it at +0.
792 return table
; // no-warning
795 namespace weird_result
{
800 WeirdResult
outParamWithWeirdResult(OS_RETURNS_RETAINED_ON_ZERO OSObject
**obj
);
802 WeirdResult
testOutParamWithWeirdResult() {
804 return outParamWithWeirdResult(&obj
); // no-warning
806 } // namespace weird_result
808 namespace inherited_constructor_crash
{
813 // This is an "inherited constructor".
817 // RetainCountChecker used to crash when looking for a summary
818 // for the inherited constructor invocation.
821 } // namespace inherited_constructor_crash