1 | // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.InnerPointer \ |
2 | // RUN: %s -analyzer-output=text -verify |
3 | |
4 | #include "Inputs/system-header-simulator-cxx.h" |
5 | namespace std { |
6 | |
7 | template <typename T> |
8 | void func_ref(T &a); |
9 | |
10 | template <typename T> |
11 | void func_const_ref(const T &a); |
12 | |
13 | template <typename T> |
14 | void func_value(T a); |
15 | |
16 | string my_string = "default"; |
17 | void default_arg(int a = 42, string &b = my_string); |
18 | |
19 | } // end namespace std |
20 | |
21 | void consume(const char *) {} |
22 | void consume(const wchar_t *) {} |
23 | void consume(const char16_t *) {} |
24 | void consume(const char32_t *) {} |
25 | |
26 | //=--------------------------------------=// |
27 | // `std::string` member functions // |
28 | //=--------------------------------------=// |
29 | |
30 | void deref_after_scope_char(bool cond) { |
31 | const char *c, *d; |
32 | { |
33 | std::string s; |
34 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
35 | d = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
36 | } // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
37 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
38 | std::string s; |
39 | const char *c2 = s.c_str(); |
40 | if (cond) { |
41 | // expected-note@-1 {{Assuming 'cond' is not equal to 0}} |
42 | // expected-note@-2 {{Taking true branch}} |
43 | // expected-note@-3 {{Assuming 'cond' is 0}} |
44 | // expected-note@-4 {{Taking false branch}} |
45 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
46 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
47 | } else { |
48 | consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}} |
49 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
50 | } |
51 | } |
52 | |
53 | void deref_after_scope_char_data_non_const() { |
54 | char *c; |
55 | { |
56 | std::string s; |
57 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
58 | } // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
59 | std::string s; |
60 | char *c2 = s.data(); |
61 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
62 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
63 | } |
64 | |
65 | void deref_after_scope_wchar_t(bool cond) { |
66 | const wchar_t *c, *d; |
67 | { |
68 | std::wstring s; |
69 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}} |
70 | d = s.data(); // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}} |
71 | } // expected-note {{Inner buffer of 'std::wstring' deallocated by call to destructor}} |
72 | // expected-note@-1 {{Inner buffer of 'std::wstring' deallocated by call to destructor}} |
73 | std::wstring s; |
74 | const wchar_t *c2 = s.c_str(); |
75 | if (cond) { |
76 | // expected-note@-1 {{Assuming 'cond' is not equal to 0}} |
77 | // expected-note@-2 {{Taking true branch}} |
78 | // expected-note@-3 {{Assuming 'cond' is 0}} |
79 | // expected-note@-4 {{Taking false branch}} |
80 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
81 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
82 | } else { |
83 | consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}} |
84 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
85 | } |
86 | } |
87 | |
88 | void deref_after_scope_char16_t_cstr() { |
89 | const char16_t *c16; |
90 | { |
91 | std::u16string s16; |
92 | c16 = s16.c_str(); // expected-note {{Pointer to inner buffer of 'std::u16string' obtained here}} |
93 | } // expected-note {{Inner buffer of 'std::u16string' deallocated by call to destructor}} |
94 | std::u16string s16; |
95 | const char16_t *c16_2 = s16.c_str(); |
96 | consume(c16); // expected-warning {{Inner pointer of container used after re/deallocation}} |
97 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
98 | } |
99 | |
100 | void deref_after_scope_char32_t_data() { |
101 | const char32_t *c32; |
102 | { |
103 | std::u32string s32; |
104 | c32 = s32.data(); // expected-note {{Pointer to inner buffer of 'std::u32string' obtained here}} |
105 | } // expected-note {{Inner buffer of 'std::u32string' deallocated by call to destructor}} |
106 | std::u32string s32; |
107 | const char32_t *c32_2 = s32.data(); |
108 | consume(c32); // expected-warning {{Inner pointer of container used after re/deallocation}} |
109 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
110 | } |
111 | |
112 | void multiple_symbols(bool cond) { |
113 | const char *c1, *d1; |
114 | { |
115 | std::string s1; |
116 | c1 = s1.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
117 | d1 = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
118 | const char *local = s1.c_str(); |
119 | consume(local); // no-warning |
120 | } // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
121 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
122 | std::string s2; |
123 | const char *c2 = s2.c_str(); |
124 | if (cond) { |
125 | // expected-note@-1 {{Assuming 'cond' is not equal to 0}} |
126 | // expected-note@-2 {{Taking true branch}} |
127 | // expected-note@-3 {{Assuming 'cond' is 0}} |
128 | // expected-note@-4 {{Taking false branch}} |
129 | consume(c1); // expected-warning {{Inner pointer of container used after re/deallocation}} |
130 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
131 | } else { |
132 | consume(d1); // expected-warning {{Inner pointer of container used after re/deallocation}} |
133 | } // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
134 | } |
135 | |
136 | void deref_after_scope_ok(bool cond) { |
137 | const char *c, *d; |
138 | std::string s; |
139 | { |
140 | c = s.c_str(); |
141 | d = s.data(); |
142 | } |
143 | if (cond) |
144 | consume(c); // no-warning |
145 | else |
146 | consume(d); // no-warning |
147 | } |
148 | |
149 | void deref_after_equals() { |
150 | const char *c; |
151 | std::string s = "hello"; |
152 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
153 | s = "world"; // expected-note {{Inner buffer of 'std::string' reallocated by call to 'operator='}} |
154 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
155 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
156 | } |
157 | |
158 | void deref_after_plus_equals() { |
159 | const char *c; |
160 | std::string s = "hello"; |
161 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
162 | s += " world"; // expected-note {{Inner buffer of 'std::string' reallocated by call to 'operator+='}} |
163 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
164 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
165 | } |
166 | |
167 | void deref_after_clear() { |
168 | const char *c; |
169 | std::string s; |
170 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
171 | s.clear(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'clear'}} |
172 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
173 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
174 | } |
175 | |
176 | void deref_after_append() { |
177 | const char *c; |
178 | std::string s = "hello"; |
179 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
180 | s.append(2, 'x'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'append'}} |
181 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
182 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
183 | } |
184 | |
185 | void deref_after_assign() { |
186 | const char *c; |
187 | std::string s; |
188 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
189 | s.assign(4, 'a'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'assign'}} |
190 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
191 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
192 | } |
193 | |
194 | void deref_after_erase() { |
195 | const char *c; |
196 | std::string s = "hello"; |
197 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
198 | s.erase(0, 2); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'erase'}} |
199 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
200 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
201 | } |
202 | |
203 | void deref_after_insert() { |
204 | const char *c; |
205 | std::string s = "ello"; |
206 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
207 | s.insert(0, 1, 'h'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'insert'}} |
208 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
209 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
210 | } |
211 | |
212 | void deref_after_replace() { |
213 | const char *c; |
214 | std::string s = "hello world"; |
215 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
216 | s.replace(6, 5, "string"); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'replace'}} |
217 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
218 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
219 | } |
220 | |
221 | void deref_after_pop_back() { |
222 | const char *c; |
223 | std::string s; |
224 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
225 | s.pop_back(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'pop_back'}} |
226 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
227 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
228 | } |
229 | |
230 | void deref_after_push_back() { |
231 | const char *c; |
232 | std::string s; |
233 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
234 | s.push_back('c'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'push_back'}} |
235 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
236 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
237 | } |
238 | |
239 | void deref_after_reserve() { |
240 | const char *c; |
241 | std::string s; |
242 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
243 | s.reserve(5); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'reserve'}} |
244 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
245 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
246 | } |
247 | |
248 | void deref_after_resize() { |
249 | const char *c; |
250 | std::string s; |
251 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
252 | s.resize(5); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'resize'}} |
253 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
254 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
255 | } |
256 | |
257 | void deref_after_shrink_to_fit() { |
258 | const char *c; |
259 | std::string s; |
260 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
261 | s.shrink_to_fit(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'shrink_to_fit'}} |
262 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
263 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
264 | } |
265 | |
266 | void deref_after_swap() { |
267 | const char *c; |
268 | std::string s1, s2; |
269 | c = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
270 | s1.swap(s2); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'swap'}} |
271 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
272 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
273 | } |
274 | |
275 | struct S { |
276 | std::string s; |
277 | const char *name() { |
278 | return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
279 | // expected-note@-1 {{Pointer to inner buffer of 'std::string' obtained here}} |
280 | } |
281 | void clear() { |
282 | s.clear(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'clear'}} |
283 | } |
284 | ~S() {} // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
285 | }; |
286 | |
287 | void cleared_through_method() { |
288 | S x; |
289 | const char *c = x.name(); // expected-note {{Calling 'S::name'}} |
290 | // expected-note@-1 {{Returning from 'S::name'}} |
291 | x.clear(); // expected-note {{Calling 'S::clear'}} |
292 | // expected-note@-1 {{Returning; inner buffer was reallocated}} |
293 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
294 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
295 | } |
296 | |
297 | void destroyed_through_method() { |
298 | S y; |
299 | const char *c = y.name(); // expected-note {{Calling 'S::name'}} |
300 | // expected-note@-1 {{Returning from 'S::name'}} |
301 | y.~S(); // expected-note {{Calling '~S'}} |
302 | // expected-note@-1 {{Returning; inner buffer was deallocated}} |
303 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
304 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
305 | } |
306 | |
307 | //=---------------------------=// |
308 | // Other STL functions // |
309 | //=---------------------------=// |
310 | |
311 | void STL_func_ref() { |
312 | const char *c; |
313 | std::string s; |
314 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
315 | std::func_ref(s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'func_ref'}} |
316 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
317 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
318 | } |
319 | |
320 | void STL_func_const_ref() { |
321 | const char *c; |
322 | std::string s; |
323 | c = s.c_str(); |
324 | std::func_const_ref(s); |
325 | consume(c); // no-warning |
326 | } |
327 | |
328 | void STL_func_value() { |
329 | const char *c; |
330 | std::string s; |
331 | c = s.c_str(); |
332 | std::func_value(s); |
333 | consume(c); // no-warning |
334 | } |
335 | |
336 | void func_ptr_known() { |
337 | const char *c; |
338 | std::string s; |
339 | void (*func_ptr)(std::string &) = std::func_ref<std::string>; |
340 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
341 | func_ptr(s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'func_ref'}} |
342 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
343 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
344 | } |
345 | |
346 | void func_ptr_unknown(void (*func_ptr)(std::string &)) { |
347 | const char *c; |
348 | std::string s; |
349 | c = s.c_str(); |
350 | func_ptr(s); |
351 | consume(c); // no-warning |
352 | } |
353 | |
354 | void func_default_arg() { |
355 | const char *c; |
356 | std::string s; |
357 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
358 | default_arg(3, s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'default_arg'}} |
359 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
360 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
361 | } |
362 | |
363 | struct T { |
364 | std::string to_string() { return s; } |
365 | private: |
366 | std::string s; |
367 | }; |
368 | |
369 | const char *escape_via_return_temp() { |
370 | T x; |
371 | return x.to_string().c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
372 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
373 | // expected-warning@-2 {{Inner pointer of container used after re/deallocation}} |
374 | // expected-note@-3 {{Inner pointer of container used after re/deallocation}} |
375 | } |
376 | |
377 | const char *escape_via_return_local() { |
378 | std::string s; |
379 | return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
380 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
381 | // expected-warning@-2 {{Inner pointer of container used after re/deallocation}} |
382 | // expected-note@-3 {{Inner pointer of container used after re/deallocation}} |
383 | } |
384 | |
385 | |
386 | char *c(); |
387 | class A {}; |
388 | |
389 | void no_CXXRecordDecl() { |
390 | A a, *b; |
391 | *(void **)&b = c() + 1; |
392 | *b = a; // no-crash |
393 | } |
394 | |
395 | void checkReference(std::string &s) { |
396 | const char *c = s.c_str(); |
397 | } |
398 | |