Clang Project

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