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