blob: 10ef144bf36e9a7e7307f8de26e557ca4b5884b0 [file] [log] [blame]
Artem Dergachev46f34622018-12-15 02:13:26 +00001// RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
2// RUN: -analyzer-checker=core,osx -verify %s
George Karpenkovab0011e2018-08-23 00:26:59 +00003
George Karpenkov2c2d0b62019-01-18 19:24:55 +00004#include "os_object_base.h"
5#include "os_smart_ptr.h"
George Karpenkovab0011e2018-08-23 00:26:59 +00006
George Karpenkov83fb5362018-10-31 17:38:46 +00007struct OSIterator : public OSObject {
George Karpenkova1c3bb82018-11-30 02:17:31 +00008 static const OSMetaClass * const metaClass;
George Karpenkov83fb5362018-10-31 17:38:46 +00009};
10
George Karpenkovab0011e2018-08-23 00:26:59 +000011struct OSArray : public OSObject {
12 unsigned int getCount();
13
George Karpenkova717bc72018-12-05 18:34:54 +000014 OSIterator * getIterator();
15
16 OSObject *identity() override;
17
George Karpenkova71ec6c2018-12-06 22:06:44 +000018 virtual OSObject *generateObject(OSObject *input);
19
George Karpenkova717bc72018-12-05 18:34:54 +000020 virtual void consumeReference(OS_CONSUME OSArray *other);
21
George Karpenkovb0b61952018-12-06 22:07:12 +000022 void putIntoArray(OSArray *array) OS_CONSUMES_THIS;
23
24 template <typename T>
25 void putIntoT(T *owner) OS_CONSUMES_THIS;
26
George Karpenkov62db8862018-11-30 02:18:23 +000027 static OSArray *generateArrayHasCode() {
28 return new OSArray;
29 }
30
George Karpenkovab0011e2018-08-23 00:26:59 +000031 static OSArray *withCapacity(unsigned int capacity);
George Karpenkov081c4772018-10-23 23:11:50 +000032 static void consumeArray(OS_CONSUME OSArray * array);
33
George Karpenkovd37ff4e2019-01-29 19:29:59 +000034 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}}
George Karpenkov081c4772018-10-23 23:11:50 +000037 }
38
George Karpenkovd37ff4e2019-01-29 19:29:59 +000039
George Karpenkov081c4772018-10-23 23:11:50 +000040 static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter();
41 static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate();
42
George Karpenkov41dc8de2018-10-11 22:59:16 +000043 static const OSMetaClass * const metaClass;
George Karpenkovab0011e2018-08-23 00:26:59 +000044};
45
George Karpenkova717bc72018-12-05 18:34:54 +000046struct MyArray : public OSArray {
47 void consumeReference(OSArray *other) override;
48
49 OSObject *identity() override;
George Karpenkova71ec6c2018-12-06 22:06:44 +000050
51 OSObject *generateObject(OSObject *input) override;
George Karpenkova717bc72018-12-05 18:34:54 +000052};
53
George Karpenkov081c4772018-10-23 23:11:50 +000054struct OtherStruct {
55 static void doNothingToArray(OSArray *array);
George Karpenkov3cfa04e2018-10-25 23:38:41 +000056 OtherStruct(OSArray *arr);
George Karpenkov081c4772018-10-23 23:11:50 +000057};
58
George Karpenkovdb0c66e2019-01-22 19:50:47 +000059bool test_meta_cast_no_leak(OSMetaClassBase *arg) {
60 return arg && arg->metaCast("blah") != nullptr;
61}
George Karpenkov41dc8de2018-10-11 22:59:16 +000062
George Karpenkovd37ff4e2019-01-29 19:29:59 +000063static 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
George Karpenkov041c9fa2018-12-08 01:18:40 +000070void escape(void *);
George Karpenkov255b0582018-12-21 19:13:40 +000071void escape_with_source(void *p) {}
George Karpenkovff014862018-12-11 01:13:40 +000072bool coin();
73
George Karpenkov5be959c2019-01-11 23:35:17 +000074typedef 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
George Karpenkov03391512019-01-16 23:21:38 +0000241void 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
George Karpenkov5be959c2019-01-11 23:35:17 +0000248struct 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
George Karpenkovf5085322018-12-21 02:16:23 +0000257bool 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 }
George Karpenkov79f03402018-12-21 19:13:28 +0000263 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
George Karpenkovf5085322018-12-21 02:16:23 +0000264}
265
George Karpenkovff014862018-12-11 01:13:40 +0000266bool 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 }
George Karpenkov79f03402018-12-21 19:13:28 +0000272 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}}
George Karpenkovff014862018-12-11 01:13:40 +0000273}
274
275void os_consume_ok(OS_CONSUME OSObject *obj) {
276 escape(obj);
277}
278
279void use_os_consume_violation() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000280 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
George Karpenkovff014862018-12-11 01:13:40 +0000281 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
George Karpenkovf5085322018-12-21 02:16:23 +0000286void use_os_consume_violation_two_args() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000287 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
George Karpenkovf5085322018-12-21 02:16:23 +0000288 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
George Karpenkovff014862018-12-11 01:13:40 +0000293void use_os_consume_ok() {
294 OSObject *obj = new OSObject;
295 os_consume_ok(obj);
296}
George Karpenkov041c9fa2018-12-08 01:18:40 +0000297
298void test_escaping_into_voidstar() {
299 OSObject *obj = new OSObject;
300 escape(obj);
301}
302
George Karpenkov255b0582018-12-21 19:13:40 +0000303void test_escape_has_source() {
304 OSObject *obj = new OSObject;
305 if (obj)
George Karpenkovd76cc592018-12-21 19:40:44 +0000306 escape_with_source(obj);
George Karpenkov255b0582018-12-21 19:13:40 +0000307 return;
308}
309
George Karpenkova71ec6c2018-12-06 22:06:44 +0000310void 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
George Karpenkova717bc72018-12-05 18:34:54 +0000318void 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
George Karpenkovb0b61952018-12-06 22:07:12 +0000339void 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
George Karpenkovbe3f4bd2018-11-30 20:43:42 +0000349void 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() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000358 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkovbe3f4bd2018-11-30 20:43:42 +0000359 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() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000366 OSArray *arr = new OSArray; // expected-note{{Operator 'new' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkov79ed11c2018-12-11 01:13:20 +0000367 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'}}
George Karpenkovbe3f4bd2018-11-30 20:43:42 +0000369}
370
371unsigned int check_leak_factory() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000372 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkovbe3f4bd2018-11-30 20:43:42 +0000373 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
George Karpenkov3bdbeb12018-11-30 02:18:10 +0000377void check_get_object() {
378 OSObject::getObject();
379}
380
381void check_Get_object() {
382 OSObject::GetObject();
383}
384
George Karpenkov83fb5362018-10-31 17:38:46 +0000385void check_custom_iterator_rule(OSArray *arr) {
386 OSIterator *it = arr->getIterator();
387 it->release();
388}
389
George Karpenkove2f09542018-11-30 02:17:57 +0000390void check_iterator_leak(OSArray *arr) {
George Karpenkov4f64b382019-01-10 18:15:57 +0000391 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}}'
George Karpenkove2f09542018-11-30 02:17:57 +0000394
George Karpenkov081c4772018-10-23 23:11:50 +0000395void check_no_invalidation() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000396 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkov081c4772018-10-23 23:11:50 +0000397 OtherStruct::doNothingToArray(arr);
398} // expected-warning{{Potential leak of an object stored into 'arr'}}
399 // expected-note@-1{{Object leaked}}
400
George Karpenkov3cfa04e2018-10-25 23:38:41 +0000401void check_no_invalidation_other_struct() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000402 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkov3cfa04e2018-10-25 23:38:41 +0000403 OtherStruct other(arr); // expected-warning{{Potential leak}}
404 // expected-note@-1{{Object leaked}}
405}
406
George Karpenkov6fd5c86d2018-10-31 17:38:29 +0000407struct 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
George Karpenkova1c3bb82018-11-30 02:17:31 +0000428OSArray *generateArray() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000429 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}}
George Karpenkova1c3bb82018-11-30 02:17:31 +0000431}
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() {
George Karpenkovf893ea12018-11-30 02:17:44 +0000445 return generateArray()->getCount(); // expected-warning{{Potential leak of an object}}
446 // expected-note@-1{{Calling 'generateArray'}}
447 // expected-note@-2{{Returning from 'generateArray'}}
George Karpenkov4f64b382019-01-10 18:15:57 +0000448 // 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}}
George Karpenkova1c3bb82018-11-30 02:17:31 +0000449}
450
George Karpenkov6fd5c86d2018-10-31 17:38:29 +0000451void 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
George Karpenkov081c4772018-10-23 23:11:50 +0000461void 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
George Karpenkov41dc8de2018-10-11 22:59:16 +0000481void check_dynamic_cast() {
482 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1));
483 arr->release();
484}
485
George Karpenkov3c2ed8f2018-10-25 23:38:07 +0000486unsigned 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) {
George Karpenkove2f09542018-11-30 02:17:57 +0000499 OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}}
George Karpenkova9e29562019-01-22 19:51:00 +0000500 OSArray *arr = OSDynamicCast(OSArray, obj); // expected-note{{Assuming dynamic cast returns null due to type mismatch}}
Csaba Dabis4b0184b2019-05-29 20:06:09 +0000501 if (!arr) // expected-note{{'arr' is null}}
502 // expected-note@-1{{Taking true branch}}
George Karpenkova1c3bb82018-11-30 02:17:31 +0000503 return; // expected-warning{{Potential leak of an object stored into 'arr1'}}
George Karpenkov3c2ed8f2018-10-25 23:38:07 +0000504 // expected-note@-1{{Object leaked}}
505 arr1->release();
506}
507
George Karpenkov41dc8de2018-10-11 22:59:16 +0000508void check_dynamic_cast_null_check() {
George Karpenkove2f09542018-11-30 02:17:57 +0000509 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}}
George Karpenkov3c2ed8f2018-10-25 23:38:07 +0000510 // expected-warning@-1{{Potential leak of an object}}
511 // expected-note@-2{{Object leaked}}
George Karpenkova9e29562019-01-22 19:51:00 +0000512 // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}}
George Karpenkov41dc8de2018-10-11 22:59:16 +0000513 if (!arr)
514 return;
515 arr->release();
516}
517
George Karpenkovab0011e2018-08-23 00:26:59 +0000518void use_after_release() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000519 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkovab0011e2018-08-23 00:26:59 +0000520 arr->release(); // expected-note{{Object released}}
521 arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
522 // expected-note@-1{{Reference-counted object is used after it is released}}
523}
524
525void potential_leak() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000526 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}}
George Karpenkovab0011e2018-08-23 00:26:59 +0000527 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
528 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
529 arr->getCount();
530} // expected-warning{{Potential leak of an object stored into 'arr'}}
531 // 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}}
532
533void proper_cleanup() {
534 OSArray *arr = OSArray::withCapacity(10); // +1
535 arr->retain(); // +2
536 arr->release(); // +1
537 arr->getCount();
538 arr->release(); // 0
539}
540
George Karpenkovab0011e2018-08-23 00:26:59 +0000541unsigned int no_warning_on_getter(ArrayOwner *owner) {
542 OSArray *arr = owner->getArray();
543 return arr->getCount();
544}
545
546unsigned int warn_on_overrelease(ArrayOwner *owner) {
George Karpenkov6fd5c86d2018-10-31 17:38:29 +0000547 // FIXME: summaries are not applied in case the source of the getter/setter
548 // is known.
549 // rdar://45681203
550 OSArray *arr = owner->getArray();
551 arr->release();
George Karpenkovab0011e2018-08-23 00:26:59 +0000552 return arr->getCount();
553}
554
555unsigned int nowarn_on_release_of_created(ArrayOwner *owner) {
556 OSArray *arr = owner->createArray();
557 unsigned int out = arr->getCount();
558 arr->release();
559 return out;
560}
561
562unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) {
563 OSArray *arr = owner->createArraySourceUnknown();
564 unsigned int out = arr->getCount();
565 arr->release();
566 return out;
567}
568
569unsigned int no_warn_ok_release(ArrayOwner *owner) {
570 OSArray *arr = owner->getArray(); // +0
571 arr->retain(); // +1
572 arr->release(); // +0
573 return arr->getCount(); // no-warning
574}
575
576unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
George Karpenkov4f64b382019-01-10 18:15:57 +0000577 OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type 'OSArray' with a +0 retain count}}
George Karpenkovab0011e2018-08-23 00:26:59 +0000578 arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
579 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
580 return arr->getCount();
581}
582
583unsigned int ok_release_with_unknown_source(ArrayOwner *owner) {
584 OSArray *arr = owner->getArraySourceUnknown(); // +0
585 arr->retain(); // +1
586 arr->release(); // +0
587 return arr->getCount();
588}
Artem Dergachev46f34622018-12-15 02:13:26 +0000589
590OSObject *getObject();
591typedef bool (^Blk)(OSObject *);
592
593void test_escape_to_unknown_block(Blk blk) {
594 blk(getObject()); // no-crash
595}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000596
597using OSObjectPtr = os::smart_ptr<OSObject>;
598
599void test_smart_ptr_uaf() {
600 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
601 {
602 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
603 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
Csaba Dabisd1f0ec32019-05-29 20:29:02 +0000604 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000605 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
606 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000607 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000608 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
609 } // expected-note{{Calling '~smart_ptr'}}
Csaba Dabisd1f0ec32019-05-29 20:29:02 +0000610 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000611 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
612 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000613 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000614 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
Csaba Dabisd1f0ec32019-05-29 20:29:02 +0000615 // expected-note@-6{{Returning from '~smart_ptr'}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000616 obj->release(); // expected-note{{Object released}}
617 obj->release(); // expected-warning{{Reference-counted object is used after it is released}}
618// expected-note@-1{{Reference-counted object is used after it is released}}
619}
620
621void test_smart_ptr_leak() {
622 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
623 {
624 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
625 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}}
Csaba Dabisd1f0ec32019-05-29 20:29:02 +0000626 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000627 // expected-note@os_smart_ptr.h:13{{Taking true branch}}
628 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000629 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000630 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
631 } // expected-note{{Calling '~smart_ptr'}}
Csaba Dabisd1f0ec32019-05-29 20:29:02 +0000632 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000633 // expected-note@os_smart_ptr.h:35{{Taking true branch}}
634 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000635 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000636 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
Csaba Dabisd1f0ec32019-05-29 20:29:02 +0000637 // expected-note@-6{{Returning from '~smart_ptr'}}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000638} // expected-warning{{Potential leak of an object stored into 'obj'}}
639// 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}}
640
641void test_smart_ptr_no_leak() {
642 OSObject *obj = new OSObject;
643 {
644 OSObjectPtr p(obj);
645 }
646 obj->release();
647}
648
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000649OSObject *getRuleViolation() {
650 return new OSObject; // expected-warning{{Potential leak of an object of type 'OSObject'}}
651// expected-note@-1{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
652// expected-note@-2{{Object leaked: allocated object of type 'OSObject' is returned from a function whose name ('getRuleViolation') starts with 'get'}}
653}
654
655OSObject *createRuleViolation(OSObject *param) { // expected-note{{Parameter 'param' starts at +0}}
656 return param; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
657 // expected-note@-1{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
658}
659
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000660void test_ostypealloc_correct_diagnostic_name() {
661 OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}}
662 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
663 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
664} // 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}}
665 // expected-warning@-1{{Potential leak of an object stored into 'arr'}}
666
667void escape_elsewhere(OSObject *obj);
668
669void test_free_on_escaped_object_diagnostics() {
670 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
671 escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}}
672 obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}}
673 // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}}
674}
675
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000676void test_tagged_retain_no_leak() {
677 OSObject *obj = new OSObject;
678 obj->taggedRelease();
679}
680
681void test_tagged_retain_no_uaf() {
682 OSObject *obj = new OSObject;
683 obj->taggedRetain();
684 obj->release();
685 obj->release();
686}
Artem Dergachev48e7a2f2019-04-26 02:05:18 +0000687
688class IOService {
689public:
690 OSObject *somethingMatching(OSObject *table = 0);
691};
692
693OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc,
694 OSObject *table = 0) {
695 // This probably just passes table through. We should probably not make
696 // ptr1 definitely equal to table, but we should not warn about leaks.
697 OSObject *ptr1 = svc->somethingMatching(table); // no-warning
698
699 // FIXME: This, however, should follow the Create Rule regardless.
700 // We should warn about the leak here.
701 OSObject *ptr2 = svc->somethingMatching(); // no-warning
702
703 if (!table)
704 table = OSTypeAlloc(OSArray);
705
706 // This function itself ends with "Matching"! Do not warn when we're
707 // returning from it at +0.
708 return table; // no-warning
709}
Artem Dergachev07c72572019-05-15 18:41:32 +0000710
711namespace weird_result {
712struct WeirdResult {
713 int x, y, z;
714};
715
716WeirdResult outParamWithWeirdResult(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj);
717
718WeirdResult testOutParamWithWeirdResult() {
719 OSObject *obj;
720 return outParamWithWeirdResult(&obj); // no-warning
721}
722} // namespace weird_result