xref: /llvm-project/clang/test/Analysis/osobject-retain-release.cpp (revision e4d242768aefabc0091dd01fabecaffbc2b6984b)
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