forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			399 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
| // RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=cplusplus.InnerPointer \
 | |
| // RUN:   -Wno-dangling -Wno-dangling-field -Wno-return-stack-address \
 | |
| // RUN:   %s -analyzer-output=text -verify
 | |
| 
 | |
| #include "Inputs/system-header-simulator-cxx.h"
 | |
| namespace std {
 | |
| 
 | |
| template <typename T>
 | |
| void func_ref(T &a);
 | |
| 
 | |
| template <typename T>
 | |
| void func_const_ref(const T &a);
 | |
| 
 | |
| template <typename T>
 | |
| void func_value(T a);
 | |
| 
 | |
| string my_string = "default";
 | |
| void default_arg(int a = 42, string &b = my_string);
 | |
| 
 | |
| } // end namespace std
 | |
| 
 | |
| void consume(const char *) {}
 | |
| void consume(const wchar_t *) {}
 | |
| void consume(const char16_t *) {}
 | |
| void consume(const char32_t *) {}
 | |
| 
 | |
| //=--------------------------------------=//
 | |
| //     `std::string` member functions     //
 | |
| //=--------------------------------------=//
 | |
| 
 | |
| void deref_after_scope_char(bool cond) {
 | |
|   const char *c, *d;
 | |
|   {
 | |
|     std::string s;
 | |
|     c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|     d = s.data();  // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   }                // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|   // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|   std::string s;
 | |
|   const char *c2 = s.c_str();
 | |
|   if (cond) {
 | |
|     // expected-note@-1 {{Assuming 'cond' is true}}
 | |
|     // expected-note@-2 {{Taking true branch}}
 | |
|     // expected-note@-3 {{Assuming 'cond' is false}}
 | |
|     // expected-note@-4 {{Taking false branch}}
 | |
|     consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|     // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
|   } else {
 | |
|     consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|     // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
|   }
 | |
| }
 | |
| 
 | |
| void deref_after_scope_char_data_non_const() {
 | |
|   char *c;
 | |
|   {
 | |
|     std::string s;
 | |
|     c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   }               // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|   std::string s;
 | |
|   char *c2 = s.data();
 | |
|   consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_scope_wchar_t(bool cond) {
 | |
|   const wchar_t *c, *d;
 | |
|   {
 | |
|     std::wstring s;
 | |
|     c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}}
 | |
|     d = s.data();  // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}}
 | |
|   }                // expected-note {{Inner buffer of 'std::wstring' deallocated by call to destructor}}
 | |
|   // expected-note@-1 {{Inner buffer of 'std::wstring' deallocated by call to destructor}}
 | |
|   std::wstring s;
 | |
|   const wchar_t *c2 = s.c_str();
 | |
|   if (cond) {
 | |
|     // expected-note@-1 {{Assuming 'cond' is true}}
 | |
|     // expected-note@-2 {{Taking true branch}}
 | |
|     // expected-note@-3 {{Assuming 'cond' is false}}
 | |
|     // expected-note@-4 {{Taking false branch}}
 | |
|     consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|     // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
|   } else {
 | |
|     consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|     // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
|   }
 | |
| }
 | |
| 
 | |
