1 // RUN: %clang_analyze_cc1 -std=c++14 -fblocks -analyze -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 // These are never refcounted.
57 struct OSSymbol : OSObject {};
58
59 struct OtherStruct {
60 static void doNothingToArray(OSArray *array);
61 OtherStruct(OSArray *arr);
62 };
63
test_meta_cast_no_leak(OSMetaClassBase * arg)64 bool test_meta_cast_no_leak(OSMetaClassBase *arg) {
65 return arg && arg->metaCast("blah") != nullptr;
66 }
67
consumedMismatch(OS_CONSUME OSObject * a,OSObject * b)68 static void consumedMismatch(OS_CONSUME OSObject *a,
69 OSObject *b) { // expected-note{{Parameter 'b' starts at +0}}
70 a->release();
71 b->retain(); // expected-note{{Reference count incremented. The object now has a +1 retain count}}
72 } // expected-warning{{Potential leak of an object of type 'OSObject'}}
73 // 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}}
74
75 void escape(void *);
escape_with_source(void * p)76 void escape_with_source(void *p) {}
77 bool coin();
78
79 typedef int kern_return_t;
80 typedef kern_return_t IOReturn;
81 typedef kern_return_t OSReturn;
82 #define kOSReturnSuccess 0
83 #define kIOReturnSuccess 0
84
85 bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj);
86
use_out_param()87 void use_out_param() {
88 OSObject *obj;
89 if (write_into_out_param_on_success(&obj)) {
90 obj->release();
91 }
92 }
93
use_out_param_leak()94 void use_out_param_leak() {
95 OSObject *obj;
96 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){{$}}}}
97 } // expected-warning{{Potential leak of an object stored into 'obj'}}
98 // 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}}
99
100 bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj);
101
use_out_param_leak2()102 void use_out_param_leak2() {
103 OSObject *obj;
104 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){{$}}}}
105 } // expected-warning{{Potential leak of an object stored into 'obj'}}
106 // 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}}
107
use_out_param_on_failure()108 void use_out_param_on_failure() {
109 OSObject *obj;
110 if (!write_into_out_param_on_failure(&obj)) {
111 obj->release();
112 }
113 }
114
115 IOReturn write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject **obj);
116
use_out_param_on_nonzero()117 void use_out_param_on_nonzero() {
118 OSObject *obj;
119 if (write_into_out_param_on_nonzero(&obj) != kIOReturnSuccess) {
120 obj->release();
121 }
122 }
123
124 bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a,
125 OS_RETURNS_RETAINED OSObject **b);
126
use_write_into_two_out_params()127 void use_write_into_two_out_params() {
128 OSObject *obj1;
129 OSObject *obj2;
130 if (write_into_two_out_params(&obj1, &obj2)) {
131 obj1->release();
132 obj2->release();
133 }
134 }
135
use_write_two_out_params_leak()136 void use_write_two_out_params_leak() {
137 OSObject *obj1;
138 OSObject *obj2;
139 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){{$}}}}
140 // 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){{$}}}}
141 } // expected-warning{{Potential leak of an object stored into 'obj1'}}
142 // expected-warning@-1{{Potential leak of an object stored into 'obj2'}}
143 // 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}}
144 // 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}}
145
146 void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a,
147 OS_RETURNS_RETAINED OSObject **b);
148
use_always_write_into_two_out_params()149 void use_always_write_into_two_out_params() {
150 OSObject *obj1;
151 OSObject *obj2;
152 always_write_into_two_out_params(&obj1, &obj2);
153 obj1->release();
154 obj2->release();
155 }
156
use_always_write_into_two_out_params_leak()157 void use_always_write_into_two_out_params_leak() {
158 OSObject *obj1;
159 OSObject *obj2;
160 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'{{$}}}}
161 // 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'{{$}}}}
162 } // expected-warning{{Potential leak of an object stored into 'obj1'}}
163 // expected-warning@-1{{Potential leak of an object stored into 'obj2'}}
164 // 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}}
165 // 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}}
166
167 char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject **obj);
168
use_out_param_osreturn_on_nonnull()169 void use_out_param_osreturn_on_nonnull() {
170 OSObject *obj;
171 if (write_into_out_param_on_nonnull(&obj)) {
172 obj->release();
173 }
174 }
175
use_out_param_leak_osreturn_on_nonnull()176 void use_out_param_leak_osreturn_on_nonnull() {
177 OSObject *obj;
178 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){{$}}}}
179 } // expected-warning{{Potential leak of an object stored into 'obj'}}
180 // 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}}
181
182 bool write_optional_out_param(OS_RETURNS_RETAINED OSObject **obj=nullptr);
183
use_optional_out_param()184 void use_optional_out_param() {
185 if (write_optional_out_param()) {};
186 }
187
188 OSReturn write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject **obj);
189
190 void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject **obj);
191
use_write_into_non_retained_out_param()192 void use_write_into_non_retained_out_param() {
193 OSObject *obj;
194 write_into_non_retained_out_param(&obj);
195 }
196
use_write_into_non_retained_out_param_uaf()197 void use_write_into_non_retained_out_param_uaf() {
198 OSObject *obj;
199 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'{{$}}}}
200 obj->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
201 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
202 }
203
204 void always_write_into_out_param(OS_RETURNS_RETAINED OSObject **obj);
205
pass_through_out_param(OSObject ** obj)206 void pass_through_out_param(OSObject **obj) {
207 always_write_into_out_param(obj);
208 }
209
always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject ** obj)210 void always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject **obj) {
211 *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
212 }
213
use_always_write_into_out_param_has_source_leak()214 void use_always_write_into_out_param_has_source_leak() {
215 OSObject *obj;
216 always_write_into_out_param_has_source(&obj); // expected-note{{Calling 'always_write_into_out_param_has_source'}}
217 // expected-note@-1{{Returning from 'always_write_into_out_param_has_source'}}
218 } // expected-warning{{Potential leak of an object stored into 'obj'}}
219 // 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}}
220
use_void_out_param_osreturn()221 void use_void_out_param_osreturn() {
222 OSObject *obj;
223 always_write_into_out_param(&obj);
224 obj->release();
225 }
226
use_void_out_param_osreturn_leak()227 void use_void_out_param_osreturn_leak() {
228 OSObject *obj;
229 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'{{$}}}}
230 } // expected-warning{{Potential leak of an object stored into 'obj'}}
231 // 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}}
232
use_out_param_osreturn()233 void use_out_param_osreturn() {
234 OSObject *obj;
235 if (write_into_out_param_on_os_success(&obj) == kOSReturnSuccess) {
236 obj->release();
237 }
238 }
239
use_out_param_leak_osreturn()240 void use_out_param_leak_osreturn() {
241 OSObject *obj;
242 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){{$}}}}
243 } // expected-warning{{Potential leak of an object stored into 'obj'}}
244 // 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}}
245
246 void cleanup(OSObject **obj);
247
test_cleanup_escaping()248 void test_cleanup_escaping() {
249 __attribute__((cleanup(cleanup))) OSObject *obj;
250 always_write_into_out_param(&obj); // no-warning, the value has escaped.
251 }
252
253 struct StructWithField {
254 OSObject *obj;
255
initViaOutParamCallStructWithField256 void initViaOutParamCall() { // no warning on writing into fields
257 always_write_into_out_param(&obj);
258 }
259
260 };
261
os_consume_violation_two_args(OS_CONSUME OSObject * obj,bool extra)262 bool os_consume_violation_two_args(OS_CONSUME OSObject *obj, bool extra) {
263 if (coin()) { // expected-note{{Assuming the condition is false}}
264 // expected-note@-1{{Taking false branch}}
265 escape(obj);
266 return true;
267 }
268 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
269 }
270
os_consume_violation(OS_CONSUME OSObject * obj)271 bool os_consume_violation(OS_CONSUME OSObject *obj) {
272 if (coin()) { // expected-note{{Assuming the condition is false}}
273 // expected-note@-1{{Taking false branch}}
274 escape(obj);
275 return true;
276 }
277 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
278 }
279
os_consume_ok(OS_CONSUME OSObject * obj)280 void os_consume_ok(OS_CONSUME OSObject *obj) {
281 escape(obj);
282 }
283
use_os_consume_violation()284 void use_os_consume_violation() {
285 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
286 os_consume_violation(obj); // expected-note{{Calling 'os_consume_violation'}}
287 // expected-note@-1{{Returning from 'os_consume_violation'}}
288 } // 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}}
289 // expected-warning@-1{{Potential leak of an object stored into 'obj'}}
290
use_os_consume_violation_two_args()291 void use_os_consume_violation_two_args() {
292 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
293 os_consume_violation_two_args(obj, coin()); // expected-note{{Calling 'os_consume_violation_two_args'}}
294 // expected-note@-1{{Returning from 'os_consume_violation_two_args'}}
295 } // 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}}
296 // expected-warning@-1{{Potential leak of an object stored into 'obj'}}
297
use_os_consume_ok()298 void use_os_consume_ok() {
299 OSObject *obj = new OSObject;
300 os_consume_ok(obj);
301 }
302
test_escaping_into_voidstar()303 void test_escaping_into_voidstar() {
304 OSObject *obj = new OSObject;
305 escape(obj);
306 }
307
test_escape_has_source()308 void test_escape_has_source() {
309 OSObject *obj = new OSObject;
310 if (obj)
311 escape_with_source(obj);
312 return;
313 }
314
test_no_infinite_check_recursion(MyArray * arr)315 void test_no_infinite_check_recursion(MyArray *arr) {
316 OSObject *input = new OSObject;
317 OSObject *o = arr->generateObject(input);
318 o->release();
319 input->release();
320 }
321
322
check_param_attribute_propagation(MyArray * parent)323 void check_param_attribute_propagation(MyArray *parent) {
324 OSArray *arr = new OSArray;
325 parent->consumeReference(arr);
326 }
327
check_attribute_propagation(OSArray * arr)328 unsigned int check_attribute_propagation(OSArray *arr) {
329 OSObject *other = arr->identity();
330 OSArray *casted = OSDynamicCast(OSArray, other);
331 if (casted)
332 return casted->getCount();
333 return 0;
334 }
335
check_attribute_indirect_propagation(MyArray * arr)336 unsigned int check_attribute_indirect_propagation(MyArray *arr) {
337 OSObject *other = arr->identity();
338 OSArray *casted = OSDynamicCast(OSArray, other);
339 if (casted)
340 return casted->getCount();
341 return 0;
342 }
343
check_consumes_this(OSArray * owner)344 void check_consumes_this(OSArray *owner) {
345 OSArray *arr = new OSArray;
346 arr->putIntoArray(owner);
347 }
348
check_consumes_this_with_template(OSArray * owner)349 void check_consumes_this_with_template(OSArray *owner) {
350 OSArray *arr = new OSArray;
351 arr->putIntoT(owner);
352 }
353
check_free_no_error()354 void check_free_no_error() {
355 OSArray *arr = OSArray::withCapacity(10);
356 arr->retain();
357 arr->retain();
358 arr->retain();
359 arr->free();
360 }
361
check_free_use_after_free()362 void check_free_use_after_free() {
363 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
364 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
365 arr->free(); // expected-note{{Object released}}
366 arr->retain(); // expected-warning{{Reference-counted object is used after it is released}}
367 // expected-note@-1{{Reference-counted object is used after it is released}}
368 }
369
check_leak_explicit_new()370 unsigned int check_leak_explicit_new() {
371 OSArray *arr = new OSArray; // expected-note{{Operator 'new' returns an OSObject of type 'OSArray' with a +1 retain count}}
372 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}}
373 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
374 }
375
check_leak_factory()376 unsigned int check_leak_factory() {
377 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
378 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}}
379 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
380 }
381
check_get_object()382 void check_get_object() {
383 OSObject::getObject();
384 }
385
check_Get_object()386 void check_Get_object() {
387 OSObject::GetObject();
388 }
389
check_custom_iterator_rule(OSArray * arr)390 void check_custom_iterator_rule(OSArray *arr) {
391 OSIterator *it = arr->getIterator();
392 it->release();
393 }
394
check_iterator_leak(OSArray * arr)395 void check_iterator_leak(OSArray *arr) {
396 arr->getIterator(); // expected-note{{Call to method 'OSArray::getIterator' returns an OSObject of type 'OSIterator' with a +1 retain count}}
397 } // expected-note{{Object leaked: allocated object of type 'OSIterator' is not referenced later}}
398 // expected-warning@-1{{Potential leak of an object of type 'OSIterator}}'
399
check_no_invalidation()400 void check_no_invalidation() {
401 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
402 OtherStruct::doNothingToArray(arr);
403 } // expected-warning{{Potential leak of an object stored into 'arr'}}
404 // expected-note@-1{{Object leaked}}
405
check_no_invalidation_other_struct()406 void check_no_invalidation_other_struct() {
407 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
408 OtherStruct other(arr); // expected-warning{{Potential leak}}
409 // expected-note@-1{{Object leaked}}
410 }
411
412 struct ArrayOwner : public OSObject {
413 OSArray *arr;
ArrayOwnerArrayOwner414 ArrayOwner(OSArray *arr) : arr(arr) {}
415
createArrayOwner416 static ArrayOwner* create(OSArray *arr) {
417 return new ArrayOwner(arr);
418 }
419
getArrayArrayOwner420 OSArray *getArray() {
421 return arr;
422 }
423
createArrayArrayOwner424 OSArray *createArray() {
425 return OSArray::withCapacity(10);
426 }
427
428 OSArray *createArraySourceUnknown();
429
430 OSArray *getArraySourceUnknown();
431 };
432
generateArray()433 OSArray *generateArray() {
434 return OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
435 // expected-note@-1{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
436 }
437
check_leak_good_error_message()438 unsigned int check_leak_good_error_message() {
439 unsigned int out;
440 {
441 OSArray *leaked = generateArray(); // expected-note{{Calling 'generateArray'}}
442 // expected-note@-1{{Returning from 'generateArray'}}
443 out = leaked->getCount(); // expected-warning{{Potential leak of an object stored into 'leaked'}}
444 // 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}}
445 }
446 return out;
447 }
448
check_leak_msg_temporary()449 unsigned int check_leak_msg_temporary() {
450 return generateArray()->getCount(); // expected-warning{{Potential leak of an object}}
451 // expected-note@-1{{Calling 'generateArray'}}
452 // expected-note@-2{{Returning from 'generateArray'}}
453 // 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}}
454 }
455
check_confusing_getters()456 void check_confusing_getters() {
457 OSArray *arr = OSArray::withCapacity(10);
458
459 ArrayOwner *AO = ArrayOwner::create(arr);
460 AO->getArray();
461
462 AO->release();
463 arr->release();
464 }
465
check_rc_consumed()466 void check_rc_consumed() {
467 OSArray *arr = OSArray::withCapacity(10);
468 OSArray::consumeArray(arr);
469 }
470
check_rc_consume_temporary()471 void check_rc_consume_temporary() {
472 OSArray::consumeArray(OSArray::withCapacity(10));
473 }
474
check_rc_getter()475 void check_rc_getter() {
476 OSArray *arr = OSArray::MaskedGetter();
477 (void)arr;
478 }
479
check_rc_create()480 void check_rc_create() {
481 OSArray *arr = OSArray::getOoopsActuallyCreate();
482 arr->release();
483 }
484
485
check_dynamic_cast()486 void check_dynamic_cast() {
487 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1));
488 arr->release();
489 }
490
check_required_cast()491 void check_required_cast() {
492 OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1));
493 arr->release(); // no-warning
494 }
495
check_cast_behavior(OSObject * obj)496 void check_cast_behavior(OSObject *obj) {
497 OSArray *arr1 = OSDynamicCast(OSArray, obj);
498 clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}}
499 // expected-note@-1{{TRUE}}
500 // expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}}
501 // expected-warning@-3{{FALSE}}
502 // expected-note@-4 {{FALSE}}
503 OSArray *arr2 = OSRequiredCast(OSArray, obj);
504 clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}}
505 // expected-note@-1{{TRUE}}
506 }
507
check_dynamic_cast_no_null_on_orig(OSObject * obj)508 unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
509 OSArray *arr = OSDynamicCast(OSArray, obj);
510 if (arr) {
511 return arr->getCount();
512 } else {
513
514 // The fact that dynamic cast has failed should not imply that
515 // the input object was null.
516 return obj->foo(); // no-warning
517 }
518 }
519
check_dynamic_cast_null_branch(OSObject * obj)520 void check_dynamic_cast_null_branch(OSObject *obj) {
521 OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}}
522 OSArray *arr = OSDynamicCast(OSArray, obj); // expected-note{{Assuming dynamic cast returns null due to type mismatch}}
523 if (!arr) // expected-note{{'arr' is null}}
524 // expected-note@-1{{Taking true branch}}
525 return; // expected-warning{{Potential leak of an object stored into 'arr1'}}
526 // expected-note@-1{{Object leaked}}
527 arr1->release();
528 }
529
check_dynamic_cast_null_check()530 void check_dynamic_cast_null_check() {
531 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}}
532 // expected-warning@-1{{Potential leak of an object}}
533 // expected-note@-2{{Object leaked}}
534 // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}}
535 if (!arr)
536 return;
537 arr->release();
538 }
539
use_after_release()540 void use_after_release() {
541 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
542 arr->release(); // expected-note{{Object released}}
543 arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
544 // expected-note@-1{{Reference-counted object is used after it is released}}
545 }
546
potential_leak()547 void potential_leak() {
548 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
549 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
550 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
551 arr->getCount();
552 } // expected-warning{{Potential leak of an object stored into 'arr'}}
553 // 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}}
554
proper_cleanup()555 void proper_cleanup() {
556 OSArray *arr = OSArray::withCapacity(10); // +1
557 arr->retain(); // +2
558 arr->release(); // +1
559 arr->getCount();
560 arr->release(); // 0
561 }
562
no_warning_on_getter(ArrayOwner * owner)563 unsigned int no_warning_on_getter(ArrayOwner *owner) {
564 OSArray *arr = owner->getArray();
565 return arr->getCount();
566 }
567
warn_on_overrelease(ArrayOwner * owner)568 unsigned int warn_on_overrelease(ArrayOwner *owner) {
569 // FIXME: summaries are not applied in case the source of the getter/setter
570 // is known.
571 // rdar://45681203
572 OSArray *arr = owner->getArray();
573 arr->release();
574 return arr->getCount();
575 }
576
nowarn_on_release_of_created(ArrayOwner * owner)577 unsigned int nowarn_on_release_of_created(ArrayOwner *owner) {
578 OSArray *arr = owner->createArray();
579 unsigned int out = arr->getCount();
580 arr->release();
581 return out;
582 }
583
nowarn_on_release_of_created_source_unknown(ArrayOwner * owner)584 unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) {
585 OSArray *arr = owner->createArraySourceUnknown();
586 unsigned int out = arr->getCount();
587 arr->release();
588 return out;
589 }
590
no_warn_ok_release(ArrayOwner * owner)591 unsigned int no_warn_ok_release(ArrayOwner *owner) {
592 OSArray *arr = owner->getArray(); // +0
593 arr->retain(); // +1
594 arr->release(); // +0
595 return arr->getCount(); // no-warning
596 }
597
warn_on_overrelease_with_unknown_source(ArrayOwner * owner)598 unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
599 OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type 'OSArray' with a +0 retain count}}
600 arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
601 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
602 return arr->getCount();
603 }
604
ok_release_with_unknown_source(ArrayOwner * owner)605 unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
606 OSArray *arr = owner->getArraySourceUnknown(); // +0
607 arr->retain(); // +1
608 arr->release(); // +0
609 return arr->getCount();
610 }
611
612 OSObject *getObject();
613 typedef bool (^Blk)(OSObject *);
614
test_escape_to_unknown_block(Blk blk)615 void test_escape_to_unknown_block(Blk blk) {
616 blk(getObject()); // no-crash
617 }
618
619 using OSObjectPtr = os::smart_ptr<OSObject>;
620
test_smart_ptr_uaf()621 void test_smart_ptr_uaf() {
622 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
623 {
624 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
625 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
626 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
627 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
628 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
629 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
630 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
631 } // expected-note{{Calling '~smart_ptr'}}
632 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
633 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
634 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
635 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
636 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
637 // expected-note@-6{{Returning from '~smart_ptr'}}
638 obj->release(); // expected-note{{Object released}}
639 obj->release(); // expected-warning{{Reference-counted object is used after it is released}}
640 // expected-note@-1{{Reference-counted object is used after it is released}}
641 }
642
test_smart_ptr_leak()643 void test_smart_ptr_leak() {
644 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
645 {
646 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
647 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
648 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
649 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
650 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
651 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
652 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
653 } // expected-note{{Calling '~smart_ptr'}}
654 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
655 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
656 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
657 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
658 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
659 // expected-note@-6{{Returning from '~smart_ptr'}}
660 } // expected-warning{{Potential leak of an object stored into 'obj'}}
661 // 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}}
662
test_smart_ptr_no_leak()663 void test_smart_ptr_no_leak() {
664 OSObject *obj = new OSObject;
665 {
666 OSObjectPtr p(obj);
667 }
668 obj->release();
669 }
670
getRuleViolation()671 OSObject *getRuleViolation() {
672 return new OSObject; // expected-warning{{Potential leak of an object of type 'OSObject'}}
673 // expected-note@-1{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
674 // expected-note@-2{{Object leaked: allocated object of type 'OSObject' is returned from a function whose name ('getRuleViolation') starts with 'get'}}
675 }
676
createRuleViolation(OSObject * param)677 OSObject *createRuleViolation(OSObject *param) { // expected-note{{Parameter 'param' starts at +0}}
678 return param; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
679 // expected-note@-1{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
680 }
681
test_ostypealloc_correct_diagnostic_name()682 void test_ostypealloc_correct_diagnostic_name() {
683 OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}}
684 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
685 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
686 } // 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}}
687 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
688
689 void escape_elsewhere(OSObject *obj);
690
test_free_on_escaped_object_diagnostics()691 void test_free_on_escaped_object_diagnostics() {
692 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
693 escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}}
694 obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}}
695 // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}}
696 }
697
test_tagged_retain_no_leak()698 void test_tagged_retain_no_leak() {
699 OSObject *obj = new OSObject;
700 obj->taggedRelease();
701 }
702
test_tagged_retain_no_uaf()703 void test_tagged_retain_no_uaf() {
704 OSObject *obj = new OSObject;
705 obj->taggedRetain();
706 obj->release();
707 obj->release();
708 }
709
710 class IOService {
711 public:
712 OSObject *somethingMatching(OSObject *table = 0);
713 };
714
testSuppressionForMethodsEndingWithMatching(IOService * svc,OSObject * table=0)715 OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc,
716 OSObject *table = 0) {
717 // This probably just passes table through. We should probably not make
718 // ptr1 definitely equal to table, but we should not warn about leaks.
719 OSObject *ptr1 = svc->somethingMatching(table); // no-warning
720
721 // FIXME: This, however, should follow the Create Rule regardless.
722 // We should warn about the leak here.
723 OSObject *ptr2 = svc->somethingMatching(); // no-warning
724
725 if (!table)
726 table = OSTypeAlloc(OSArray);
727
728 // This function itself ends with "Matching"! Do not warn when we're
729 // returning from it at +0.
730 return table; // no-warning
731 }
732
733 namespace weird_result {
734 struct WeirdResult {
735 int x, y, z;
736 };
737
738 WeirdResult outParamWithWeirdResult(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj);
739
testOutParamWithWeirdResult()740 WeirdResult testOutParamWithWeirdResult() {
741 OSObject *obj;
742 return outParamWithWeirdResult(&obj); // no-warning
743 }
744 } // namespace weird_result
745
746 namespace inherited_constructor_crash {
747 struct a {
748 a(int);
749 };
750 struct b : a {
751 // This is an "inherited constructor".
752 using a::a;
753 };
test()754 void test() {
755 // RetainCountChecker used to crash when looking for a summary
756 // for the inherited constructor invocation.
757 b(0);
758 }
759 } // namespace inherited_constructor_crash
760
761 namespace ossymbol_suppression {
762 OSSymbol *createSymbol();
test()763 void test() {
764 OSSymbol *sym = createSymbol(); // no-warning
765 }
766 } // namespace ossymbol_suppression
767