llvm-project/clang/test/Analysis/inner-pointer.cpp

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();
}