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