1 // RUN: %clang_analyze_cc1 -std=c++14 -fblocks -analyzer-output=text\
2 // RUN: -analyzer-checker=core,osx,debug.ExprInspection -verify %s
3
4 #include "os_object_base.h"
5 #include "os_smart_ptr.h"
6
7 void clang_analyzer_eval(bool);
8
9 struct OSIterator : public OSObject {
10 static const OSMetaClass * const metaClass;
11 };
12
13 struct OSArray : public OSObject {
14 unsigned int getCount();
15
16 OSIterator * getIterator();
17
18 OSObject *identity() override;
19
20 virtual OSObject *generateObject(OSObject *input);
21
22 virtual void consumeReference(OS_CONSUME OSArray *other);
23
24 void putIntoArray(OSArray *array) OS_CONSUMES_THIS;
25
26 template <typename T>
27 void putIntoT(T *owner) OS_CONSUMES_THIS;
28
generateArrayHasCodeOSArray29 static OSArray *generateArrayHasCode() {
30 return new OSArray;
31 }
32
33 static OSArray *withCapacity(unsigned int capacity);
34 static void consumeArray(OS_CONSUME OSArray * array);
35
consumeArrayHasCodeOSArray36 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}}
39 }
40
41
42 static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter();
43 static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate();
44
45 static const OSMetaClass * const metaClass;
46 };
47
48 struct MyArray : public OSArray {
49 void consumeReference(OSArray *other) override;
50
51 OSObject *identity() override;
52
53 OSObject *generateObject(OSObject *input) override;
54 };
55
56 struct OtherStruct {
57 static void doNothingToArray(OSArray *array);
58 OtherStruct(OSArray *arr);
59 };
60
test_meta_cast_no_leak(OSMetaClassBase * arg)61 bool test_meta_cast_no_leak(OSMetaClassBase *arg) {
62 return arg && arg->metaCast("blah") != nullptr;
63 }
64
consumedMismatch(OS_CONSUME OSObject * a,OSObject * b)65 static void consumedMismatch(OS_CONSUME OSObject *a,
66 OSObject *b) { // expected-note{{Parameter 'b' starts at +0}}
67 a->release();
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}}
71
72 void escape(void *);
escape_with_source(void * p)73 void escape_with_source(void *p) {}
74 bool coin();
75
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
81
82 bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj);
83
use_out_param()84 void use_out_param() {
85 OSObject *obj;
86 if (write_into_out_param_on_success(&obj)) {
87 obj->release();
88 }
89 }
90
use_out_param_leak()91 void use_out_param_leak() {
92 OSObject *obj;
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}}
96
97 bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj);
98
use_out_param_leak2()99 void use_out_param_leak2() {
100 OSObject *obj;
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}}
104
use_out_param_on_failure()105 void use_out_param_on_failure() {
106 OSObject *obj;
107 if (!write_into_out_param_on_failure(&obj)) {
108 obj->release();
109 }
110 }
111
112 IOReturn write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject **obj);
113
use_out_param_on_nonzero()114 void use_out_param_on_nonzero() {
115 OSObject *obj;
116 if (write_into_out_param_on_nonzero(&obj) != kIOReturnSuccess) {
117 obj->release();
118 }
119 }
120
121 bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a,
122 OS_RETURNS_RETAINED OSObject **b);
123
use_write_into_two_out_params()124 void use_write_into_two_out_params() {
125 OSObject *obj1;
126 OSObject *obj2;
127 if (write_into_two_out_params(&obj1, &obj2)) {
128 obj1->release();
129 obj2->release();
130 }
131 }
132
use_write_two_out_params_leak()133 void use_write_two_out_params_leak() {
134 OSObject *obj1;
135 OSObject *obj2;
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}}
142
143 void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a,
144 OS_RETURNS_RETAINED OSObject **b);
145
use_always_write_into_two_out_params()146 void use_always_write_into_two_out_params() {
147 OSObject *obj1;
148 OSObject *obj2;
149 always_write_into_two_out_params(&obj1, &obj2);
150 obj1->release();
151 obj2->release();
152 }
153
use_always_write_into_two_out_params_leak()154 void use_always_write_into_two_out_params_leak() {
155 OSObject *obj1;
156 OSObject *obj2;
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}}
163
164 char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject **obj);
165
use_out_param_osreturn_on_nonnull()166 void use_out_param_osreturn_on_nonnull() {
167 OSObject *obj;
168 if (write_into_out_param_on_nonnull(&obj)) {
169 obj->release();
170 }
171 }
172
use_out_param_leak_osreturn_on_nonnull()173 void use_out_param_leak_osreturn_on_nonnull() {
174 OSObject *obj;
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}}
178
179 bool write_optional_out_param(OS_RETURNS_RETAINED OSObject **obj=nullptr);
180
use_optional_out_param()181 void use_optional_out_param() {
182 if (write_optional_out_param()) {};
183 }
184
185 OSReturn write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject **obj);
186
187 void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject **obj);
188
use_write_into_non_retained_out_param()189 void use_write_into_non_retained_out_param() {
190 OSObject *obj;
191 write_into_non_retained_out_param(&obj);
192 }
193
use_write_into_non_retained_out_param_uaf()194 void use_write_into_non_retained_out_param_uaf() {
195 OSObject *obj;
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}}
199 }
200
201 void always_write_into_out_param(OS_RETURNS_RETAINED OSObject **obj);
202
pass_through_out_param(OSObject ** obj)203 void pass_through_out_param(OSObject **obj) {
204 always_write_into_out_param(obj);
205 }
206
always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject ** 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}}
209 }
210
use_always_write_into_out_param_has_source_leak()211 void use_always_write_into_out_param_has_source_leak() {
212 OSObject *obj;
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}}
217
use_void_out_param_osreturn()218 void use_void_out_param_osreturn() {
219 OSObject *obj;
220 always_write_into_out_param(&obj);
221 obj->release();
222 }
223
use_void_out_param_osreturn_leak()224 void use_void_out_param_osreturn_leak() {
225 OSObject *obj;
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}}
229
use_out_param_osreturn()230 void use_out_param_osreturn() {
231 OSObject *obj;
232 if (write_into_out_param_on_os_success(&obj) == kOSReturnSuccess) {
233 obj->release();
234 }
235 }
236
use_out_param_leak_osreturn()237 void use_out_param_leak_osreturn() {
238 OSObject *obj;
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}}
242
243 void cleanup(OSObject **obj);
244
test_cleanup_escaping()245 void test_cleanup_escaping() {
246 __attribute__((cleanup(cleanup))) OSObject *obj;
247 always_write_into_out_param(&obj); // no-warning, the value has escaped.
248 }
249
250 struct StructWithField {
251 OSObject *obj;
252
initViaOutParamCallStructWithField253 void initViaOutParamCall() { // no warning on writing into fields
254 always_write_into_out_param(&obj);
255 }
256
257 };
258
os_consume_violation_two_args(OS_CONSUME OSObject * obj,bool extra)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}}
262 escape(obj);
263 return true;
264 }
265 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
266 }
267
os_consume_violation(OS_CONSUME OSObject * obj)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}}
271 escape(obj);
272 return true;
273 }
274 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
275 }
276
os_consume_ok(OS_CONSUME OSObject * obj)277 void os_consume_ok(OS_CONSUME OSObject *obj) {
278 escape(obj);
279 }
280
use_os_consume_violation()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'}}
287
use_os_consume_violation_two_args()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'}}
294
use_os_consume_ok()295 void use_os_consume_ok() {
296 OSObject *obj = new OSObject;
297 os_consume_ok(obj);
298 }
299
test_escaping_into_voidstar()300 void test_escaping_into_voidstar() {
301 OSObject *obj = new OSObject;
302 escape(obj);
303 }
304
test_escape_has_source()305 void test_escape_has_source() {
306 OSObject *obj = new OSObject;
307 if (obj)
308 escape_with_source(obj);
309 return;
310 }
311
test_no_infinite_check_recursion(MyArray * arr)312 void test_no_infinite_check_recursion(MyArray *arr) {
313 OSObject *input = new OSObject;
314 OSObject *o = arr->generateObject(input);
315 o->release();
316 input->release();
317 }
318
319
check_param_attribute_propagation(MyArray * parent)320 void check_param_attribute_propagation(MyArray *parent) {
321 OSArray *arr = new OSArray;
322 parent->consumeReference(arr);
323 }
324
check_attribute_propagation(OSArray * arr)325 unsigned int check_attribute_propagation(OSArray *arr) {
326 OSObject *other = arr->identity();
327 OSArray *casted = OSDynamicCast(OSArray, other);
328 if (casted)
329 return casted->getCount();
330 return 0;
331 }
332
check_attribute_indirect_propagation(MyArray * arr)333 unsigned int check_attribute_indirect_propagation(MyArray *arr) {
334 OSObject *other = arr->identity();
335 OSArray *casted = OSDynamicCast(OSArray, other);
336 if (casted)
337 return casted->getCount();
338 return 0;
339 }
340
check_consumes_this(OSArray * owner)341 void check_consumes_this(OSArray *owner) {
342 OSArray *arr = new OSArray;
343 arr->putIntoArray(owner);
344 }
345
check_consumes_this_with_template(OSArray * owner)346 void check_consumes_this_with_template(OSArray *owner) {
347 OSArray *arr = new OSArray;
348 arr->putIntoT(owner);
349 }
350
check_free_no_error()351 void check_free_no_error() {
352 OSArray *arr = OSArray::withCapacity(10);
353 arr->retain();
354 arr->retain();
355 arr->retain();
356 arr->free();
357 }
358
check_free_use_after_free()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}}
365 }
366
check_leak_explicit_new()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'}}
371 }
372
check_leak_factory()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'}}
377 }
378
check_get_object()379 void check_get_object() {
380 OSObject::getObject();
381 }
382
check_Get_object()383 void check_Get_object() {
384 OSObject::GetObject();
385 }
386
check_custom_iterator_rule(OSArray * arr)387 void check_custom_iterator_rule(OSArray *arr) {
388 OSIterator *it = arr->getIterator();
389 it->release();
390 }
391
check_iterator_leak(OSArray * arr)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}}'
396
check_no_invalidation()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}}
402
check_no_invalidation_other_struct()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}}
407 }
408
409 struct ArrayOwner : public OSObject {
410 OSArray *arr;
ArrayOwnerArrayOwner411 ArrayOwner(OSArray *arr) : arr(arr) {}
412
createArrayOwner413 static ArrayOwner* create(OSArray *arr) {
414 return new ArrayOwner(arr);
415 }
416
getArrayArrayOwner417 OSArray *getArray() {
418 return arr;
419 }
420
createArrayArrayOwner421 OSArray *createArray() {
422 return OSArray::withCapacity(10);
423 }
424
425 OSArray *createArraySourceUnknown();
426
427 OSArray *getArraySourceUnknown();
428 };
429
generateArray()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}}
433 }
434
check_leak_good_error_message()435 unsigned int check_leak_good_error_message() {
436 unsigned int out;
437 {
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}}
442 }
443 return out;
444 }
445
check_leak_msg_temporary()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}}
451 }
452
check_confusing_getters()453 void check_confusing_getters() {
454 OSArray *arr = OSArray::withCapacity(10);
455
456 ArrayOwner *AO = ArrayOwner::create(arr);
457 AO->getArray();
458
459 AO->release();
460 arr->release();
461 }
462
check_rc_consumed()463 void check_rc_consumed() {
464 OSArray *arr = OSArray::withCapacity(10);
465 OSArray::consumeArray(arr);
466 }
467
check_rc_consume_temporary()468 void check_rc_consume_temporary() {
469 OSArray::consumeArray(OSArray::withCapacity(10));
470 }
471
check_rc_getter()472 void check_rc_getter() {
473 OSArray *arr = OSArray::MaskedGetter();
474 (void)arr;
475 }
476
check_rc_create()477 void check_rc_create() {
478 OSArray *arr = OSArray::getOoopsActuallyCreate();
479 arr->release();
480 }
481
482
check_dynamic_cast()483 void check_dynamic_cast() {
484 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1));
485 arr->release();
486 }
487
check_required_cast()488 void check_required_cast() {
489 OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1));
490 arr->release(); // no-warning
491 }
492
check_cast_behavior(OSObject * obj)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}}
503 }
504
check_dynamic_cast_no_null_on_orig(OSObject * obj)505 unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
506 OSArray *arr = OSDynamicCast(OSArray, obj);
507 if (arr) {
508 return arr->getCount();
509 } else {
510
511 // The fact that dynamic cast has failed should not imply that
512 // the input object was null.
513 return obj->foo(); // no-warning
514 }
515 }
516
check_dynamic_cast_null_branch(OSObject * obj)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}}
524 arr1->release();
525 }
526
check_dynamic_cast_null_check()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}}
532 if (!arr)
533 return;
534 arr->release();
535 }
536
check_dynamic_cast_alias()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);
543 (void)newPtr;
544 }
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}}
547 }
548
check_dynamic_cast_alias_cond()549 void check_dynamic_cast_alias_cond() {
550 OSObject *originalPtr = OSObject::generateObject(1); // expected-note {{Call to method 'OSObject::generateObject' returns an OSObject}}
551 OSArray *newPtr = 0;
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);
556 (void)newPtr;
557 }
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}}
560 }
561
check_dynamic_cast_alias_intermediate()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
565 OSArray *newPtr = 0;
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);
570 (void)newPtr;
571 }
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}}
574 }
575
check_dynamic_cast_alias_intermediate_2()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
579 OSArray *newPtr = 0;
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);
584 (void)originalPtr;
585 }
586 (void)newPtr;
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}}
589 }
590
use_after_release()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}}
596 }
597
potential_leak()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}}
602 arr->getCount();
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}}
605
proper_cleanup()606 void proper_cleanup() {
607 OSArray *arr = OSArray::withCapacity(10); // +1
608 arr->retain(); // +2
609 arr->release(); // +1
610 arr->getCount();
611 arr->release(); // 0
612 }
613
no_warning_on_getter(ArrayOwner * owner)614 unsigned int no_warning_on_getter(ArrayOwner *owner) {
615 OSArray *arr = owner->getArray();
616 return arr->getCount();
617 }
618
warn_on_overrelease(ArrayOwner * owner)619 unsigned int warn_on_overrelease(ArrayOwner *owner) {
620 // FIXME: summaries are not applied in case the source of the getter/setter
621 // is known.
622 OSArray *arr = owner->getArray();
623 arr->release();
624 return arr->getCount();
625 }
626
nowarn_on_release_of_created(ArrayOwner * owner)627 unsigned int nowarn_on_release_of_created(ArrayOwner *owner) {
628 OSArray *arr = owner->createArray();
629 unsigned int out = arr->getCount();
630 arr->release();
631 return out;
632 }
633
nowarn_on_release_of_created_source_unknown(ArrayOwner * owner)634 unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) {
635 OSArray *arr = owner->createArraySourceUnknown();
636 unsigned int out = arr->getCount();
637 arr->release();
638 return out;
639 }
640
no_warn_ok_release(ArrayOwner * owner)641 unsigned int no_warn_ok_release(ArrayOwner *owner) {
642 OSArray *arr = owner->getArray(); // +0
643 arr->retain(); // +1
644 arr->release(); // +0
645 return arr->getCount(); // no-warning
646 }
647
warn_on_overrelease_with_unknown_source(ArrayOwner * owner)648 unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
649 OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type 'OSArray' with a +0 retain count}}
650 arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
651 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
652 return arr->getCount();
653 }
654
ok_release_with_unknown_source(ArrayOwner * owner)655 unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
656 OSArray *arr = owner->getArraySourceUnknown(); // +0
657 arr->retain(); // +1
658 arr->release(); // +0
659 return arr->getCount();
660 }
661
662 OSObject *getObject();
663 typedef bool (^Blk)(OSObject *);
664
test_escape_to_unknown_block(Blk blk)665 void test_escape_to_unknown_block(Blk blk) {
666 blk(getObject()); // no-crash
667 }
668
669 using OSObjectPtr = os::smart_ptr<OSObject>;
670
test_smart_ptr_uaf()671 void test_smart_ptr_uaf() {
672 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
673 {
674 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
675 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
676 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
677 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
678 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
679 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
680 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
681 } // expected-note{{Calling '~smart_ptr'}}
682 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
683 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
684 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
685 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
686 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
687 // expected-note@-6{{Returning from '~smart_ptr'}}
688 obj->release(); // expected-note{{Object released}}
689 obj->release(); // expected-warning{{Reference-counted object is used after it is released}}
690 // expected-note@-1{{Reference-counted object is used after it is released}}
691 }
692
test_smart_ptr_leak()693 void test_smart_ptr_leak() {
694 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
695 {
696 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
697 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
698 // expected-note@-2 {{'p' initialized here}}
699 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
700 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
701 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
702 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
703 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
704 } // expected-note{{Calling '~smart_ptr'}}
705 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
706 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
707 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
708 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
709 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
710 // expected-note@-6{{Returning from '~smart_ptr'}}
711 } // expected-warning{{Potential leak of an object stored into 'p'}}
712 // 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}}
713
test_smart_ptr_no_leak()714 void test_smart_ptr_no_leak() {
715 OSObject *obj = new OSObject;
716 {
717 OSObjectPtr p(obj);
718 }
719 obj->release();
720 }
721
test_osmetaclass_release()722 void test_osmetaclass_release() {
723 const char *name = "no_name";
724 const OSMetaClass *meta = OSMetaClass::copyMetaClassWithName(name);
725 if (!meta) {
726 return;
727 } else {
728 meta->releaseMetaClass();
729 }
730 }
731
getRuleViolation()732 OSObject *getRuleViolation() {
733 return new OSObject; // expected-warning{{Potential leak of an object of type 'OSObject'}}
734 // expected-note@-1{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
735 // expected-note@-2{{Object leaked: allocated object of type 'OSObject' is returned from a function whose name ('getRuleViolation') starts with 'get'}}
736 }
737
createRuleViolation(OSObject * param)738 OSObject *createRuleViolation(OSObject *param) { // expected-note{{Parameter 'param' starts at +0}}
739 return param; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
740 // expected-note@-1{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
741 }
742
test_ostypealloc_correct_diagnostic_name()743 void test_ostypealloc_correct_diagnostic_name() {
744 OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}}
745 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
746 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
747 } // 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}}
748 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
749
750 void escape_elsewhere(OSObject *obj);
751
test_free_on_escaped_object_diagnostics()752 void test_free_on_escaped_object_diagnostics() {
753 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
754 escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}}
755 obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}}
756 // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}}
757 }
758
test_tagged_retain_no_leak()759 void test_tagged_retain_no_leak() {
760 OSObject *obj = new OSObject;
761 obj->taggedRelease();
762 }
763
test_tagged_retain_no_uaf()764 void test_tagged_retain_no_uaf() {
765 OSObject *obj = new OSObject;
766 obj->taggedRetain();
767 obj->release();
768 obj->release();
769 }
770
771 class IOService {
772 public:
773 OSObject *somethingMatching(OSObject *table = 0);
774 };
775
testSuppressionForMethodsEndingWithMatching(IOService * svc,OSObject * table=0)776 OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc,
777 OSObject *table = 0) {
778 // This probably just passes table through. We should probably not make
779 // ptr1 definitely equal to table, but we should not warn about leaks.
780 OSObject *ptr1 = svc->somethingMatching(table); // no-warning
781
782 // FIXME: This, however, should follow the Create Rule regardless.
783 // We should warn about the leak here.
784 OSObject *ptr2 = svc->somethingMatching(); // no-warning
785
786 if (!table)
787 table = OSTypeAlloc(OSArray);
788
789 // This function itself ends with "Matching"! Do not warn when we're
790 // returning from it at +0.
791 return table; // no-warning
792 }
793
794 namespace weird_result {
795 struct WeirdResult {
796 int x, y, z;
797 };
798
799 WeirdResult outParamWithWeirdResult(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj);
800
testOutParamWithWeirdResult()801 WeirdResult testOutParamWithWeirdResult() {
802 OSObject *obj;
803 return outParamWithWeirdResult(&obj); // no-warning
804 }
805 } // namespace weird_result
806
807 namespace inherited_constructor_crash {
808 struct a {
809 a(int);
810 };
811 struct b : a {
812 // This is an "inherited constructor".
813 using a::a;
814 };
test()815 void test() {
816 // RetainCountChecker used to crash when looking for a summary
817 // for the inherited constructor invocation.
818 b(0);
819 }
820 } // namespace inherited_constructor_crash
821