| void deref_after_scope_char16_t_cstr() {
 | |
|   const char16_t *c16;
 | |
|   {
 | |
|     std::u16string s16;
 | |
|     c16 = s16.c_str(); // expected-note {{Pointer to inner buffer of 'std::u16string' obtained here}}
 | |
|   }                    // expected-note {{Inner buffer of 'std::u16string' deallocated by call to destructor}}
 | |
|   std::u16string s16;
 | |
|   const char16_t *c16_2 = s16.c_str();
 | |
|   consume(c16); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_scope_char32_t_data() {
 | |
|   const char32_t *c32;
 | |
|   {
 | |
|     std::u32string s32;
 | |
|     c32 = s32.data(); // expected-note {{Pointer to inner buffer of 'std::u32string' obtained here}}
 | |
|   }                   // expected-note {{Inner buffer of 'std::u32string' deallocated by call to destructor}}
 | |
|   std::u32string s32;
 | |
|   const char32_t *c32_2 = s32.data();
 | |
|   consume(c32); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void multiple_symbols(bool cond) {
 | |
|   const char *c1, *d1;
 | |
|   {
 | |
|     std::string s1;
 | |
|     c1 = s1.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|     d1 = s1.data();  // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|     const char *local = s1.c_str();
 | |
|     consume(local); // no-warning
 | |
|   }                 // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|   // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|   std::string s2;
 | |
|   const char *c2 = s2.c_str();
 | |
|   if (cond) {
 | |
|     // expected-note@-1 {{Assuming 'cond' is true}}
 | |
|     // expected-note@-2 {{Taking true branch}}
 | |
|     // expected-note@-3 {{Assuming 'cond' is false}}
 | |
|     // expected-note@-4 {{Taking false branch}}
 | |
|     consume(c1); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|     // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
|   } else {
 | |
|     consume(d1); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   }              // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_scope_ok(bool cond) {
 | |
|   const char *c, *d;
 | |
|   std::string s;
 | |
|   {
 | |
|     c = s.c_str();
 | |
|     d = s.data();
 | |
|   }
 | |
|   if (cond)
 | |
|     consume(c); // no-warning
 | |
|   else
 | |
|     consume(d); // no-warning
 | |
| }
 | |
| 
 | |
| void deref_after_equals() {
 | |
|   const char *c;
 | |
|   std::string s = "hello";
 | |
|   c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s = "world";   // expected-note {{Inner buffer of 'std::string' reallocated by call to 'operator='}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_plus_equals() {
 | |
|   const char *c;
 | |
|   std::string s = "hello";
 | |
|   c = s.data();  // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s += " world"; // expected-note {{Inner buffer of 'std::string' reallocated by call to 'operator+='}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_clear() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.clear();     // expected-note {{Inner buffer of 'std::string' reallocated by call to 'clear'}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_append() {
 | |
|   const char *c;
 | |
|   std::string s = "hello";
 | |
|   c = s.c_str();    // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.append(2, 'x'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'append'}}
 | |
|   consume(c);       // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_assign() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.data();     // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.assign(4, 'a'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'assign'}}
 | |
|   consume(c);       // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_erase() {
 | |
|   const char *c;
 | |
|   std::string s = "hello";
 | |
|   c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.erase(0, 2); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'erase'}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_insert() {
 | |
|   const char *c;
 | |
|   std::string s = "ello";
 | |
|   c = s.c_str();       // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.insert(0, 1, 'h'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'insert'}}
 | |
|   consume(c);          // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_replace() {
 | |
|   const char *c;
 | |
|   std::string s = "hello world";
 | |
|   c = s.c_str();             // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.replace(6, 5, "string"); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'replace'}}
 | |
|   consume(c);                // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_pop_back() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.pop_back();  // expected-note {{Inner buffer of 'std::string' reallocated by call to 'pop_back'}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_push_back() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.data();     // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.push_back('c'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'push_back'}}
 | |
|   consume(c);       // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_reserve() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.reserve(5);  // expected-note {{Inner buffer of 'std::string' reallocated by call to 'reserve'}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_resize() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.resize(5);  // expected-note {{Inner buffer of 'std::string' reallocated by call to 'resize'}}
 | |
|   consume(c);   // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_shrink_to_fit() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.data();      // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s.shrink_to_fit(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'shrink_to_fit'}}
 | |
|   consume(c);        // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void deref_after_swap() {
 | |
|   const char *c;
 | |
|   std::string s1, s2;
 | |
|   c = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   s1.swap(s2);   // expected-note {{Inner buffer of 'std::string' reallocated by call to 'swap'}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| struct S {
 | |
|   std::string s;
 | |
|   const char *name() {
 | |
|     return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|                       // expected-note@-1 {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   }
 | |
|   void clear() {
 | |
|     s.clear(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'clear'}}
 | |
|   }
 | |
|   ~S() {} // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
| };
 | |
| 
 | |
| void cleared_through_method() {
 | |
|   S x;
 | |
|   const char *c = x.name(); // expected-note {{Calling 'S::name'}}
 | |
|                             // expected-note@-1 {{Returning from 'S::name'}}
 | |
|   x.clear(); // expected-note {{Calling 'S::clear'}}
 | |
|              // expected-note@-1 {{Returning; inner buffer was reallocated}}
 | |
|   consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void destroyed_through_method() {
 | |
|   S y;
 | |
|   const char *c = y.name(); // expected-note {{Calling 'S::name'}}
 | |
|                             // expected-note@-1 {{Returning from 'S::name'}}
 | |
|   y.~S(); // expected-note {{Calling '~S'}}
 | |
|           // expected-note@-1 {{Returning; inner buffer was deallocated}}
 | |
|   consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| //=---------------------------=//
 | |
| //     Other STL functions     //
 | |
| //=---------------------------=//
 | |
| 
 | |
| void STL_func_ref() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str();    // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   std::func_ref(s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'func_ref'}}
 | |
|   consume(c);       // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void STL_func_const_ref() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str();
 | |
|   std::func_const_ref(s);
 | |
|   consume(c); // no-warning
 | |
| }
 | |
| 
 | |
| void STL_func_value() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str();
 | |
|   std::func_value(s);
 | |
|   consume(c); // no-warning
 | |
| }
 | |
| 
 | |
| void func_ptr_known() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   void (*func_ptr)(std::string &) = std::func_ref<std::string>;
 | |
|   c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   func_ptr(s);   // expected-note {{Inner buffer of 'std::string' reallocated by call to 'func_ref'}}
 | |
|   consume(c);    // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| void func_ptr_unknown(void (*func_ptr)(std::string &)) {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str();
 | |
|   func_ptr(s);
 | |
|   consume(c); // no-warning
 | |
| }
 | |
| 
 | |
| void func_default_arg() {
 | |
|   const char *c;
 | |
|   std::string s;
 | |
|   c = s.c_str();     // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   default_arg(3, s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'default_arg'}}
 | |
|   consume(c);        // expected-warning {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-1 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| struct T {
 | |
|   std::string to_string() { return s; }
 | |
| private:
 | |
|   std::string s;
 | |
| };
 | |
| 
 | |
| const char *escape_via_return_temp() {
 | |
|   T x;
 | |
|   return x.to_string().c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|   // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|   // expected-warning@-2 {{Inner pointer of container used after re/deallocation}}
 | |
|   // expected-note@-3 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| const char *escape_via_return_local() {
 | |
|   std::string s;
 | |
|   return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}}
 | |
|                     // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}}
 | |
|                     // expected-warning@-2 {{Inner pointer of container used after re/deallocation}}
 | |
|                     // expected-note@-3 {{Inner pointer of container used after re/deallocation}}
 | |
| }
 | |
| 
 | |
| 
 | |
| char *c();
 | |
| class A {};
 | |
| 
 | |
| void no_CXXRecordDecl() {
 | |
|   A a, *b;
 | |
|   *(void **)&b = c() + 1;
 | |
|   *b = a; // no-crash
 | |
| }
 | |
| 
 | |
| void checkReference(std::string &s) {
 | |
|   const char *c = s.c_str();
 | |
| }
 |