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