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