1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
// RUN: %clang_analyze_cc1 -verify -analyzer-output=text %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=cplusplus \
// RUN: -analyzer-checker=unix
#include "Inputs/system-header-simulator-for-malloc.h"
//===----------------------------------------------------------------------===//
// unique_ptr test cases
//===----------------------------------------------------------------------===//
namespace unique_ptr_tests {
// Custom unique_ptr implementation for testing
template <typename T>
struct unique_ptr {
T* ptr;
unique_ptr(T* p) : ptr(p) {}
~unique_ptr() {
// This destructor intentionally doesn't delete 'ptr' to validate that the
// heuristic trusts that smart pointers (based on their class name) will
// release the pointee even if it doesn't understand their destructor.
}
unique_ptr(unique_ptr&& other) : ptr(other.ptr) { other.ptr = nullptr; }
T* get() const { return ptr; }
};
template <typename T, typename... Args>
unique_ptr<T> make_unique(Args&&... args) {
return unique_ptr<T>(new T(args...));
}
// Test 1: Check that we report leaks for malloc when passing smart pointers
void add_unique_ptr(unique_ptr<int> ptr) {
// The unique_ptr destructor will be called when ptr goes out of scope
}
void test_malloc_with_smart_ptr() {
void *ptr = malloc(4); // expected-note {{Memory is allocated}}
add_unique_ptr(make_unique<int>(1));
(void)ptr;
// expected-warning@+1 {{Potential leak of memory pointed to by 'ptr'}} expected-note@+1 {{Potential leak of memory pointed to by 'ptr'}}
}
// Test 2: Check that we don't report leaks for unique_ptr in temporary objects
struct Foo {
unique_ptr<int> i;
};
void add_foo(Foo foo) {
// The unique_ptr destructor will be called when foo goes out of scope
}
void test_temporary_object() {
// No warning should be emitted for this - the memory is managed by unique_ptr
// in the temporary Foo object, which will properly clean up the memory
add_foo({make_unique<int>(1)});
}
// Test 3: Check that we don't report leaks for smart pointers in base class fields
struct Base {
unique_ptr<int> base_ptr;
Base() : base_ptr(nullptr) {}
Base(unique_ptr<int>&& ptr) : base_ptr(static_cast<unique_ptr<int>&&>(ptr)) {}
};
struct Derived : public Base {
int derived_field;
Derived() : Base(), derived_field(0) {}
Derived(unique_ptr<int>&& ptr, int field) : Base(static_cast<unique_ptr<int>&&>(ptr)), derived_field(field) {}
};
void add_derived(Derived derived) {
// The unique_ptr destructor will be called when derived goes out of scope
// This should include the base_ptr field from the base class
}
void test_base_class_smart_ptr() {
// No warning should be emitted for this - the memory is managed by unique_ptr
// in the base class field of the temporary Derived object
add_derived(Derived(make_unique<int>(1), 42));
}
// Test 4: Check that we don't report leaks for multiple owning arguments
struct SinglePtr {
unique_ptr<int> ptr;
SinglePtr(unique_ptr<int>&& p) : ptr(static_cast<unique_ptr<int>&&>(p)) {}
};
struct MultiPtr {
unique_ptr<int> ptr1;
unique_ptr<int> ptr2;
unique_ptr<int> ptr3;
MultiPtr(unique_ptr<int>&& p1, unique_ptr<int>&& p2, unique_ptr<int>&& p3)
: ptr1(static_cast<unique_ptr<int>&&>(p1))
, ptr2(static_cast<unique_ptr<int>&&>(p2))
, ptr3(static_cast<unique_ptr<int>&&>(p3)) {}
};
void addMultiple(SinglePtr single, MultiPtr multi) {
// All unique_ptr destructors will be called when the objects go out of scope
// This tests handling of multiple by-value arguments with smart pointer fields
}
void test_multiple_owning_args() {
// No warning should be emitted - all memory is properly managed by unique_ptr
// in the temporary objects, which will properly clean up the memory
addMultiple(
SinglePtr(make_unique<int>(1)),
MultiPtr(make_unique<int>(2), make_unique<int>(3), make_unique<int>(4))
);
}
// Test 5: Check that we DO report leaks for raw pointers in mixed ownership scenarios
struct MixedOwnership {
unique_ptr<int> smart_ptr; // Should NOT leak (smart pointer managed)
int *raw_ptr; // Should leak (raw pointer)
MixedOwnership() : smart_ptr(make_unique<int>(1)), raw_ptr(new int(42)) {} // expected-note {{Memory is allocated}}
};
void consume(MixedOwnership obj) {
// The unique_ptr destructor will be called when obj goes out of scope
// But raw_ptr will leak!
}
void test_mixed_ownership() {
// This should report a leak for raw_ptr but not for smart_ptr
consume(MixedOwnership()); // expected-note {{Calling default constructor for 'MixedOwnership'}} expected-note {{Returning from default constructor for 'MixedOwnership'}}
} // expected-warning {{Potential memory leak}} expected-note {{Potential memory leak}}
// Test 6: Check that we handle direct smart pointer constructor calls correctly
void test_direct_constructor() {
// Direct constructor call - should not leak
int* raw_ptr = new int(42);
unique_ptr<int> smart(raw_ptr); // This should escape the raw_ptr symbol
// No leak should be reported here since smart pointer takes ownership
}
void test_mixed_direct_constructor() {
int* raw1 = new int(1);
int* raw2 = new int(2); // expected-note {{Memory is allocated}}
unique_ptr<int> smart(raw1); // This should escape raw1
// raw2 should leak since it's not managed by any smart pointer
int x = *raw2; // expected-warning {{Potential leak of memory pointed to by 'raw2'}} expected-note {{Potential leak of memory pointed to by 'raw2'}}
}
// Test 7: Multiple memory owning arguments - demonstrates addTransition API usage
void addMultipleOwningArgs(
unique_ptr<int> ptr1,
unique_ptr<int> ptr2,
unique_ptr<int> ptr3
) {
// All unique_ptr destructors will be called when arguments go out of scope
// This tests handling of multiple smart pointer parameters in a single call
}
void test_multiple_memory_owning_arguments() {
// No warning should be emitted - all memory is properly managed by unique_ptr
// This test specifically exercises the addTransition API with multiple owning arguments
addMultipleOwningArgs(
make_unique<int>(1),
make_unique<int>(2),
make_unique<int>(3)
);
}
} // namespace unique_ptr_tests
//===----------------------------------------------------------------------===//
// Variadic constructor test cases
//===----------------------------------------------------------------------===//
namespace variadic_constructor_tests {
// Variadic constructor - test for potential out-of-bounds access
// This is the only test in this namespace and tests a scenario where Call.getNumArgs() > CD->getNumParams()
// We use a synthetic unique_ptr here to activate the specific logic in the MallocChecker that will test out of bounds
template <typename T>
struct unique_ptr {
T* ptr;
// Constructor with ellipsis - can receive more arguments than parameters
unique_ptr(T* p, ...) : ptr(p) {}
~unique_ptr() {
// This destructor intentionally doesn't delete 'ptr' to validate that the
// heuristic trusts that smart pointers (based on their class name) will
// release the pointee even if it doesn't understand their destructor.
}
};
void process_variadic_smart_ptr(unique_ptr<int> ptr) {
// Function body doesn't matter for this test
}
void test_variadic_constructor_bounds() {
void *malloc_ptr = malloc(4); // expected-note {{Memory is allocated}}
// This call creates a smart pointer with more arguments than formal parameters
// The constructor has 1 formal parameter (T* p) plus ellipsis, but we pass multiple args
// This should trigger the bounds checking issue in handleSmartPointerConstructorArguments
int* raw_ptr = new int(42);
process_variadic_smart_ptr(unique_ptr<int>(raw_ptr, 1, 2, 3, 4, 5));
(void)malloc_ptr;
} // expected-warning {{Potential leak of memory pointed to by 'malloc_ptr'}}
// expected-note@-1 {{Potential leak of memory pointed to by 'malloc_ptr'}}
} // namespace variadic_constructor_tests
//===----------------------------------------------------------------------===//
// shared_ptr test cases
//===----------------------------------------------------------------------===//
namespace shared_ptr_tests {
// Custom shared_ptr implementation for testing
template <typename T>
struct shared_ptr {
T* ptr;
shared_ptr(T* p) : ptr(p) {}
~shared_ptr() {
// This destructor intentionally doesn't delete 'ptr' to validate that the
// heuristic trusts that smart pointers (based on their class name) will
// release the pointee even if it doesn't understand their destructor.
}
shared_ptr(shared_ptr&& other) : ptr(other.ptr) { other.ptr = nullptr; }
T* get() const { return ptr; }
};
template <typename T, typename... Args>
shared_ptr<T> make_shared(Args&&... args) {
return shared_ptr<T>(new T(args...));
}
// Test 1: Check that we don't report leaks for shared_ptr in temporary objects
struct Foo {
shared_ptr<int> i;
};
void add_foo(Foo foo) {
// The shared_ptr destructor will be called when foo goes out of scope
}
void test_temporary_object() {
// No warning should be emitted for this - the memory is managed by shared_ptr
// in the temporary Foo object, which will properly clean up the memory
add_foo({make_shared<int>(1)});
}
} // namespace shared_ptr_tests
|