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 | |
7 | struct OSIterator : public OSObject { |
8 | static const OSMetaClass * const metaClass; |
9 | }; |
10 | |
11 | struct 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 | |
46 | struct MyArray : public OSArray { |
47 | void consumeReference(OSArray *other) override; |
48 | |
49 | OSObject *identity() override; |
50 | |
51 | OSObject *generateObject(OSObject *input) override; |
52 | }; |
53 | |
54 | struct OtherStruct { |
55 | static void doNothingToArray(OSArray *array); |
56 | OtherStruct(OSArray *arr); |
57 | }; |
58 | |
59 | bool test_meta_cast_no_leak(OSMetaClassBase *arg) { |
60 | return arg && arg->metaCast("blah") != nullptr; |
61 | } |
62 | |
63 | static 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 | |
70 | void escape(void *); |
71 | void escape_with_source(void *p) {} |
72 | bool coin(); |
73 | |
74 | typedef int kern_return_t; |
75 | typedef kern_return_t IOReturn; |
76 | typedef kern_return_t OSReturn; |
77 | #define kOSReturnSuccess 0 |
78 | #define kIOReturnSuccess 0 |
79 | |
80 | bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj); |
81 | |
82 | void use_out_param() { |
83 | OSObject *obj; |
84 | if (write_into_out_param_on_success(&obj)) { |
85 | obj->release(); |
86 | } |
87 | } |
88 | |
89 | void 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 | |
95 | bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj); |
96 | |
97 | void 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 | |
103 | void use_out_param_on_failure() { |
104 | OSObject *obj; |
105 | if (!write_into_out_param_on_failure(&obj)) { |
106 | obj->release(); |
107 | } |
108 | } |
109 | |
110 | IOReturn write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject **obj); |
111 | |
112 | void use_out_param_on_nonzero() { |
113 | OSObject *obj; |
114 | if (write_into_out_param_on_nonzero(&obj) != kIOReturnSuccess) { |
115 | obj->release(); |
116 | } |
117 | } |
118 | |
119 | bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a, |
120 | OS_RETURNS_RETAINED OSObject **b); |
121 | |
122 | void 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 | |
131 | void 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 | |
141 | void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a, |
142 | OS_RETURNS_RETAINED OSObject **b); |
143 | |
144 | void 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 | |
152 | void 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 | |
162 | char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject **obj); |
163 | |
164 | void use_out_param_osreturn_on_nonnull() { |
165 | OSObject *obj; |
166 | if (write_into_out_param_on_nonnull(&obj)) { |
167 | obj->release(); |
168 | } |
169 | } |
170 | |
171 | void 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 | |
177 | bool write_optional_out_param(OS_RETURNS_RETAINED OSObject **obj=nullptr); |
178 | |
179 | void use_optional_out_param() { |
180 | if (write_optional_out_param()) {}; |
181 | } |
182 | |
183 | OSReturn write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject **obj); |
184 | |
185 | void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject **obj); |
186 | |
187 | void use_write_into_non_retained_out_param() { |
188 | OSObject *obj; |
189 | write_into_non_retained_out_param(&obj); |
190 | } |
191 | |
192 | void 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 | |
199 | void always_write_into_out_param(OS_RETURNS_RETAINED OSObject **obj); |
200 | |
201 | void pass_through_out_param(OSObject **obj) { |
202 | always_write_into_out_param(obj); |
203 | } |
204 | |
205 | void 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 | |
209 | void 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 | |
216 | void use_void_out_param_osreturn() { |
217 | OSObject *obj; |
218 | always_write_into_out_param(&obj); |
219 | obj->release(); |
220 | } |
221 | |
222 | void 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 | |
228 | void use_out_param_osreturn() { |
229 | OSObject *obj; |
230 | if (write_into_out_param_on_os_success(&obj) == kOSReturnSuccess) { |
231 | obj->release(); |
232 | } |
233 | } |
234 | |
235 | void 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 | |
241 | void cleanup(OSObject **obj); |
242 | |
243 | void test_cleanup_escaping() { |
244 | __attribute__((cleanup(cleanup))) OSObject *obj; |
245 | always_write_into_out_param(&obj); // no-warning, the value has escaped. |
246 | } |
247 | |
248 | struct 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 | |
257 | bool 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 | |
266 | bool 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 | |
275 | void os_consume_ok(OS_CONSUME OSObject *obj) { |
276 | escape(obj); |
277 | } |
278 | |
279 | void 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 | |
286 | void 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 | |
293 | void use_os_consume_ok() { |
294 | OSObject *obj = new OSObject; |
295 | os_consume_ok(obj); |
296 | } |
297 | |
298 | void test_escaping_into_voidstar() { |
299 | OSObject *obj = new OSObject; |
300 | escape(obj); |
301 | } |
302 | |
303 | void test_escape_has_source() { |
304 | OSObject *obj = new OSObject; |
305 | if (obj) |
306 | escape_with_source(obj); |
307 | return; |
308 | } |
309 | |
310 | void 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 | |
318 | void check_param_attribute_propagation(MyArray *parent) { |
319 | OSArray *arr = new OSArray; |
320 | parent->consumeReference(arr); |
321 | } |
322 | |
323 | unsigned 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 | |
331 | unsigned 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 | |
339 | void check_consumes_this(OSArray *owner) { |
340 | OSArray *arr = new OSArray; |
341 | arr->putIntoArray(owner); |
342 | } |
343 | |
344 | void check_consumes_this_with_template(OSArray *owner) { |
345 | OSArray *arr = new OSArray; |
346 | arr->putIntoT(owner); |
347 | } |
348 | |
349 | void check_free_no_error() { |
350 | OSArray *arr = OSArray::withCapacity(10); |
351 | arr->retain(); |
352 | arr->retain(); |
353 | arr->retain(); |
354 | arr->free(); |
355 | } |
356 | |
357 | void 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 | |
365 | unsigned 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 | |
371 | unsigned 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 | |
377 | void check_get_object() { |
378 | OSObject::getObject(); |
379 | } |
380 | |
381 | void check_Get_object() { |
382 | OSObject::GetObject(); |
383 | } |
384 | |
385 | void check_custom_iterator_rule(OSArray *arr) { |
386 | OSIterator *it = arr->getIterator(); |
387 | it->release(); |
388 | } |
389 | |
390 | void 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 | |
395 | void 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 | |
401 | void 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 | |
407 | struct 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 | |
428 | OSArray *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 | |
433 | unsigned 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 | |
444 | unsigned 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 | |
451 | void 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 | |
461 | void check_rc_consumed() { |
462 | OSArray *arr = OSArray::withCapacity(10); |
463 | OSArray::consumeArray(arr); |
464 | } |
465 | |
466 | void check_rc_consume_temporary() { |
467 | OSArray::consumeArray(OSArray::withCapacity(10)); |
468 | } |
469 | |
470 | void check_rc_getter() { |
471 | OSArray *arr = OSArray::MaskedGetter(); |
472 | (void)arr; |
473 | } |
474 | |
475 | void check_rc_create() { |
476 | OSArray *arr = OSArray::getOoopsActuallyCreate(); |
477 | arr->release(); |
478 | } |
479 | |
480 | |
481 | void check_dynamic_cast() { |
482 | OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); |
483 | arr->release(); |
484 | } |
485 | |
486 | unsigned 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 | |
498 | void 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 | |
507 | void 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 | |
517 | void 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 | |
524 | void 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 | |
532 | void 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 | |
540 | unsigned int no_warning_on_getter(ArrayOwner *owner) { |
541 | OSArray *arr = owner->getArray(); |
542 | return arr->getCount(); |
543 | } |
544 | |
545 | unsigned 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 | |
554 | unsigned 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 | |
561 | unsigned 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 | |
568 | unsigned 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 | |
575 | unsigned 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 | |
582 | unsigned 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 | |
589 | OSObject *getObject(); |
590 | typedef bool (^Blk)(OSObject *); |
591 | |
592 | void test_escape_to_unknown_block(Blk blk) { |
593 | blk(getObject()); // no-crash |
594 | } |
595 | |
596 | using OSObjectPtr = os::smart_ptr<OSObject>; |
597 | |
598 | void 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 | |
618 | void 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 | |
636 | void test_smart_ptr_no_leak() { |
637 | OSObject *obj = new OSObject; |
638 | { |
639 | OSObjectPtr p(obj); |
640 | } |
641 | obj->release(); |
642 | } |
643 | |
644 | OSObject *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 | |
650 | OSObject *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 | |
655 | void 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 | |
662 | void escape_elsewhere(OSObject *obj); |
663 | |
664 | void 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 | |
671 | void test_tagged_retain_no_leak() { |
672 | OSObject *obj = new OSObject; |
673 | obj->taggedRelease(); |
674 | } |
675 | |
676 | void test_tagged_retain_no_uaf() { |
677 | OSObject *obj = new OSObject; |
678 | obj->taggedRetain(); |
679 | obj->release(); |
680 | obj->release(); |
681 | } |
682 | |