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