1 // RUN: %clang_analyze_cc1 -std=c++14 -fblocks -analyze -analyzer-output=text\ 2 // RUN: -analyzer-checker=core,osx,debug.ExprInspection -verify %s 3 4 #include "os_object_base.h" 5 #include "os_smart_ptr.h" 6 7 void clang_analyzer_eval(bool); 8 9 struct OSIterator : public OSObject { 10 static const OSMetaClass * const metaClass; 11 }; 12 13 struct OSArray : public OSObject { 14 unsigned int getCount(); 15 16 OSIterator * getIterator(); 17 18 OSObject *identity() override; 19 20 virtual OSObject *generateObject(OSObject *input); 21 22 virtual void consumeReference(OS_CONSUME OSArray *other); 23 24 void putIntoArray(OSArray *array) OS_CONSUMES_THIS; 25 26 template <typename T> 27 void putIntoT(T *owner) OS_CONSUMES_THIS; 28 29 static OSArray *generateArrayHasCode() { 30 return new OSArray; 31 } 32 33 static OSArray *withCapacity(unsigned int capacity); 34 static void consumeArray(OS_CONSUME OSArray * array); 35 36 static OSArray* consumeArrayHasCode(OS_CONSUME OSArray * array) { // expected-note{{Parameter 'array' starts at +1, as it is marked as consuming}} 37 return nullptr; // expected-warning{{Potential leak of an object of type 'OSArray'}} 38 // 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}} 39 } 40 41 42 static OS_RETURNS_NOT_RETAINED OSArray *MaskedGetter(); 43 static OS_RETURNS_RETAINED OSArray *getOoopsActuallyCreate(); 44 45 static const OSMetaClass * const metaClass; 46 }; 47 48 struct MyArray : public OSArray { 49 void consumeReference(OSArray *other) override; 50 51 OSObject *identity() override; 52 53 OSObject *generateObject(OSObject *input) override; 54 }; 55 56 // These are never refcounted. 57 struct OSSymbol : OSObject {}; 58 59 struct OtherStruct { 60 static void doNothingToArray(OSArray *array); 61 OtherStruct(OSArray *arr); 62 }; 63 64 bool test_meta_cast_no_leak(OSMetaClassBase *arg) { 65 return arg && arg->metaCast("blah") != nullptr; 66 } 67 68 static void consumedMismatch(OS_CONSUME OSObject *a, 69 OSObject *b) { // expected-note{{Parameter 'b' starts at +0}} 70 a->release(); 71 b->retain(); // expected-note{{Reference count incremented. The object now has a +1 retain count}} 72 } // expected-warning{{Potential leak of an object of type 'OSObject'}} 73 // 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}} 74 75 void escape(void *); 76 void escape_with_source(void *p) {} 77 bool coin(); 78 79 typedef int kern_return_t; 80 typedef kern_return_t IOReturn; 81 typedef kern_return_t OSReturn; 82 #define kOSReturnSuccess 0 83 #define kIOReturnSuccess 0 84 85 bool write_into_out_param_on_success(OS_RETURNS_RETAINED OSObject **obj); 86 87 void use_out_param() { 88 OSObject *obj; 89 if (write_into_out_param_on_success(&obj)) { 90 obj->release(); 91 } 92 } 93 94 void use_out_param_leak() { 95 OSObject *obj; 96 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){{$}}}} 97 } // expected-warning{{Potential leak of an object stored into 'obj'}} 98 // 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}} 99 100 bool write_into_out_param_on_failure(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj); 101 102 void use_out_param_leak2() { 103 OSObject *obj; 104 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){{$}}}} 105 } // expected-warning{{Potential leak of an object stored into 'obj'}} 106 // 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}} 107 108 void use_out_param_on_failure() { 109 OSObject *obj; 110 if (!write_into_out_param_on_failure(&obj)) { 111 obj->release(); 112 } 113 } 114 115 IOReturn write_into_out_param_on_nonzero(OS_RETURNS_RETAINED_ON_NONZERO OSObject **obj); 116 117 void use_out_param_on_nonzero() { 118 OSObject *obj; 119 if (write_into_out_param_on_nonzero(&obj) != kIOReturnSuccess) { 120 obj->release(); 121 } 122 } 123 124 bool write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a, 125 OS_RETURNS_RETAINED OSObject **b); 126 127 void use_write_into_two_out_params() { 128 OSObject *obj1; 129 OSObject *obj2; 130 if (write_into_two_out_params(&obj1, &obj2)) { 131 obj1->release(); 132 obj2->release(); 133 } 134 } 135 136 void use_write_two_out_params_leak() { 137 OSObject *obj1; 138 OSObject *obj2; 139 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){{$}}}} 140 // 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){{$}}}} 141 } // expected-warning{{Potential leak of an object stored into 'obj1'}} 142 // expected-warning@-1{{Potential leak of an object stored into 'obj2'}} 143 // 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}} 144 // 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}} 145 146 void always_write_into_two_out_params(OS_RETURNS_RETAINED OSObject **a, 147 OS_RETURNS_RETAINED OSObject **b); 148 149 void use_always_write_into_two_out_params() { 150 OSObject *obj1; 151 OSObject *obj2; 152 always_write_into_two_out_params(&obj1, &obj2); 153 obj1->release(); 154 obj2->release(); 155 } 156 157 void use_always_write_into_two_out_params_leak() { 158 OSObject *obj1; 159 OSObject *obj2; 160 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'{{$}}}} 161 // 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'{{$}}}} 162 } // expected-warning{{Potential leak of an object stored into 'obj1'}} 163 // expected-warning@-1{{Potential leak of an object stored into 'obj2'}} 164 // 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}} 165 // 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}} 166 167 char *write_into_out_param_on_nonnull(OS_RETURNS_RETAINED OSObject **obj); 168 169 void use_out_param_osreturn_on_nonnull() { 170 OSObject *obj; 171 if (write_into_out_param_on_nonnull(&obj)) { 172 obj->release(); 173 } 174 } 175 176 void use_out_param_leak_osreturn_on_nonnull() { 177 OSObject *obj; 178 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){{$}}}} 179 } // expected-warning{{Potential leak of an object stored into 'obj'}} 180 // 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}} 181 182 bool write_optional_out_param(OS_RETURNS_RETAINED OSObject **obj=nullptr); 183 184 void use_optional_out_param() { 185 if (write_optional_out_param()) {}; 186 } 187 188 OSReturn write_into_out_param_on_os_success(OS_RETURNS_RETAINED OSObject **obj); 189 190 void write_into_non_retained_out_param(OS_RETURNS_NOT_RETAINED OSObject **obj); 191 192 void use_write_into_non_retained_out_param() { 193 OSObject *obj; 194 write_into_non_retained_out_param(&obj); 195 } 196 197 void use_write_into_non_retained_out_param_uaf() { 198 OSObject *obj; 199 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'{{$}}}} 200 obj->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} 201 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} 202 } 203 204 void always_write_into_out_param(OS_RETURNS_RETAINED OSObject **obj); 205 206 void pass_through_out_param(OSObject **obj) { 207 always_write_into_out_param(obj); 208 } 209 210 void always_write_into_out_param_has_source(OS_RETURNS_RETAINED OSObject **obj) { 211 *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 212 } 213 214 void use_always_write_into_out_param_has_source_leak() { 215 OSObject *obj; 216 always_write_into_out_param_has_source(&obj); // expected-note{{Calling 'always_write_into_out_param_has_source'}} 217 // expected-note@-1{{Returning from 'always_write_into_out_param_has_source'}} 218 } // expected-warning{{Potential leak of an object stored into 'obj'}} 219 // 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}} 220 221 void use_void_out_param_osreturn() { 222 OSObject *obj; 223 always_write_into_out_param(&obj); 224 obj->release(); 225 } 226 227 void use_void_out_param_osreturn_leak() { 228 OSObject *obj; 229 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'{{$}}}} 230 } // expected-warning{{Potential leak of an object stored into 'obj'}} 231 // 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}} 232 233 void use_out_param_osreturn() { 234 OSObject *obj; 235 if (write_into_out_param_on_os_success(&obj) == kOSReturnSuccess) { 236 obj->release(); 237 } 238 } 239 240 void use_out_param_leak_osreturn() { 241 OSObject *obj; 242 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){{$}}}} 243 } // expected-warning{{Potential leak of an object stored into 'obj'}} 244 // 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}} 245 246 void cleanup(OSObject **obj); 247 248 void test_cleanup_escaping() { 249 __attribute__((cleanup(cleanup))) OSObject *obj; 250 always_write_into_out_param(&obj); // no-warning, the value has escaped. 251 } 252 253 struct StructWithField { 254 OSObject *obj; 255 256 void initViaOutParamCall() { // no warning on writing into fields 257 always_write_into_out_param(&obj); 258 } 259 260 }; 261 262 bool os_consume_violation_two_args(OS_CONSUME OSObject *obj, bool extra) { 263 if (coin()) { // expected-note{{Assuming the condition is false}} 264 // expected-note@-1{{Taking false branch}} 265 escape(obj); 266 return true; 267 } 268 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}} 269 } 270 271 bool os_consume_violation(OS_CONSUME OSObject *obj) { 272 if (coin()) { // expected-note{{Assuming the condition is false}} 273 // expected-note@-1{{Taking false branch}} 274 escape(obj); 275 return true; 276 } 277 return false; // expected-note{{Parameter 'obj' is marked as consuming, but the function did not consume the reference}} 278 } 279 280 void os_consume_ok(OS_CONSUME OSObject *obj) { 281 escape(obj); 282 } 283 284 void use_os_consume_violation() { 285 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 286 os_consume_violation(obj); // expected-note{{Calling 'os_consume_violation'}} 287 // expected-note@-1{{Returning from 'os_consume_violation'}} 288 } // 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}} 289 // expected-warning@-1{{Potential leak of an object stored into 'obj'}} 290 291 void use_os_consume_violation_two_args() { 292 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 293 os_consume_violation_two_args(obj, coin()); // expected-note{{Calling 'os_consume_violation_two_args'}} 294 // expected-note@-1{{Returning from 'os_consume_violation_two_args'}} 295 } // 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}} 296 // expected-warning@-1{{Potential leak of an object stored into 'obj'}} 297 298 void use_os_consume_ok() { 299 OSObject *obj = new OSObject; 300 os_consume_ok(obj); 301 } 302 303 void test_escaping_into_voidstar() { 304 OSObject *obj = new OSObject; 305 escape(obj); 306 } 307 308 void test_escape_has_source() { 309 OSObject *obj = new OSObject; 310 if (obj) 311 escape_with_source(obj); 312 return; 313 } 314 315 void test_no_infinite_check_recursion(MyArray *arr) { 316 OSObject *input = new OSObject; 317 OSObject *o = arr->generateObject(input); 318 o->release(); 319 input->release(); 320 } 321 322 323 void check_param_attribute_propagation(MyArray *parent) { 324 OSArray *arr = new OSArray; 325 parent->consumeReference(arr); 326 } 327 328 unsigned int check_attribute_propagation(OSArray *arr) { 329 OSObject *other = arr->identity(); 330 OSArray *casted = OSDynamicCast(OSArray, other); 331 if (casted) 332 return casted->getCount(); 333 return 0; 334 } 335 336 unsigned int check_attribute_indirect_propagation(MyArray *arr) { 337 OSObject *other = arr->identity(); 338 OSArray *casted = OSDynamicCast(OSArray, other); 339 if (casted) 340 return casted->getCount(); 341 return 0; 342 } 343 344 void check_consumes_this(OSArray *owner) { 345 OSArray *arr = new OSArray; 346 arr->putIntoArray(owner); 347 } 348 349 void check_consumes_this_with_template(OSArray *owner) { 350 OSArray *arr = new OSArray; 351 arr->putIntoT(owner); 352 } 353 354 void check_free_no_error() { 355 OSArray *arr = OSArray::withCapacity(10); 356 arr->retain(); 357 arr->retain(); 358 arr->retain(); 359 arr->free(); 360 } 361 362 void check_free_use_after_free() { 363 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 364 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} 365 arr->free(); // expected-note{{Object released}} 366 arr->retain(); // expected-warning{{Reference-counted object is used after it is released}} 367 // expected-note@-1{{Reference-counted object is used after it is released}} 368 } 369 370 unsigned int check_leak_explicit_new() { 371 OSArray *arr = new OSArray; // expected-note{{Operator 'new' returns an OSObject of type 'OSArray' with a +1 retain count}} 372 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}} 373 // expected-warning@-1{{Potential leak of an object stored into 'arr'}} 374 } 375 376 unsigned int check_leak_factory() { 377 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 378 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}} 379 // expected-warning@-1{{Potential leak of an object stored into 'arr'}} 380 } 381 382 void check_get_object() { 383 OSObject::getObject(); 384 } 385 386 void check_Get_object() { 387 OSObject::GetObject(); 388 } 389 390 void check_custom_iterator_rule(OSArray *arr) { 391 OSIterator *it = arr->getIterator(); 392 it->release(); 393 } 394 395 void check_iterator_leak(OSArray *arr) { 396 arr->getIterator(); // expected-note{{Call to method 'OSArray::getIterator' returns an OSObject of type 'OSIterator' with a +1 retain count}} 397 } // expected-note{{Object leaked: allocated object of type 'OSIterator' is not referenced later}} 398 // expected-warning@-1{{Potential leak of an object of type 'OSIterator}}' 399 400 void check_no_invalidation() { 401 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 402 OtherStruct::doNothingToArray(arr); 403 } // expected-warning{{Potential leak of an object stored into 'arr'}} 404 // expected-note@-1{{Object leaked}} 405 406 void check_no_invalidation_other_struct() { 407 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 408 OtherStruct other(arr); // expected-warning{{Potential leak}} 409 // expected-note@-1{{Object leaked}} 410 } 411 412 struct ArrayOwner : public OSObject { 413 OSArray *arr; 414 ArrayOwner(OSArray *arr) : arr(arr) {} 415 416 static ArrayOwner* create(OSArray *arr) { 417 return new ArrayOwner(arr); 418 } 419 420 OSArray *getArray() { 421 return arr; 422 } 423 424 OSArray *createArray() { 425 return OSArray::withCapacity(10); 426 } 427 428 OSArray *createArraySourceUnknown(); 429 430 OSArray *getArraySourceUnknown(); 431 }; 432 433 OSArray *generateArray() { 434 return OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 435 // expected-note@-1{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 436 } 437 438 unsigned int check_leak_good_error_message() { 439 unsigned int out; 440 { 441 OSArray *leaked = generateArray(); // expected-note{{Calling 'generateArray'}} 442 // expected-note@-1{{Returning from 'generateArray'}} 443 out = leaked->getCount(); // expected-warning{{Potential leak of an object stored into 'leaked'}} 444 // 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}} 445 } 446 return out; 447 } 448 449 unsigned int check_leak_msg_temporary() { 450 return generateArray()->getCount(); // expected-warning{{Potential leak of an object}} 451 // expected-note@-1{{Calling 'generateArray'}} 452 // expected-note@-2{{Returning from 'generateArray'}} 453 // 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}} 454 } 455 456 void check_confusing_getters() { 457 OSArray *arr = OSArray::withCapacity(10); 458 459 ArrayOwner *AO = ArrayOwner::create(arr); 460 AO->getArray(); 461 462 AO->release(); 463 arr->release(); 464 } 465 466 void check_rc_consumed() { 467 OSArray *arr = OSArray::withCapacity(10); 468 OSArray::consumeArray(arr); 469 } 470 471 void check_rc_consume_temporary() { 472 OSArray::consumeArray(OSArray::withCapacity(10)); 473 } 474 475 void check_rc_getter() { 476 OSArray *arr = OSArray::MaskedGetter(); 477 (void)arr; 478 } 479 480 void check_rc_create() { 481 OSArray *arr = OSArray::getOoopsActuallyCreate(); 482 arr->release(); 483 } 484 485 486 void check_dynamic_cast() { 487 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); 488 arr->release(); 489 } 490 491 void check_required_cast() { 492 OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1)); 493 arr->release(); // no-warning 494 } 495 496 void check_cast_behavior(OSObject *obj) { 497 OSArray *arr1 = OSDynamicCast(OSArray, obj); 498 clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}} 499 // expected-note@-1{{TRUE}} 500 // expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}} 501 // expected-warning@-3{{FALSE}} 502 // expected-note@-4 {{FALSE}} 503 OSArray *arr2 = OSRequiredCast(OSArray, obj); 504 clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}} 505 // expected-note@-1{{TRUE}} 506 } 507 508 unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) { 509 OSArray *arr = OSDynamicCast(OSArray, obj); 510 if (arr) { 511 return arr->getCount(); 512 } else { 513 514 // The fact that dynamic cast has failed should not imply that 515 // the input object was null. 516 return obj->foo(); // no-warning 517 } 518 } 519 520 void check_dynamic_cast_null_branch(OSObject *obj) { 521 OSArray *arr1 = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject}} 522 OSArray *arr = OSDynamicCast(OSArray, obj); // expected-note{{Assuming dynamic cast returns null due to type mismatch}} 523 if (!arr) // expected-note{{'arr' is null}} 524 // expected-note@-1{{Taking true branch}} 525 return; // expected-warning{{Potential leak of an object stored into 'arr1'}} 526 // expected-note@-1{{Object leaked}} 527 arr1->release(); 528 } 529 530 void check_dynamic_cast_null_check() { 531 OSArray *arr = OSDynamicCast(OSArray, OSObject::generateObject(1)); // expected-note{{Call to method 'OSObject::generateObject' returns an OSObject}} 532 // expected-warning@-1{{Potential leak of an object}} 533 // expected-note@-2{{Object leaked}} 534 // expected-note@-3{{Assuming dynamic cast returns null due to type mismatch}} 535 if (!arr) 536 return; 537 arr->release(); 538 } 539 540 void use_after_release() { 541 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 542 arr->release(); // expected-note{{Object released}} 543 arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}} 544 // expected-note@-1{{Reference-counted object is used after it is released}} 545 } 546 547 void potential_leak() { 548 OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to method 'OSArray::withCapacity' returns an OSObject of type 'OSArray' with a +1 retain count}} 549 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} 550 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}} 551 arr->getCount(); 552 } // expected-warning{{Potential leak of an object stored into 'arr'}} 553 // 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}} 554 555 void proper_cleanup() { 556 OSArray *arr = OSArray::withCapacity(10); // +1 557 arr->retain(); // +2 558 arr->release(); // +1 559 arr->getCount(); 560 arr->release(); // 0 561 } 562 563 unsigned int no_warning_on_getter(ArrayOwner *owner) { 564 OSArray *arr = owner->getArray(); 565 return arr->getCount(); 566 } 567 568 unsigned int warn_on_overrelease(ArrayOwner *owner) { 569 // FIXME: summaries are not applied in case the source of the getter/setter 570 // is known. 571 // rdar://45681203 572 OSArray *arr = owner->getArray(); 573 arr->release(); 574 return arr->getCount(); 575 } 576 577 unsigned int nowarn_on_release_of_created(ArrayOwner *owner) { 578 OSArray *arr = owner->createArray(); 579 unsigned int out = arr->getCount(); 580 arr->release(); 581 return out; 582 } 583 584 unsigned int nowarn_on_release_of_created_source_unknown(ArrayOwner *owner) { 585 OSArray *arr = owner->createArraySourceUnknown(); 586 unsigned int out = arr->getCount(); 587 arr->release(); 588 return out; 589 } 590 591 unsigned int no_warn_ok_release(ArrayOwner *owner) { 592 OSArray *arr = owner->getArray(); // +0 593 arr->retain(); // +1 594 arr->release(); // +0 595 return arr->getCount(); // no-warning 596 } 597 598 unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) { 599 OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{Call to method 'ArrayOwner::getArraySourceUnknown' returns an OSObject of type 'OSArray' with a +0 retain count}} 600 arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} 601 // expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} 602 return arr->getCount(); 603 } 604 605 unsigned int ok_release_with_unknown_source(ArrayOwner *owner) { 606 OSArray *arr = owner->getArraySourceUnknown(); // +0 607 arr->retain(); // +1 608 arr->release(); // +0 609 return arr->getCount(); 610 } 611 612 OSObject *getObject(); 613 typedef bool (^Blk)(OSObject *); 614 615 void test_escape_to_unknown_block(Blk blk) { 616 blk(getObject()); // no-crash 617 } 618 619 using OSObjectPtr = os::smart_ptr<OSObject>; 620 621 void test_smart_ptr_uaf() { 622 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 623 { 624 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}} 625 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}} 626 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}} 627 // expected-note@os_smart_ptr.h:13{{Taking true branch}} 628 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} 629 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}} 630 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}} 631 } // expected-note{{Calling '~smart_ptr'}} 632 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}} 633 // expected-note@os_smart_ptr.h:35{{Taking true branch}} 634 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} 635 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}} 636 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} 637 // expected-note@-6{{Returning from '~smart_ptr'}} 638 obj->release(); // expected-note{{Object released}} 639 obj->release(); // expected-warning{{Reference-counted object is used after it is released}} 640 // expected-note@-1{{Reference-counted object is used after it is released}} 641 } 642 643 void test_smart_ptr_leak() { 644 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 645 { 646 OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}} 647 // expected-note@-1{{Returning from constructor for 'smart_ptr<OSObject>'}} 648 // expected-note@os_smart_ptr.h:13{{Field 'pointer' is non-null}} 649 // expected-note@os_smart_ptr.h:13{{Taking true branch}} 650 // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} 651 // expected-note@os_smart_ptr.h:71{{Reference count incremented. The object now has a +2 retain count}} 652 // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}} 653 } // expected-note{{Calling '~smart_ptr'}} 654 // expected-note@os_smart_ptr.h:35{{Field 'pointer' is non-null}} 655 // expected-note@os_smart_ptr.h:35{{Taking true branch}} 656 // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} 657 // expected-note@os_smart_ptr.h:76{{Reference count decremented. The object now has a +1 retain count}} 658 // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} 659 // expected-note@-6{{Returning from '~smart_ptr'}} 660 } // expected-warning{{Potential leak of an object stored into 'obj'}} 661 // 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}} 662 663 void test_smart_ptr_no_leak() { 664 OSObject *obj = new OSObject; 665 { 666 OSObjectPtr p(obj); 667 } 668 obj->release(); 669 } 670 671 OSObject *getRuleViolation() { 672 return new OSObject; // expected-warning{{Potential leak of an object of type 'OSObject'}} 673 // expected-note@-1{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 674 // expected-note@-2{{Object leaked: allocated object of type 'OSObject' is returned from a function whose name ('getRuleViolation') starts with 'get'}} 675 } 676 677 OSObject *createRuleViolation(OSObject *param) { // expected-note{{Parameter 'param' starts at +0}} 678 return param; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} 679 // expected-note@-1{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} 680 } 681 682 void test_ostypealloc_correct_diagnostic_name() { 683 OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}} 684 arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} 685 arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}} 686 } // 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}} 687 // expected-warning@-1{{Potential leak of an object stored into 'arr'}} 688 689 void escape_elsewhere(OSObject *obj); 690 691 void test_free_on_escaped_object_diagnostics() { 692 OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} 693 escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}} 694 obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}} 695 // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}} 696 } 697 698 void test_tagged_retain_no_leak() { 699 OSObject *obj = new OSObject; 700 obj->taggedRelease(); 701 } 702 703 void test_tagged_retain_no_uaf() { 704 OSObject *obj = new OSObject; 705 obj->taggedRetain(); 706 obj->release(); 707 obj->release(); 708 } 709 710 class IOService { 711 public: 712 OSObject *somethingMatching(OSObject *table = 0); 713 }; 714 715 OSObject *testSuppressionForMethodsEndingWithMatching(IOService *svc, 716 OSObject *table = 0) { 717 // This probably just passes table through. We should probably not make 718 // ptr1 definitely equal to table, but we should not warn about leaks. 719 OSObject *ptr1 = svc->somethingMatching(table); // no-warning 720 721 // FIXME: This, however, should follow the Create Rule regardless. 722 // We should warn about the leak here. 723 OSObject *ptr2 = svc->somethingMatching(); // no-warning 724 725 if (!table) 726 table = OSTypeAlloc(OSArray); 727 728 // This function itself ends with "Matching"! Do not warn when we're 729 // returning from it at +0. 730 return table; // no-warning 731 } 732 733 namespace weird_result { 734 struct WeirdResult { 735 int x, y, z; 736 }; 737 738 WeirdResult outParamWithWeirdResult(OS_RETURNS_RETAINED_ON_ZERO OSObject **obj); 739 740 WeirdResult testOutParamWithWeirdResult() { 741 OSObject *obj; 742 return outParamWithWeirdResult(&obj); // no-warning 743 } 744 } // namespace weird_result 745 746 namespace inherited_constructor_crash { 747 struct a { 748 a(int); 749 }; 750 struct b : a { 751 // This is an "inherited constructor". 752 using a::a; 753 }; 754 void test() { 755 // RetainCountChecker used to crash when looking for a summary 756 // for the inherited constructor invocation. 757 b(0); 758 } 759 } // namespace inherited_constructor_crash 760 761 namespace ossymbol_suppression { 762 OSSymbol *createSymbol(); 763 void test() { 764 OSSymbol *sym = createSymbol(); // no-warning 765 } 766 } // namespace ossymbol_suppression 767