blob: 8572b6416f150f39f38323c5ac8c0ecb93cc19ae [file] [edit]
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.DanglingPtrDeref \
// RUN: -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s
void test_case_one() {
int *ptr = nullptr;
{
int num = 5; // expected-note {{'num' initialized to 5}}
ptr = # // expected-note {{Value assigned to 'ptr'}}
}
// expected-note@-1 {{'num' is destroyed here}}
*ptr = 6;
// expected-warning@-1 {{Use of 'num' after its lifetime ended}}
// expected-note@-2 {{Use of 'num' after its lifetime ended}}
}
void test_case_two() {
int *ptr_one = nullptr;
int *ptr_two = nullptr;
{
int n = 1; // expected-note {{'n' initialized to 1}}
int m = 2; // expected-note {{'m' initialized to 2}}
ptr_one = &n; // expected-note {{Value assigned to 'ptr_one'}}
ptr_two = &m; // expected-note {{Value assigned to 'ptr_two'}}
}
// expected-note@-1 {{'n' is destroyed here}}
// expected-note@-2 {{'m' is destroyed here}}
*ptr_one = 6;
*ptr_two = 7;
// expected-warning@-2 {{Use of 'n' after its lifetime ended}}
// expected-note@-3 {{Use of 'n' after its lifetime ended}}
// expected-warning@-3 {{Use of 'm' after its lifetime ended}}
// expected-note@-4 {{Use of 'm' after its lifetime ended}}
}
void escape(int *ptr);
void test_case_three() {
int num = 5;
int *ptr = #
{
*ptr = 6; // no-warning
}
}
void test_case_four() {
int *ptr = nullptr;
{
int num = 5; // expected-note {{'num' initialized to 5}}
ptr = #
}
// expected-note@-1 {{'num' is destroyed here}}
int i = *ptr;
// expected-warning@-1 {{Use of 'num' after its lifetime ended}}
// expected-note@-2 {{Use of 'num' after its lifetime ended}}
i += i;
}
void test_case_five() {
int *ptr = nullptr;
for(int i = 0; i < 10; ++i) {
ptr = &i;
}
escape(ptr); // no-warning
}
void test_case_six() {
for(int i = 0; i < 10; ++i) {
int *ptr = &i;
escape(ptr); // no-warning
}
}
void test_case_seven() {
int *ptr = nullptr;
// expected-note@+3 {{Loop condition is true. Entering loop body}}
// expected-note@+2 {{Assuming 'i' is >= 10}}
// expected-note@+1 {{Loop condition is false. Execution continues on line}}
for (int i = 0; i < 10; ++i) { // expected-note {{'i' initialized to 0}}
ptr = &i; // expected-note {{Value assigned to 'ptr'}}
escape(ptr);
}
// expected-note@-1 {{'i' is destroyed here}}
*ptr = 6;
// expected-warning@-1 {{Use of 'i' after its lifetime ended}}
// expected-note@-2 {{Use of 'i' after its lifetime ended}}
}
void passing_dangling_ptr_to_opaque_func() {
int *ptr = nullptr;
{
int num = 5; // expected-note {{'num' initialized to 5}}
ptr = &num; // expected-note {{Value assigned to 'ptr'}}
}
// expected-note@-1 {{'num' is destroyed here}}
escape(ptr);
// expected-warning@-1 {{Use of 'num' after its lifetime ended}}
// expected-note@-2 {{Use of 'num' after its lifetime ended}}
}
int deref_param(int *p) { return *p; }
// expected-warning@-1 {{Use of 'num' after its lifetime ended}}
// expected-note@-2 {{Use of 'num' after its lifetime ended}}
void inlined_callee_single_report() {
int *ptr = nullptr;
{
int num = 5; // expected-note {{'num' initialized to 5}}
ptr = &num;
}
// expected-note@-1 {{'num' is destroyed here}}
int r = deref_param(ptr);
// expected-note@-1 {{Calling 'deref_param'}}
(void)r;
}
struct MyBuffer {
char buffer[8];
};
struct MyStruct { int x; };
struct Inner { int x; };
struct Outer { Inner inner; };
struct A {
struct B { int x; };
B b;
};
char member_subregion_dangling_deref() {
const char *p = nullptr;
{
MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
p = tmp_buffer.buffer;
}
// expected-note@-1 {{'tmp_buffer.buffer[0]' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}}
// expected-note@-2 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}}
}
void opaque(const char *);
void opaque_pp(const char **);
void passing_dangling_to_call() {
const char *p = nullptr;
{
MyBuffer tmp_buffer = {};
p = tmp_buffer.buffer; // expected-note {{Value assigned to 'p'}}
}
// expected-note@-1 {{'tmp_buffer.buffer[0]' is destroyed here}}
opaque(p);
// expected-warning@-1 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}}
// expected-note@-2 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}}
}
char member_subregion_alive_deref() {
{
MyBuffer tmp_buffer = {};
const char *p = tmp_buffer.buffer;
opaque(p); // no-warning
return *p; // no-warning
}
}
char member_subregion_alive_deref_pp() {
const char *ptr = nullptr;
const char **pp = &ptr;
{
MyBuffer tmp_buffer = {};
ptr = tmp_buffer.buffer;
}
opaque_pp(pp);
return **pp; // no-warning
}
void arr_elem_subreg_dangling_deref() {
int *ptr = nullptr;
{
int local_arr[4]; // expected-note {{'local_arr' declared without an initial value}}
ptr = &local_arr[1]; // expected-note {{Value assigned to 'ptr'}}
}
// expected-note@-1 {{'local_arr[1]' is destroyed here}}
*ptr = 7;
// expected-warning@-1 {{Use of 'local_arr[1]' after its lifetime ended}}
// expected-note@-2 {{Use of 'local_arr[1]' after its lifetime ended}}
}
char member_array_elem_dangling_deref() {
const char *p = nullptr;
{
MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
p = tmp_buffer.buffer + 3;
}
// expected-note@-1 {{'tmp_buffer.buffer[3]' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 'tmp_buffer.buffer[3]' after its lifetime ended}}
// expected-note@-2 {{Use of 'tmp_buffer.buffer[3]' after its lifetime ended}}
}
char member_array_out_of_bounds_dangling_deref() {
const char *p = nullptr;
{
MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
p = tmp_buffer.buffer + 10;
}
// expected-note@-1 {{'tmp_buffer.buffer[10]' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 'tmp_buffer.buffer[10]' after its lifetime ended}}
// expected-note@-2 {{Use of 'tmp_buffer.buffer[10]' after its lifetime ended}}
}
int struct_field_dangling_deref() {
int *p = nullptr;
{
MyStruct s = {}; // expected-note {{'s.x' initialized to 0}}
p = &s.x;
}
// expected-note@-1 {{'s.x' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 's.x' after its lifetime ended}}
// expected-note@-2 {{Use of 's.x' after its lifetime ended}}
}
int struct_array_element_dangling_deref() {
int *p = nullptr;
{
MyStruct arr[4] = {}; // expected-note {{field 'x' initialized to 0}}
p = &arr[2].x;
}
// expected-note@-1 {{'arr[2].x' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 'arr[2].x' after its lifetime ended}}
// expected-note@-2 {{Use of 'arr[2].x' after its lifetime ended}}
}
int nested_field_dangling_deref() {
int *p = nullptr;
{
Outer o = {}; // expected-note {{'o.inner.x' initialized to 0}}
p = &o.inner.x;
}
// expected-note@-1 {{'o.inner.x' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 'o.inner.x' after its lifetime ended}}
// expected-note@-2 {{Use of 'o.inner.x' after its lifetime ended}}
}
int nested_type_field_dangling_deref() {
int *p = nullptr;
{
A a = {}; // expected-note {{'a.b.x' initialized to 0}}
p = &a.b.x;
}
// expected-note@-1 {{'a.b.x' is destroyed here}}
return *p;
// expected-warning@-1 {{Use of 'a.b.x' after its lifetime ended}}
// expected-note@-2 {{Use of 'a.b.x' after its lifetime ended}}
}
char member_subregion_dangling_deref_increment() {
const char *p = nullptr;
{
MyBuffer tmp_buffer = {}; // expected-note {{Initializing to 0}}
p = tmp_buffer.buffer;
}
// expected-note@-1 {{'tmp_buffer.buffer[1]' is destroyed here}}
p++;
return *p;
// expected-warning@-1 {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}}
// expected-note@-2 {{Use of 'tmp_buffer.buffer[1]' after its lifetime ended}}
}
void chain() {
int *ptr = nullptr;
{
int local = 5; // expected-note {{'local' initialized to 5}}
int *a = &local; // expected-note {{'a' initialized here}}
int *b = a; // expected-note {{'b' initialized to the value of 'a'}}
int *c = b; // expected-note {{'c' initialized to the value of 'b'}}
ptr = c; // expected-note {{The value of 'c' is assigned to 'ptr'}}
}
// expected-note@-1 {{'local' is destroyed here}}
*ptr = 6;
// expected-warning@-1 {{Use of 'local' after its lifetime ended}}
// expected-note@-2 {{Use of 'local' after its lifetime ended}}
}
void branch(bool cond) {
int *ptr = nullptr;
{
int x = 1; // expected-note {{'x' initialized to 1}}
int y = 2; // expected-note {{'y' initialized to 2}}
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}}
ptr = &x; // expected-note {{Value assigned to 'ptr'}}
} else {
ptr = &y; // expected-note {{Value assigned to 'ptr'}}
}
}
// expected-note@-1 {{'x' is destroyed here}}
// expected-note@-2 {{'y' is destroyed here}}
*ptr = 6;
// expected-warning@-1 {{Use of 'x' after its lifetime ended}}
// expected-note@-2 {{Use of 'x' after its lifetime ended}}
// expected-warning@-3 {{Use of 'y' after its lifetime ended}}
// expected-note@-4 {{Use of 'y' after its lifetime ended}}
}
int *func(int *p) { return p; }
int *call_func(int *p) { return func(p); }
// expected-note@-1 {{Passing value via 1st parameter 'p'}}
// expected-note@-2 {{Returning pointer}}
void dangling_through_calls() {
int *ptr = nullptr;
{
int local = 5; // expected-note {{'local' initialized to 5}}
ptr = call_func(&local);
// expected-note@-1 {{Passing value via 1st parameter 'p'}}
// expected-note@-2 {{Calling 'call_func'}}
// expected-note@-3 {{Returning from 'call_func'}}
// expected-note@-4 {{Value assigned to 'ptr'}}
}
// expected-note@-1 {{'local' is destroyed here}}
*ptr = 6;
// expected-warning@-1 {{Use of 'local' after its lifetime ended}}
// expected-note@-2 {{Use of 'local' after its lifetime ended}}
}
// If the same variable is dereferenced multiple times then only
// report for the first dereference.
void multiple_deref() {
int *ptr = nullptr;
{
int a = 5; // expected-note {{'a' initialized to 5}}
ptr = &a; // expected-note {{Value assigned to 'ptr'}}
} // expected-note {{'a' is destroyed here}}
*ptr = 6; // expected-note {{Use of 'a' after its lifetime ended}}
// expected-warning@-1 {{Use of 'a' after its lifetime ended}}
*ptr = 7; // no-warning: Already reported this base region.
}