mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 12:46:08 +00:00

Summary: The following code snippet taken from D64271#1572188 has an issue: namely, because `flag`'s value isn't undef or a concrete int, it isn't being tracked. int flag; bool coin(); void foo() { flag = coin(); } void test() { int *x = 0; int local_flag; flag = 1; foo(); local_flag = flag; if (local_flag) x = new int; foo(); local_flag = flag; if (local_flag) *x = 5; } This, in my opinion, makes no sense, other values may be interesting too. Originally added by rC185608. Differential Revision: https://reviews.llvm.org/D64287 llvm-svn: 368773
444 lines
14 KiB
C++
444 lines
14 KiB
C++
// RUN: %clang_analyze_cc1 %s \
|
|
// RUN: -verify=expected,tracking \
|
|
// RUN: -analyzer-config track-conditions=true \
|
|
// RUN: -analyzer-output=text \
|
|
// RUN: -analyzer-checker=core
|
|
|
|
// RUN: not %clang_analyze_cc1 -verify %s \
|
|
// RUN: -analyzer-checker=core \
|
|
// RUN: -analyzer-config track-conditions-debug=true \
|
|
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-INVALID-DEBUG
|
|
|
|
// CHECK-INVALID-DEBUG: (frontend): invalid input for analyzer-config option
|
|
// CHECK-INVALID-DEBUG-SAME: 'track-conditions-debug', that expects
|
|
// CHECK-INVALID-DEBUG-SAME: 'track-conditions' to also be enabled
|
|
//
|
|
// RUN: %clang_analyze_cc1 %s \
|
|
// RUN: -verify=expected,tracking,debug \
|
|
// RUN: -analyzer-config track-conditions=true \
|
|
// RUN: -analyzer-config track-conditions-debug=true \
|
|
// RUN: -analyzer-output=text \
|
|
// RUN: -analyzer-checker=core
|
|
|
|
// RUN: %clang_analyze_cc1 %s -verify \
|
|
// RUN: -analyzer-output=text \
|
|
// RUN: -analyzer-checker=core
|
|
|
|
namespace example_1 {
|
|
int flag;
|
|
bool coin();
|
|
|
|
void foo() {
|
|
flag = coin(); // tracking-note{{Value assigned to 'flag'}}
|
|
}
|
|
|
|
void test() {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
flag = 1;
|
|
|
|
foo(); // TODO: Add nodes here about flag's value being invalidated.
|
|
if (flag) // expected-note {{Assuming 'flag' is 0}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
x = new int;
|
|
|
|
foo(); // tracking-note{{Calling 'foo'}}
|
|
// tracking-note@-1{{Returning from 'foo'}}
|
|
|
|
if (flag) // expected-note {{Assuming 'flag' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace example_1
|
|
|
|
namespace example_2 {
|
|
int flag;
|
|
bool coin();
|
|
|
|
void foo() {
|
|
flag = coin(); // tracking-note{{Value assigned to 'flag'}}
|
|
}
|
|
|
|
void test() {
|
|
int *x = 0;
|
|
flag = 1;
|
|
|
|
foo();
|
|
if (flag) // expected-note {{Assuming 'flag' is 0}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
x = new int;
|
|
|
|
x = 0; // expected-note{{Null pointer value stored to 'x'}}
|
|
|
|
foo(); // tracking-note{{Calling 'foo'}}
|
|
// tracking-note@-1{{Returning from 'foo'}}
|
|
|
|
if (flag) // expected-note {{Assuming 'flag' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace example_2
|
|
|
|
namespace global_variable_invalidation {
|
|
int flag;
|
|
bool coin();
|
|
|
|
void foo() {
|
|
// coin() could write bar, do it's invalidated.
|
|
flag = coin(); // tracking-note{{Value assigned to 'flag'}}
|
|
// tracking-note@-1{{Value assigned to 'bar'}}
|
|
}
|
|
|
|
int bar;
|
|
|
|
void test() {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
flag = 1;
|
|
|
|
foo(); // tracking-note{{Calling 'foo'}}
|
|
// tracking-note@-1{{Returning from 'foo'}}
|
|
|
|
if (bar) // expected-note {{Assuming 'bar' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'bar'}}
|
|
if (flag) // expected-note {{Assuming 'flag' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace global_variable_invalidation
|
|
|
|
namespace variable_declaration_in_condition {
|
|
bool coin();
|
|
|
|
bool foo() {
|
|
return coin();
|
|
}
|
|
|
|
int bar;
|
|
|
|
void test() {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
|
|
if (int flag = foo()) // tracking-note{{'flag' initialized here}}
|
|
// debug-note@-1{{Tracking condition 'flag'}}
|
|
// expected-note@-2{{Assuming 'flag' is not equal to 0}}
|
|
// expected-note@-3{{Taking true branch}}
|
|
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace variable_declaration_in_condition
|
|
|
|
namespace conversion_to_bool {
|
|
bool coin();
|
|
|
|
struct ConvertsToBool {
|
|
operator bool() const { return coin(); }
|
|
};
|
|
|
|
void test() {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
|
|
if (ConvertsToBool())
|
|
// debug-note@-1{{Tracking condition 'ConvertsToBool()'}}
|
|
// expected-note@-2{{Assuming the condition is true}}
|
|
// expected-note@-3{{Taking true branch}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
|
|
} // end of namespace variable_declaration_in_condition
|
|
|
|
namespace important_returning_pointer_loaded_from {
|
|
bool coin();
|
|
|
|
int *getIntPtr();
|
|
|
|
void storeValue(int **i) {
|
|
*i = getIntPtr(); // tracking-note{{Value assigned to 'i'}}
|
|
}
|
|
|
|
int *conjurePointer() {
|
|
int *i;
|
|
storeValue(&i); // tracking-note{{Calling 'storeValue'}}
|
|
// tracking-note@-1{{Returning from 'storeValue'}}
|
|
return i; // tracking-note{{Returning pointer (loaded from 'i')}}
|
|
}
|
|
|
|
void f(int *ptr) {
|
|
if (ptr) // expected-note{{Assuming 'ptr' is null}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
;
|
|
if (!conjurePointer())
|
|
// tracking-note@-1{{Calling 'conjurePointer'}}
|
|
// tracking-note@-2{{Returning from 'conjurePointer'}}
|
|
// debug-note@-3{{Tracking condition '!conjurePointer()'}}
|
|
// expected-note@-4{{Assuming the condition is true}}
|
|
// expected-note@-5{{Taking true branch}}
|
|
*ptr = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace important_returning_pointer_loaded_from
|
|
|
|
namespace unimportant_returning_pointer_loaded_from {
|
|
bool coin();
|
|
|
|
int *getIntPtr();
|
|
|
|
int *conjurePointer() {
|
|
int *i = getIntPtr(); // tracking-note{{'i' initialized here}}
|
|
return i; // tracking-note{{Returning pointer (loaded from 'i')}}
|
|
}
|
|
|
|
void f(int *ptr) {
|
|
if (ptr) // expected-note{{Assuming 'ptr' is null}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
;
|
|
if (!conjurePointer())
|
|
// tracking-note@-1{{Calling 'conjurePointer'}}
|
|
// tracking-note@-2{{Returning from 'conjurePointer'}}
|
|
// debug-note@-3{{Tracking condition '!conjurePointer()'}}
|
|
// expected-note@-4{{Assuming the condition is true}}
|
|
// expected-note@-5{{Taking true branch}}
|
|
*ptr = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace unimportant_returning_pointer_loaded_from
|
|
|
|
namespace unimportant_returning_pointer_loaded_from_through_cast {
|
|
|
|
void *conjure();
|
|
|
|
int *cast(void *P) {
|
|
return static_cast<int *>(P);
|
|
}
|
|
|
|
void f() {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
|
|
if (cast(conjure()))
|
|
// tracking-note@-1{{Passing value via 1st parameter 'P'}}
|
|
// debug-note@-2{{Tracking condition 'cast(conjure())'}}
|
|
// expected-note@-3{{Assuming the condition is false}}
|
|
// expected-note@-4{{Taking false branch}}
|
|
return;
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
|
|
} // end of namespace unimportant_returning_pointer_loaded_from_through_cast
|
|
|
|
namespace unimportant_returning_value_note {
|
|
bool coin();
|
|
|
|
bool flipCoin() { return coin(); }
|
|
|
|
void i(int *ptr) {
|
|
if (ptr) // expected-note{{Assuming 'ptr' is null}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
;
|
|
if (!flipCoin())
|
|
// debug-note@-1{{Tracking condition '!flipCoin()'}}
|
|
// expected-note@-2{{Assuming the condition is true}}
|
|
// expected-note@-3{{Taking true branch}}
|
|
*ptr = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace unimportant_returning_value_note
|
|
|
|
namespace important_returning_value_note {
|
|
bool coin();
|
|
|
|
bool flipCoin() {
|
|
if (coin()) // tracking-note{{Assuming the condition is false}}
|
|
// tracking-note@-1{{Taking false branch}}
|
|
// debug-note@-2{{Tracking condition 'coin()'}}
|
|
return true;
|
|
return coin(); // tracking-note{{Returning value}}
|
|
}
|
|
|
|
void i(int *ptr) {
|
|
if (ptr) // expected-note{{Assuming 'ptr' is null}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
;
|
|
if (!flipCoin())
|
|
// tracking-note@-1{{Calling 'flipCoin'}}
|
|
// tracking-note@-2{{Returning from 'flipCoin'}}
|
|
// debug-note@-3{{Tracking condition '!flipCoin()'}}
|
|
// expected-note@-4{{Assuming the condition is true}}
|
|
// expected-note@-5{{Taking true branch}}
|
|
*ptr = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace important_returning_value_note
|
|
|
|
namespace important_returning_value_note_in_linear_function {
|
|
bool coin();
|
|
|
|
struct super_complicated_template_hackery {
|
|
static constexpr bool value = false;
|
|
};
|
|
|
|
bool flipCoin() {
|
|
if (super_complicated_template_hackery::value)
|
|
// tracking-note@-1{{'value' is false}}
|
|
// tracking-note@-2{{Taking false branch}}
|
|
return true;
|
|
return coin(); // tracking-note{{Returning value}}
|
|
}
|
|
|
|
void i(int *ptr) {
|
|
if (ptr) // expected-note{{Assuming 'ptr' is null}}
|
|
// expected-note@-1{{Taking false branch}}
|
|
;
|
|
if (!flipCoin())
|
|
// tracking-note@-1{{Calling 'flipCoin'}}
|
|
// tracking-note@-2{{Returning from 'flipCoin'}}
|
|
// debug-note@-3{{Tracking condition '!flipCoin()'}}
|
|
// expected-note@-4{{Assuming the condition is true}}
|
|
// expected-note@-5{{Taking true branch}}
|
|
*ptr = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace important_returning_value_note_in_linear_function
|
|
|
|
namespace tracked_condition_is_only_initialized {
|
|
int getInt();
|
|
|
|
void f() {
|
|
int flag = getInt(); // tracking-note{{'flag' initialized here}}
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
if (flag) // expected-note{{Assuming 'flag' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace tracked_condition_is_only_initialized
|
|
|
|
namespace tracked_condition_written_in_same_stackframe {
|
|
int flag;
|
|
int getInt();
|
|
|
|
void f(int y) {
|
|
y = 1; // tracking-note{{The value 1 is assigned to 'y'}}
|
|
flag = y; // tracking-note{{The value 1 is assigned to 'flag'}}
|
|
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
if (flag) // expected-note{{'flag' is 1}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace tracked_condition_written_in_same_stackframe
|
|
|
|
namespace tracked_condition_written_in_nested_stackframe {
|
|
int flag;
|
|
int getInt();
|
|
|
|
void foo() {
|
|
int y;
|
|
y = 1; // tracking-note{{The value 1 is assigned to 'y'}}
|
|
flag = y; // tracking-note{{The value 1 is assigned to 'flag'}}
|
|
|
|
}
|
|
|
|
void f(int y) {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
|
|
foo(); // tracking-note{{Calling 'foo'}}
|
|
// tracking-note@-1{{Returning from 'foo'}}
|
|
|
|
if (flag) // expected-note{{'flag' is 1}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace tracked_condition_written_in_nested_stackframe
|
|
|
|
namespace condition_written_in_nested_stackframe_before_assignment {
|
|
int flag = 0;
|
|
int getInt();
|
|
|
|
void foo() {
|
|
flag = getInt(); // tracking-note{{Value assigned to 'flag'}}
|
|
}
|
|
|
|
void f() {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
int y = 0;
|
|
|
|
foo(); // tracking-note{{Calling 'foo'}}
|
|
// tracking-note@-1{{Returning from 'foo'}}
|
|
y = flag; // tracking-note{{Value assigned to 'y'}}
|
|
|
|
if (y) // expected-note{{Assuming 'y' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'y'}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
} // end of namespace condition_written_in_nested_stackframe_before_assignment
|
|
|
|
namespace collapse_point_not_in_condition {
|
|
|
|
[[noreturn]] void halt();
|
|
|
|
void assert(int b) {
|
|
if (!b) // tracking-note{{Assuming 'b' is not equal to 0}}
|
|
// tracking-note@-1{{Taking false branch}}
|
|
halt();
|
|
}
|
|
|
|
void f(int flag) {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
|
|
assert(flag); // tracking-note{{Calling 'assert'}}
|
|
// tracking-note@-1{{Returning from 'assert'}}
|
|
|
|
if (flag) // expected-note{{'flag' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
|
|
} // end of namespace collapse_point_not_in_condition
|
|
|
|
namespace unimportant_write_before_collapse_point {
|
|
|
|
[[noreturn]] void halt();
|
|
|
|
void assert(int b) {
|
|
if (!b) // tracking-note{{Assuming 'b' is not equal to 0}}
|
|
// tracking-note@-1{{Taking false branch}}
|
|
halt();
|
|
}
|
|
int getInt();
|
|
|
|
void f(int flag) {
|
|
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
|
|
|
|
flag = getInt(); // tracking-note{{Value assigned to 'flag'}}
|
|
assert(flag); // tracking-note{{Calling 'assert'}}
|
|
// tracking-note@-1{{Returning from 'assert'}}
|
|
|
|
if (flag) // expected-note{{'flag' is not equal to 0}}
|
|
// expected-note@-1{{Taking true branch}}
|
|
// debug-note@-2{{Tracking condition 'flag'}}
|
|
*x = 5; // expected-warning{{Dereference of null pointer}}
|
|
// expected-note@-1{{Dereference of null pointer}}
|
|
}
|
|
|
|
} // end of namespace unimportant_write_before_collapse_point
|