Skip to content

Commit dbd3caa

Browse files
[NFC] Introduce regression tests
1 parent 6421727 commit dbd3caa

File tree

2 files changed

+104
-4
lines changed

2 files changed

+104
-4
lines changed

clang/test/Analysis/NewDelete-checker-test.cpp

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
// RUN: -analyzer-checker=core \
44
// RUN: -analyzer-checker=cplusplus.NewDelete
55
//
6-
// RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks %s \
6+
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks %s \
77
// RUN: -verify=expected,newdelete,leak \
88
// RUN: -analyzer-checker=core \
99
// RUN: -analyzer-checker=cplusplus.NewDelete \
1010
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks
1111
//
12-
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \
12+
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks %s \
1313
// RUN: -verify=expected,leak \
1414
// RUN: -analyzer-checker=core \
1515
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks
@@ -19,13 +19,13 @@
1919
// RUN: -analyzer-checker=core \
2020
// RUN: -analyzer-checker=cplusplus.NewDelete
2121
//
22-
// RUN: %clang_analyze_cc1 -DLEAKS -std=c++17 -fblocks %s \
22+
// RUN: %clang_analyze_cc1 -std=c++17 -fblocks %s \
2323
// RUN: -verify=expected,newdelete,leak \
2424
// RUN: -analyzer-checker=core \
2525
// RUN: -analyzer-checker=cplusplus.NewDelete \
2626
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks
2727
//
28-
// RUN: %clang_analyze_cc1 -std=c++17 -fblocks -verify %s \
28+
// RUN: %clang_analyze_cc1 -std=c++17 -fblocks %s \
2929
// RUN: -verify=expected,leak,inspection \
3030
// RUN: -analyzer-checker=core \
3131
// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \
@@ -503,3 +503,75 @@ namespace optional_union {
503503
custom_union_t a;
504504
} // leak-warning{{Potential leak of memory pointed to by 'a.present.q'}}
505505
}
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

clang/test/Analysis/ctor-trivial-copy.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,31 @@ void entrypoint() {
117117
}
118118

119119
} // namespace trivial_struct_copy
120+
121+
namespace gh153782 {
122+
123+
// Ensure we do not regress on the following use cases.
124+
// The assumption made on a field in `setPtr` should apply to the returned copy in `func`.
125+
struct Status { int error; };
126+
Status getError();
127+
128+
Status setPtr(int **outptr, int* ptr) {
129+
Status e = getError();
130+
if (e.error != 0) return e; // When assuming the error field is non-zero,
131+
*outptr = ptr; // this is not executed
132+
return e;
133+
}
134+
135+
int func() {
136+
int *ptr = nullptr;
137+
int x = 42;
138+
if (setPtr(&ptr, &x).error == 0) {
139+
// The assumption made in get() SHOULD match the assumption about
140+
// the returned value, hence the engine SHOULD NOT assume ptr is null.
141+
clang_analyzer_dump_val(ptr); // expected-warning {{&x}}
142+
return *ptr;
143+
}
144+
return 0;
145+
}
146+
147+
} // namespace gh153782

0 commit comments

Comments
 (0)