|
3 | 3 | // RUN: -analyzer-checker=core \ |
4 | 4 | // RUN: -analyzer-checker=cplusplus.NewDelete |
5 | 5 | // |
6 | | -// RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks %s \ |
| 6 | +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks %s \ |
7 | 7 | // RUN: -verify=expected,newdelete,leak \ |
8 | 8 | // RUN: -analyzer-checker=core \ |
9 | 9 | // RUN: -analyzer-checker=cplusplus.NewDelete \ |
10 | 10 | // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks |
11 | 11 | // |
12 | | -// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ |
| 12 | +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks %s \ |
13 | 13 | // RUN: -verify=expected,leak \ |
14 | 14 | // RUN: -analyzer-checker=core \ |
15 | 15 | // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks |
|
19 | 19 | // RUN: -analyzer-checker=core \ |
20 | 20 | // RUN: -analyzer-checker=cplusplus.NewDelete |
21 | 21 | // |
22 | | -// RUN: %clang_analyze_cc1 -DLEAKS -std=c++17 -fblocks %s \ |
| 22 | +// RUN: %clang_analyze_cc1 -std=c++17 -fblocks %s \ |
23 | 23 | // RUN: -verify=expected,newdelete,leak \ |
24 | 24 | // RUN: -analyzer-checker=core \ |
25 | 25 | // RUN: -analyzer-checker=cplusplus.NewDelete \ |
26 | 26 | // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks |
27 | 27 | // |
28 | | -// RUN: %clang_analyze_cc1 -std=c++17 -fblocks -verify %s \ |
| 28 | +// RUN: %clang_analyze_cc1 -std=c++17 -fblocks %s \ |
29 | 29 | // RUN: -verify=expected,leak,inspection \ |
30 | 30 | // RUN: -analyzer-checker=core \ |
31 | 31 | // RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ |
@@ -503,3 +503,75 @@ namespace optional_union { |
503 | 503 | custom_union_t a; |
504 | 504 | } // leak-warning{{Potential leak of memory pointed to by 'a.present.q'}} |
505 | 505 | } |
| 506 | + |
| 507 | +namespace gh153782 { |
| 508 | + |
| 509 | +// Ensure we do not regress on the following use case. |
| 510 | + |
| 511 | +namespace mutually_exclusive_test_case_1 { |
| 512 | +struct StorageWrapper { |
| 513 | + // Imagine those two call a reset() function (among other things) |
| 514 | + ~StorageWrapper() { delete parts; } |
| 515 | + StorageWrapper(StorageWrapper const&) = default; |
| 516 | + |
| 517 | + // Mind that there is no assignment here -- this is the bug we would like to find. |
| 518 | + void operator=(StorageWrapper&&) { delete parts; } // newdelete-warning{{Attempt to release already released memory}} |
| 519 | + |
| 520 | + // Not provided, typically would do `parts = new long`. |
| 521 | + StorageWrapper(); |
| 522 | + |
| 523 | + long* parts; |
| 524 | +}; |
| 525 | + |
| 526 | +void test_non_trivial_struct_assignment() { |
| 527 | + StorageWrapper* object = new StorageWrapper[]{StorageWrapper()}; |
| 528 | + object[0] = StorageWrapper(); // This assignment leads to the double-free. |
| 529 | +} |
| 530 | +} // mutually_exclusive_test_case_1 |
| 531 | + |
| 532 | +namespace mutually_exclusive_test_case_2 { |
| 533 | +struct StorageWrapper { |
| 534 | + // Imagine those two call a reset() function (among other things) |
| 535 | + ~StorageWrapper() { delete parts; } |
| 536 | + StorageWrapper(StorageWrapper const&) = default; |
| 537 | + |
| 538 | + // Mind that there is no assignment here -- this is the bug we would like to find. |
| 539 | + void operator=(StorageWrapper&&) { delete parts; } |
| 540 | + |
| 541 | + // Not provided, typically would do `parts = new long`. |
| 542 | + StorageWrapper(); |
| 543 | + |
| 544 | + long* parts; |
| 545 | +}; |
| 546 | + |
| 547 | +void test_non_trivial_struct_assignment() { |
| 548 | + StorageWrapper* object = new StorageWrapper[]{StorageWrapper()}; |
| 549 | + // object[0] = StorageWrapper(); // Remove the source of double free to make the potential leak appear. |
| 550 | +} // leak-warning{{Potential leak of memory pointed to by 'object'}} |
| 551 | +} // mutually_exclusive_test_case_2 |
| 552 | + |
| 553 | +namespace mutually_exclusive_test_case_3 { |
| 554 | +struct StorageWrapper { |
| 555 | + // Imagine those two call a reset() function (among other things) |
| 556 | + ~StorageWrapper() { delete parts; } |
| 557 | + StorageWrapper(StorageWrapper const&) = default; |
| 558 | + |
| 559 | + // Mind that there is no assignment here -- this is the bug we would like to find. |
| 560 | + void operator=(StorageWrapper&&) { delete parts; } // newdelete-warning{{Attempt to release already released memory}} |
| 561 | + |
| 562 | + // Not provided, typically would do `parts = new long`. |
| 563 | + StorageWrapper(); |
| 564 | + |
| 565 | + long* parts; |
| 566 | +}; |
| 567 | + |
| 568 | +struct TestDoubleFreeWithInitializerList { |
| 569 | + StorageWrapper* Object; |
| 570 | + TestDoubleFreeWithInitializerList() |
| 571 | + : Object(new StorageWrapper[]{StorageWrapper()}) { |
| 572 | + Object[0] = StorageWrapper(); // This assignment leads to the double-free. |
| 573 | + } |
| 574 | +}; |
| 575 | +} // mutually_exclusive_test_case_3 |
| 576 | + |
| 577 | +} // namespace gh153782 |
0 commit comments