1 // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
2 // RUN:                    -analyzer-checker=core,osx -verify %s
3 
4 struct OSMetaClass;
5 
6 #define OS_CONSUME __attribute__((os_consumed))
7 #define OS_RETURNS_RETAINED __attribute__((os_returns_retained))
8 #define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero))
9 #define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero))
10 #define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained))
11 #define OS_CONSUMES_THIS __attribute__((os_consumes_this))
12 
13 #define OSTypeID(type)   (type::metaClass)
14 
15 #define OSDynamicCast(type, inst)   \
16     ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
17 
18 using size_t = decltype(sizeof(int));
19 
20 struct OSObject {
21   virtual void retain();
22   virtual void release() {};
23   virtual void free();
24   virtual ~OSObject(){}
25 
26   unsigned int foo() { return 42; }
27 
28   virtual OS_RETURNS_NOT_RETAINED OSObject *identity();
29 
30   static OSObject *generateObject(int);
31 
32   static OSObject *getObject();
33   static OSObject *GetObject();
34 
35   static void * operator new(size_t size);
36 
37   static const OSMetaClass * const metaClass;
38 };
39 
40 struct OSIterator : public OSObject {
41 
42   static const OSMetaClass * const metaClass;
43 };
44 
45 struct OSArray : public OSObject {
46   unsigned int getCount();
47 
48   OSIterator * getIterator();
49 
50   OSObject *identity() override;
51 
52   virtual OSObject *generateObject(OSObject *input);
53 
54   virtual void consumeReference(OS_CONSUME OSArray *other);
55 
56   void putIntoArray(OSArray *array) OS_CONSUMES_THIS;
57 
58   template <typename T>
59   void putIntoT(T *owner) OS_CONSUMES_THIS;
60 
61   static OSArray *generateArrayHasCode() {
62     return new OSArray;
63   }
64 
65   static OSArray *withCapacity(unsigned int capacity);
66   static void consumeArray(OS_CONSUME OSArray * array);
67 
68   static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) {
69     return nullptr;
70   }
71 
72   static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter();
73   static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate();
74 
75   static const OSMetaClass * const metaClass;
76 };
77 
78 struct MyArray : public OSArray {
79   void consumeReference(OSArray *other) override;
80 
81   OSObject *identity() override;
82 
83   OSObject *generateObject(OSObject *input) override;
84 };
85 
86 struct OtherStruct {
87   static void doNothingToArray(OSArray *array);
88   OtherStruct(OSArray *arr);
89 };
90 
91 struct OSMetaClassBase {
92   static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta);
93 };
94 
95 void escape(void *);
96 void escape_with_source(void *p) {}
97 bool coin();
98 
99 typedef int kern_return_t;
100 typedef kern_return_t IOReturn;
101 typedef kern_return_t OSReturn;
102 #define kOSReturnSuccess  0
103 #define kIOReturnSuccess 0
104 
105 bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj);
106 
107 void use_out_param() {
108   OSObject *obj;
109   if (write_into_out_param_on_success(&obj)) {
110     obj->release();
111   }
112 }
113 
114 void use_out_param_leak() {
115   OSObject *obj;
116   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){{$}}}}
117 } // expected-warning{{Potential leak of an object stored into 'obj'}}
118  // 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}}
119 
120 bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj);
121 
122 void use_out_param_leak2() {
123   OSObject *obj;
124   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){{$}}}}
125 } // expected-warning{{Potential leak of an object stored into 'obj'}}
126  // 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}}
127 
128 void use_out_param_on_failure() {
129   OSObject *obj;
130   if (!write_into_out_param_on_failure(&obj)) {
131     obj->release();
132   }
133 }
134 
135 IOReturn write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject **obj);
136 
137 void use_out_param_on_nonzero() {
138   OSObject *obj;
139   if (write_into_out_param_on_nonzero(&obj) != kIOReturnSuccess) {
140     obj->release();
141   }
142 }
143 
144 bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a,
145                                OS_RETURNS_RETAINED OSObject **b);
146 
147 void use_write_into_two_out_params() {
148   OSObject *obj1;
149   OSObject *obj2;
150   if (write_into_two_out_params(&obj1, &obj2)) {
151     obj1->release();
152     obj2->release();
153   }
154 }
155 
156 void use_write_two_out_params_leak() {
157   OSObject *obj1;
158   OSObject *obj2;
159   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){{$}}}}
160                                            // 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){{$}}}}
161 } // expected-warning{{Potential leak of an object stored into 'obj1'}}
162   // expected-warning@-1{{Potential leak of an object stored into 'obj2'}}
163   // 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}}
164   // 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}}
165 
166 void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a,
167                                       OS_RETURNS_RETAINED OSObject **b);
168 
169 void use_always_write_into_two_out_params() {
170   OSObject *obj1;
171   OSObject *obj2;
172   always_write_into_two_out_params(&obj1, &obj2);
173   obj1->release();
174   obj2->release();
175 }
176 
177 void use_always_write_into_two_out_params_leak() {
178   OSObject *obj1;
179   OSObject *obj2;
180   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'{{$}}}}
181                                                   // 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'{{$}}}}
182 } // expected-warning{{Potential leak of an object stored into 'obj1'}}
183   // expected-warning@-1{{Potential leak of an object stored into 'obj2'}}
184   // 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}}
185   // 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}}
186 
187 char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject **obj);
188 
189 void use_out_param_osreturn_on_nonnull() {
190   OSObject *obj;
191   if (write_into_out_param_on_nonnull(&obj)) {
192     obj->release();
193   }
194 }
195 
196 void use_out_param_leak_osreturn_on_nonnull() {
197   OSObject *obj;
198   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){{$}}}}
199 } // expected-warning{{Potential leak of an object stored into 'obj'}}
200   // 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}}
201 
202 bool write_optional_out_param(OS_RETURNS_RETAINED OSObject **obj=nullptr);
203 
204 void use_optional_out_param() {
205   if (write_optional_out_param()) {};
206 }
207 
208 OSReturn write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject **obj);
209 
210 void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject **obj);
211 
212 void use_write_into_non_retained_out_param() {
213   OSObject *obj;
214   write_into_non_retained_out_param(&obj);
215 }
216 
217 void use_write_into_non_retained_out_param_uaf() {
218   OSObject *obj;
219   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'{{$}}}}
220   obj->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
221                   // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
222 }
223 
224 void always_write_into_out_param(OS_RETURNS_RETAINED OSObject **obj);
225 
226 void pass_through_out_param(OSObject **obj) {
227   always_write_into_out_param(obj);
228 }
229 
230 void always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject **obj) {
231   *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
232 }
233 
234 void use_always_write_into_out_param_has_source_leak() {
235   OSObject *obj;
236   always_write_into_out_param_has_source(&obj); // expected-note{{Calling 'always_write_into_out_param_has_source'}}
237                                                 // expected-note@-1{{Returning from 'always_write_into_out_param_has_source'}}
238 } // expected-warning{{Potential leak of an object stored into 'obj'}}
239   // 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}}
240 
241 void use_void_out_param_osreturn() {
242   OSObject *obj;
243   always_write_into_out_param(&obj);
244   obj->release();
245 }
246 
247 void use_void_out_param_osreturn_leak() {
248   OSObject *obj;
249   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'{{$}}}}
250 } // expected-warning{{Potential leak of an object stored into 'obj'}}
251   // 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}}
252 
253 void use_out_param_osreturn() {
254   OSObject *obj;
255   if (write_into_out_param_on_os_success(&obj) == kOSReturnSuccess) {
256     obj->release();
257   }
258 }
259 
260 void use_out_param_leak_osreturn() {
261   OSObject *obj;
262   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){{$}}}}
263 } // expected-warning{{Potential leak of an object stored into 'obj'}}
264   // 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}}
265 
266 struct StructWithField {
267   OSObject *obj;
268 
269   void initViaOutParamCall() { // no warning on writing into fields
270     always_write_into_out_param(&obj);
271   }
272 
273 };
274 
275 bool os_consume_violation_two_args(OS_CONSUME OSObject *obj, bool extra) {
276   if (coin()) { // expected-note{{Assuming the condition is false}}
277                 // expected-note@-1{{Taking false branch}}
278     escape(obj);
279     return true;
280   }
281   return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
282 }
283 
284 bool os_consume_violation(OS_CONSUME OSObject *obj) {
285   if (coin()) { // expected-note{{Assuming the condition is false}}
286                 // expected-note@-1{{Taking false branch}}
287     escape(obj);
288     return true;
289   }
290   return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
291 }
292 
293 void os_consume_ok(OS_CONSUME OSObject *obj) {
294   escape(obj);
295 }
296 
297 void use_os_consume_violation() {
298   OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
299   os_consume_violation(obj); // expected-note{{Calling 'os_consume_violation'}}
300                              // expected-note@-1{{Returning from 'os_consume_violation'}}
301 } // 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}}
302   // expected-warning@-1{{Potential leak of an object stored into 'obj'}}
303 
304 void use_os_consume_violation_two_args() {
305   OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
306   os_consume_violation_two_args(obj, coin()); // expected-note{{Calling 'os_consume_violation_two_args'}}
307                              // expected-note@-1{{Returning from 'os_consume_violation_two_args'}}
308 } // 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}}
309   // expected-warning@-1{{Potential leak of an object stored into 'obj'}}
310 
311 void use_os_consume_ok() {
312   OSObject *obj = new OSObject;
313   os_consume_ok(obj);
314 }
315 
316 void test_escaping_into_voidstar() {
317   OSObject *obj = new OSObject;
318   escape(obj);
319 }
320 
321 void test_escape_has_source() {
322   OSObject *obj = new OSObject;
323   if (obj)
324     escape_with_source(obj);
325   return;
326 }
327 
328 void test_no_infinite_check_recursion(MyArray *arr) {
329   OSObject *input = new OSObject;
330   OSObject *o = arr->generateObject(input);
331   o->release();
332   input->release();
333 }
334 
335 
336 void check_param_attribute_propagation(MyArray *parent) {
337   OSArray *arr = new OSArray;
338   parent->consumeReference(arr);
339 }
340 
341 unsigned int check_attribute_propagation(OSArray *arr) {
342   OSObject *other = arr->identity();
343   OSArray *casted = OSDynamicCast(OSArray, other);
344   if (casted)
345     return casted->getCount();
346   return 0;
347 }
348 
349 unsigned int check_attribute_indirect_propagation(MyArray *arr) {
350   OSObject *other = arr->identity();
351   OSArray *casted = OSDynamicCast(OSArray, other);
352   if (casted)
353     return casted->getCount();
354   return 0;
355 }
356 
357 void check_consumes_this(OSArray *owner) {
358   OSArray *arr = new OSArray;
359   arr->putIntoArray(owner);
360 }
361 
362 void check_consumes_this_with_template(OSArray *owner) {
363   OSArray *arr = new OSArray;
364   arr->putIntoT(owner);
365 }
366 
367 void check_free_no_error() {
368   OSArray *arr = OSArray::withCapacity(10);
369   arr->retain();
370   arr->retain();
371   arr->retain();
372   arr->free();
373 }
374 
375 void check_free_use_after_free() {
376   OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
377   arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
378   arr->free(); // expected-note{{Object released}}
379   arr->retain(); // expected-warning{{Reference-counted object is used after it is released}}
380                  // expected-note@-1{{Reference-counted object is used after it is released}}
381 }
382 
383 unsigned int check_leak_explicit_new() {
384   OSArray *arr = new OSArray; // expected-note{{Operator 'new' returns an OSObject of type 'OSArray' with a +1 retain count}}
385   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}}
386                           // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
387 }
388 
389 unsigned int check_leak_factory() {
390   OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
391   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}}
392                           // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
393 }
394 
395 void check_get_object() {
396   OSObject::getObject();
397 }
398 
399 void check_Get_object() {
400   OSObject::GetObject();
401 }
402 
403 void check_custom_iterator_rule(OSArray *arr) {
404   OSIterator *it = arr->getIterator();
405   it->release();
406 }
407 
408 void check_iterator_leak(OSArray *arr) {
409   arr->getIterator(); // expected-note{{Call to method 'OSArray::getIterator' returns an OSObject of type 'OSIterator' with a +1 retain count}}
410 } // expected-note{{Object leaked: allocated object of type 'OSIterator' is not referenced later}}
411   // expected-warning@-1{{Potential leak of an object of type 'OSIterator}}'
412 
413 void check_no_invalidation() {
414   OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
415   OtherStruct::doNothingToArray(arr);
416 } // expected-warning{{Potential leak of an object stored into 'arr'}}
417   // expected-note@-1{{Object leaked}}
418 
419 void check_no_invalidation_other_struct() {
420   OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
421   OtherStruct other(arr); // expected-warning{{Potential leak}}
422                           // expected-note@-1{{Object leaked}}
423 }
424 
425 struct ArrayOwner : public OSObject {
426   OSArray *arr;
427   ArrayOwner(OSArray *arr) : arr(arr) {}
428 
429   static ArrayOwner* create(OSArray *arr) {
430     return new ArrayOwner(arr);
431   }
432 
433   OSArray *getArray() {
434     return arr;
435   }
436 
437   OSArray *createArray() {
438     return OSArray::withCapacity(10);
439   }
440 
441   OSArray *createArraySourceUnknown();
442 
443   OSArray *getArraySourceUnknown();
444 };
445 
446 OSArray *generateArray() {
447   return OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
448                                     // expected-note@-1{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
449 }
450 
451 unsigned int check_leak_good_error_message() {
452   unsigned int out;
453   {
454     OSArray *leaked = generateArray(); // expected-note{{Calling 'generateArray'}}
455                                        // expected-note@-1{{Returning from 'generateArray'}}
456     out = leaked->getCount(); // expected-warning{{Potential leak of an object stored into 'leaked'}}
457                               // 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}}
458   }
459   return out;
460 }
461 
462 unsigned int check_leak_msg_temporary() {
463   return generateArray()->getCount(); // expected-warning{{Potential leak of an object}}
464                                       // expected-note@-1{{Calling 'generateArray'}}
465                                       // expected-note@-2{{Returning from 'generateArray'}}
466                                       // 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}}
467 }
468 
469 void check_confusing_getters() {
470   OSArray *arr = OSArray::withCapacity(10);
471 
472   ArrayOwner *AO = ArrayOwner::create(arr);
473   AO->getArray();
474 
475   AO->release();
476   arr->release();
477 }
478 
479 void check_rc_consumed() {
480   OSArray *arr = OSArray::withCapacity(10);
481   OSArray::consumeArray(arr);
482 }
483 
484 void check_rc_consume_temporary() {
485   OSArray::consumeArray(OSArray::withCapacity(10));
486 }
487 
488 void check_rc_getter() {
489   OSArray *arr = OSArray::MaskedGetter();
490   (void)arr;
491 }
492 
493 void check_rc_create() {
494   OSArray *arr = OSArray::getOoopsActuallyCreate();
495   arr->release();
496 }
497 
498 
499 void check_dynamic_cast() {
500   OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1));
501   arr->release();
502 }
503 
504 unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
505   OSArray *arr = OSDynamicCast(OSArray, obj);
506   if (arr) {
507     return arr->getCount();
508   } else {
509 
510     // The fact that dynamic cast has failed should not imply that
511     // the input object was null.
512     return obj->foo(); // no-warning
513   }
514 }
515 
516 void check_dynamic_cast_null_branch(OSObject *obj) {
517   OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}}
518   OSArray *arr = OSDynamicCast(OSArray, obj);
519   if (!arr) // expected-note{{Taking true branch}}
520     return; // expected-warning{{Potential leak of an object stored into 'arr1'}}
521             // expected-note@-1{{Object leaked}}
522   arr1->release();
523 }
524 
525 void check_dynamic_cast_null_check() {
526   OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}}
527     // expected-warning@-1{{Potential leak of an object}}
528     // expected-note@-2{{Object leaked}}
529   if (!arr)
530     return;
531   arr->release();
532 }
533 
534 void use_after_release() {
535   OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
536   arr->release(); // expected-note{{Object released}}
537   arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
538                    // expected-note@-1{{Reference-counted object is used after it is released}}
539 }
540 
541 void potential_leak() {
542   OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
543   arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
544   arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
545   arr->getCount();
546 } // expected-warning{{Potential leak of an object stored into 'arr'}}
547   // 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}}
548 
549 void proper_cleanup() {
550   OSArray *arr = OSArray::withCapacity(10); // +1
551   arr->retain(); // +2
552   arr->release(); // +1
553   arr->getCount();
554   arr->release(); // 0
555 }
556 
557 unsigned int no_warning_on_getter(ArrayOwner *owner) {
558   OSArray *arr = owner->getArray();
559   return arr->getCount();
560 }
561 
562 unsigned int warn_on_overrelease(ArrayOwner *owner) {
563   // FIXME: summaries are not applied in case the source of the getter/setter
564   // is known.
565   // rdar://45681203
566   OSArray *arr = owner->getArray();
567   arr->release();
568   return arr->getCount();
569 }
570 
571 unsigned int nowarn_on_release_of_created(ArrayOwner *owner) {
572   OSArray *arr = owner->createArray();
573   unsigned int out = arr->getCount();
574   arr->release();
575   return out;
576 }
577 
578 unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) {
579   OSArray *arr = owner->createArraySourceUnknown();
580   unsigned int out = arr->getCount();
581   arr->release();
582   return out;
583 }
584 
585 unsigned int no_warn_ok_release(ArrayOwner *owner) {
586   OSArray *arr = owner->getArray(); // +0
587   arr->retain(); // +1
588   arr->release(); // +0
589   return arr->getCount(); // no-warning
590 }
591 
592 unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
593   OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type 'OSArray' with a +0 retain count}}
594   arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
595                   // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
596   return arr->getCount();
597 }
598 
599 unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
600   OSArray *arr = owner->getArraySourceUnknown(); // +0
601   arr->retain(); // +1
602   arr->release(); // +0
603   return arr->getCount();
604 }
605 
606 OSObject *getObject();
607 typedef bool (^Blk)(OSObject *);
608 
609 void test_escape_to_unknown_block(Blk blk) {
610   blk(getObject()); // no-crash
611 }
612