blob: 35d91add1a982df8714a66d9e4d2f582866623cb [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}}
George Karpenkov3c2ed8f2018-10-25 23:38:07 +0000501 if (!arr) // expected-note{{Taking true branch}}
George Karpenkova1c3bb82018-11-30 02:17:31 +0000502 return; // expected-warning{{Potential leak of an object stored into 'arr1'}}
George Karpenkov3c2ed8f2018-10-25 23:38:07 +0000503 // expected-note@-1{{Object leaked}}
504 arr1->release();
505}
506
George Karpenkov41dc8de2018-10-11 22:59:16 +0000507void check_dynamic_cast_null_check() {
George Karpenkove2f09542018-11-30 02:17:57 +0000508 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}}
George Karpenkov3c2ed8f2018-10-25 23:38:07 +0000509 // expected-warning@-1{{Potential leak of an object}}
510 // expected-note@-2{{Object leaked}}
George Karpenkova9e29562019-01-22 19:51:00 +0000511 // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}}
George Karpenkov41dc8de2018-10-11 22:59:16 +0000512 if (!arr)
513 return;
514 arr->release();
515}
516
George Karpenkovab0011e2018-08-23 00:26:59 +0000517void use_after_release() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000518 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 +0000519 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() {
George Karpenkov4f64b382019-01-10 18:15:57 +0000525 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 +0000526 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
George Karpenkovab0011e2018-08-23 00:26:59 +0000540unsigned 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) {
George Karpenkov6fd5c86d2018-10-31 17:38:29 +0000546 // 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();
George Karpenkovab0011e2018-08-23 00:26:59 +0000551 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) {
George Karpenkov4f64b382019-01-10 18:15:57 +0000576 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 +0000577 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}
Artem Dergachev46f34622018-12-15 02:13:26 +0000588
589OSObject *getObject();
590typedef bool (^Blk)(OSObject *);
591
592void test_escape_to_unknown_block(Blk blk) {
593 blk(getObject()); // no-crash
594}
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000595
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'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000605 // 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 +0000606 // 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'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000610 // 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 +0000611 // 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'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000625 // 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 +0000626 // 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'}}
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000630 // 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 +0000631 // 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
George Karpenkovd37ff4e2019-01-29 19:29:59 +0000644OSObject *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
George Karpenkov2c2d0b62019-01-18 19:24:55 +0000655void 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
George Karpenkov0f3bbba2019-01-29 19:29:07 +0000671void 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}
Artem Dergachev48e7a2f2019-04-26 02:05:18 +0000682
683class IOService {
684public:
685 OSObject *somethingMatching(OSObject *table = 0);
686};
687
688OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc,
689 OSObject *table = 0) {
690 // This probably just passes table through. We should probably not make
691 // ptr1 definitely equal to table, but we should not warn about leaks.
692 OSObject *ptr1 = svc->somethingMatching(table); // no-warning
693
694 // FIXME: This, however, should follow the Create Rule regardless.
695 // We should warn about the leak here.
696 OSObject *ptr2 = svc->somethingMatching(); // no-warning
697
698 if (!table)
699 table = OSTypeAlloc(OSArray);
700
701 // This function itself ends with "Matching"! Do not warn when we're
702 // returning from it at +0.
703 return table; // no-warning
704}