// 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 void func_ref(T &a); template void func_const_ref(const T &a); template 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; 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(); }