blob: eb48d9e674d9a31219b369d18af19c2c36815ee6 [file] [log] [blame] [edit]
// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s
struct View;
struct [[gsl::Owner]] MyObj {
int id;
~MyObj() {} // Non-trivial destructor
MyObj operator+(MyObj);
View getView() const [[clang::lifetimebound]];
};
struct [[gsl::Pointer()]] View {
View(const MyObj&); // Borrows from MyObj
View();
void use() const;
};
class TriviallyDestructedClass {
View a, b;
};
//===----------------------------------------------------------------------===//
// Basic Definite Use-After-Free (-W...permissive)
// These are cases where the pointer is guaranteed to be dangling at the use site.
//===----------------------------------------------------------------------===//
void definite_simple_case() {
MyObj* p;
{
MyObj s;
p = &s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
void definite_simple_case_gsl() {
View v;
{
MyObj s;
v = s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void no_use_no_error() {
MyObj* p;
{
MyObj s;
p = &s;
}
// 'p' is dangling here, but since it is never used, no warning is issued.
}
void no_use_no_error_gsl() {
View v;
{
MyObj s;
v = s;
}
// 'v' is dangling here, but since it is never used, no warning is issued.
}
void definite_pointer_chain() {
MyObj* p;
MyObj* q;
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
q = p;
} // expected-note {{destroyed here}}
(void)*q; // expected-note {{later used here}}
}
void definite_propagation_gsl() {
View v1, v2;
{
MyObj s;
v1 = s; // expected-warning {{object whose reference is captured does not live long enough}}
v2 = v1;
} // expected-note {{destroyed here}}
v2.use(); // expected-note {{later used here}}
}
void definite_multiple_uses_one_warning() {
MyObj* p;
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
// No second warning for the same loan.
p->id = 1;
MyObj* q = p;
(void)*q;
}
void definite_multiple_pointers() {
MyObj *p, *q, *r;
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
q = &s; // expected-warning {{does not live long enough}}
r = &s; // expected-warning {{does not live long enough}}
} // expected-note 3 {{destroyed here}}
(void)*p; // expected-note {{later used here}}
(void)*q; // expected-note {{later used here}}
(void)*r; // expected-note {{later used here}}
}
void definite_single_pointer_multiple_loans(bool cond) {
MyObj *p;
if (cond){
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
else {
MyObj t;
p = &t; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
}
void definite_single_pointer_multiple_loans_gsl(bool cond) {
View v;
if (cond){
MyObj s;
v = s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
else {
MyObj t;
v = t; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
}
void definite_if_branch(bool cond) {
MyObj safe;
MyObj* p = &safe;
if (cond) {
MyObj temp;
p = &temp; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
void potential_if_branch(bool cond) {
MyObj safe;
MyObj* p = &safe;
if (cond) {
MyObj temp;
p = &temp; // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
if (!cond)
(void)*p; // expected-note {{later used here}}
else
p = &safe;
}
void definite_if_branch_gsl(bool cond) {
MyObj safe;
View v = safe;
if (cond) {
MyObj temp;
v = temp; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void definite_potential_together(bool cond) {
MyObj safe;
MyObj* p_maybe = &safe;
MyObj* p_definite = nullptr;
{
MyObj s;
if (cond)
p_definite = &s; // expected-warning {{does not live long enough}}
if (cond)
p_maybe = &s; // expected-warning {{may not live long enough}}
} // expected-note 2 {{destroyed here}}
(void)*p_definite; // expected-note {{later used here}}
if (!cond)
(void)*p_maybe; // expected-note {{later used here}}
}
void definite_overrides_potential(bool cond) {
MyObj safe;
MyObj* p;
MyObj* q;
{
MyObj s;
q = &s; // expected-warning {{does not live long enough}}
p = q;
} // expected-note {{destroyed here}}
if (cond) {
// 'q' is conditionally "rescued". 'p' is not.
q = &safe;
}
// The use of 'p' is a definite error because it was never rescued.
(void)*q;
(void)*p; // expected-note {{later used here}}
(void)*q;
}
void potential_due_to_conditional_killing(bool cond) {
MyObj safe;
MyObj* q;
{
MyObj s;
q = &s; // expected-warning {{may not live long enough}}
} // expected-note {{destroyed here}}
if (cond) {
// 'q' is conditionally "rescued". 'p' is not.
q = &safe;
}
(void)*q; // expected-note {{later used here}}
}
void potential_for_loop_use_after_loop_body(MyObj safe) {
MyObj* p = &safe;
for (int i = 0; i < 1; ++i) {
MyObj s;
p = &s; // expected-warning {{may not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
void safe_for_loop_gsl() {
MyObj safe;
View v = safe;
for (int i = 0; i < 1; ++i) {
MyObj s;
v = s;
v.use();
}
}
void potential_for_loop_gsl() {
MyObj safe;
View v = safe;
for (int i = 0; i < 1; ++i) {
MyObj s;
v = s; // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void potential_for_loop_use_before_loop_body(MyObj safe) {
MyObj* p = &safe;
// Prefer the earlier use for diagnsotics.
for (int i = 0; i < 1; ++i) {
(void)*p; // expected-note {{later used here}}
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p;
}
void definite_loop_with_break(bool cond) {
MyObj safe;
MyObj* p = &safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj temp;
p = &temp; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note {{later used here}}
}
void definite_loop_with_break_gsl(bool cond) {
MyObj safe;
View v = safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj temp;
v = temp; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
v.use(); // expected-note {{later used here}}
}
void potential_multiple_expiry_of_same_loan(bool cond) {
// Choose the last expiry location for the loan (e.g., through scope-ends and break statements).
MyObj safe;
MyObj* p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond) {
p = &unsafe; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond) {
p = &unsafe; // expected-warning {{does not live long enough}}
if (cond)
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj unsafe2;
p = &unsafe2; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
// TODO: This can be argued to be a "maybe" warning. This is because
// we only check for confidence of liveness and not the confidence of
// the loan contained in an origin. To deal with this, we can introduce
// a confidence in loan propagation analysis as well like liveness.
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond)
p = &unsafe; // expected-warning {{does not live long enough}}
if (cond)
break; // expected-note {{destroyed here}}
}
(void)*p; // expected-note {{later used here}}
}
void potential_switch(int mode) {
MyObj safe;
MyObj* p = &safe;
switch (mode) {
case 1: {
MyObj temp;
p = &temp; // expected-warning {{object whose reference is captured may not live long enough}}
break; // expected-note {{destroyed here}}
}
case 2: {
p = &safe; // This path is okay.
break;
}
}
if (mode == 2)
(void)*p; // expected-note {{later used here}}
}
void definite_switch(int mode) {
MyObj safe;
MyObj* p = &safe;
// A use domintates all the loan expires --> all definite error.
switch (mode) {
case 1: {
MyObj temp1;
p = &temp1; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
case 2: {
MyObj temp2;
p = &temp2; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
default: {
MyObj temp2;
p = &temp2; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note 3 {{later used here}}
}
void definite_switch_gsl(int mode) {
View v;
switch (mode) {
case 1: {
MyObj temp1;
v = temp1; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
case 2: {
MyObj temp2;
v = temp2; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
default: {
MyObj temp3;
v = temp3; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
v.use(); // expected-note 3 {{later used here}}
}
void loan_from_previous_iteration(MyObj safe, bool condition) {
MyObj* p = &safe;
MyObj* q = &safe;
while (condition) {
MyObj x;
p = &x; // expected-warning {{may not live long enough}}
if (condition)
q = p;
(void)*p;
(void)*q; // expected-note {{later used here}}
} // expected-note {{destroyed here}}
}
void trivial_int_uaf() {
int * a;
{
int b = 1;
a = &b; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*a; // expected-note {{later used here}}
}
void trivial_class_uaf() {
TriviallyDestructedClass* ptr;
{
TriviallyDestructedClass s;
ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)ptr; // expected-note {{later used here}}
}
void small_scope_reference_var_no_error() {
MyObj safe;
View view;
{
const MyObj& ref = safe;
view = ref;
}
view.use();
}
//===----------------------------------------------------------------------===//
// Basic Definite Use-After-Return (Return-Stack-Address) (-W...permissive)
// These are cases where the pointer is guaranteed to be dangling at the use site.
//===----------------------------------------------------------------------===//
MyObj* simple_return_stack_address() {
MyObj s;
MyObj* p = &s; // expected-warning {{address of stack memory is returned later}}
return p; // expected-note {{returned here}}
}
MyObj* direct_return() {
MyObj s;
return &s; // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
const MyObj& return_reference_to_param_no_error(const MyObj& in) {
return in;
}
const MyObj& return_reference_to_param_via_ref_no_error(const MyObj& in) {
const MyObj& ref = in;
return ref;
}
const MyObj* getPointer();
const MyObj& return_reference_to_param_via_pointer_no_error() {
const MyObj& ref = *getPointer();
return ref;
}
const MyObj* conditional_assign_unconditional_return(const MyObj& safe, bool c) {
MyObj s;
const MyObj* p = &safe;
if (c) {
p = &s; // expected-warning {{address of stack memory is returned later}}
}
return p; // expected-note {{returned here}}
}
View conditional_assign_both_branches(const MyObj& safe, bool c) {
MyObj s;
View p;
if (c) {
p = s; // expected-warning {{address of stack memory is returned later}}
}
else {
p = safe;
}
return p; // expected-note {{returned here}}
}
View reassign_safe_to_local(const MyObj& safe) {
MyObj local;
View p = safe;
p = local; // expected-warning {{address of stack memory is returned later}}
return p; // expected-note {{returned here}}
}
View pointer_chain_to_local() {
MyObj local;
View p1 = local; // expected-warning {{address of stack memory is returned later}}
View p2 = p1;
return p2; // expected-note {{returned here}}
}
View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) {
MyObj local1;
MyObj local2;
View p;
if (c1) {
p = local1; // expected-warning {{address of stack memory is returned later}}
return p; // expected-note {{returned here}}
}
else if (c2) {
p = local2; // expected-warning {{address of stack memory is returned later}}
return p; // expected-note {{returned here}}
}
p = safe;
return p;
}
View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) {
MyObj local1;
MyObj local2;
View p;
if (c1) {
p = local1; // expected-warning {{address of stack memory is returned later}}
}
else if (c2) {
p = local2; // expected-warning {{address of stack memory is returned later}}
}
else {
p = safe;
}
return p; // expected-note 2 {{returned here}}
}
View direct_return_of_local() {
MyObj stack;
return stack; // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
MyObj& reference_return_of_local() {
MyObj stack;
return stack; // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
int* trivial_int_uar() {
int *a;
int b = 1;
a = &b; // expected-warning {{address of stack memory is returned later}}
return a; // expected-note {{returned here}}
}
TriviallyDestructedClass* trivial_class_uar () {
TriviallyDestructedClass *ptr;
TriviallyDestructedClass s;
ptr = &s; // expected-warning {{address of stack memory is returned later}}
return ptr; // expected-note {{returned here}}
}
const int& return_parameter(int a) {
return a; // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
int* return_pointer_to_parameter(int a) {
return &a; // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
const int& return_reference_to_parameter(int a) {
const int &b = a; // expected-warning {{address of stack memory is returned later}}
return b; // expected-note {{returned here}}
}
int return_reference_to_parameter_no_error(int a) {
const int &b = a;
return b;
}
const int& reference_via_conditional(int a, int b, bool cond) {
const int &c = (cond ? ((a)) : (b)); // expected-warning 2 {{address of stack memory is returned later}}
return c; // expected-note 2 {{returned here}}
}
const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) {
const int &c = cond ? a : b; // expected-warning 2 {{address of stack memory is returned later}}
const int* d = &c;
return d; // expected-note 2 {{returned here}}
}
// FIXME: Dereference of a pointer does not track the reference.
const int& return_pointer_to_parameter_via_reference_1(int a) {
const int* d = &a;
return *d;
}
const int& get_ref_to_local() {
int a = 42;
return a; // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
void test_view_pointer() {
View* vp;
{
View v;
vp = &v; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
vp->use(); // expected-note {{later used here}}
}
void test_view_double_pointer() {
View** vpp;
{
View* vp = nullptr;
vpp = &vp; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(**vpp).use(); // expected-note {{later used here}}
}
struct PtrHolder {
int* ptr;
int* const& getRef() const [[clang::lifetimebound]] { return ptr; }
};
int* const& test_ref_to_ptr() {
PtrHolder a;
int *const &ref = a.getRef(); // expected-warning {{address of stack memory is returned later}}
return ref; // expected-note {{returned here}}
}
int* const test_ref_to_ptr_no_error() {
PtrHolder a;
int *const &ref = a.getRef();
return ref;
}
int** return_inner_ptr_addr(int*** ppp [[clang::lifetimebound]]);
void test_lifetimebound_multi_level() {
int** result;
{
int* p = nullptr;
int** pp = &p;
int*** ppp = &pp; // expected-warning {{object whose reference is captured does not live long enough}}
result = return_inner_ptr_addr(ppp);
} // expected-note {{destroyed here}}
(void)**result; // expected-note {{used here}}
}
// FIXME: Assignment does not track the dereference of a pointer.
void test_assign_through_double_ptr() {
int a = 1, b = 2;
int* p = &a;
int** pp = &p;
{
int c = 3;
*pp = &c;
}
(void)**pp;
}
int** test_ternary_double_ptr(bool cond) {
int a = 1, b = 2;
int* pa = &a; // expected-warning {{address of stack memory is returned later}}
int* pb = &b; // expected-warning {{address of stack memory is returned later}}
int** result = cond ? &pa : &pb; // expected-warning 2 {{address of stack memory is returned later}}
return result; // expected-note 4 {{returned here}}
}
//===----------------------------------------------------------------------===//
// Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined
// These are cases where the diagnostic kind is determined by location
//===----------------------------------------------------------------------===//
MyObj* uaf_before_uar() {
MyObj* p;
{
MyObj local_obj;
p = &local_obj; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
return p; // expected-note {{later used here}}
}
View uar_before_uaf(const MyObj& safe, bool c) {
View p;
{
MyObj local_obj;
p = local_obj; // expected-warning {{address of stack memory is returned later}}
if (c) {
return p; // expected-note {{returned here}}
}
}
p.use();
p = safe;
return p;
}
//===----------------------------------------------------------------------===//
// No-Error Cases
//===----------------------------------------------------------------------===//
void no_error_if_dangle_then_rescue() {
MyObj safe;
MyObj* p;
{
MyObj temp;
p = &temp; // p is temporarily dangling.
}
p = &safe; // p is "rescued" before use.
(void)*p; // This is safe.
}
void no_error_if_dangle_then_rescue_gsl() {
MyObj safe;
View v;
{
MyObj temp;
v = temp; // 'v' is temporarily dangling.
}
v = safe; // 'v' is "rescued" before use by reassigning to a valid object.
v.use(); // This is safe.
}
void no_error_loan_from_current_iteration(bool cond) {
// See https://github.com/llvm/llvm-project/issues/156959.
MyObj b;
while (cond) {
MyObj a;
View p = b;
if (cond) {
p = a;
}
(void)p;
}
}
View safe_return(const MyObj& safe) {
MyObj local;
View p = local;
p = safe; // p has been reassigned
return p; // This is safe
}
//===----------------------------------------------------------------------===//
// Lifetimebound Attribute Tests
//===----------------------------------------------------------------------===//
View Identity(View v [[clang::lifetimebound]]);
const MyObj& IdentityRef(const MyObj& obj [[clang::lifetimebound]]);
MyObj* Identity(MyObj* v [[clang::lifetimebound]]);
View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]);
MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]);
void lifetimebound_simple_function() {
View v;
{
MyObj obj;
v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_multiple_args_definite() {
View v;
{
MyObj obj1, obj2;
v = Choose(true,
obj1, // expected-warning {{object whose reference is captured does not live long enough}}
obj2); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note 2 {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
}
void lifetimebound_multiple_args_potential(bool cond) {
MyObj safe;
View v = safe;
{
MyObj obj1;
if (cond) {
MyObj obj2;
v = Choose(true,
obj1, // expected-warning {{object whose reference is captured does not live long enough}}
obj2); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
} // expected-note {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
}
View SelectFirst(View a [[clang::lifetimebound]], View b);
void lifetimebound_mixed_args() {
View v;
{
MyObj obj1, obj2;
v = SelectFirst(obj1, // expected-warning {{object whose reference is captured does not live long enough}}
obj2);
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
struct LifetimeBoundMember {
LifetimeBoundMember();
View get() const [[clang::lifetimebound]];
operator View() const [[clang::lifetimebound]];
};
void lifetimebound_member_function() {
View v;
{
MyObj obj;
v = obj.getView(); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
struct LifetimeBoundConversionView {
LifetimeBoundConversionView();
~LifetimeBoundConversionView();
operator View() const [[clang::lifetimebound]];
};
void lifetimebound_conversion_operator() {
View v;
{
LifetimeBoundConversionView obj;
v = obj; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_chained_calls() {
View v;
{
MyObj obj;
v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_with_pointers() {
MyObj* ptr;
{
MyObj obj;
ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
void lifetimebound_no_error_safe_usage() {
MyObj obj;
View v1 = Identity(obj); // No warning - obj lives long enough
View v2 = Choose(true, v1, Identity(obj)); // No warning - all args are safe
v2.use(); // Safe usage
}
void lifetimebound_partial_safety(bool cond) {
MyObj safe_obj;
View v = safe_obj;
if (cond) {
MyObj temp_obj;
v = Choose(true,
safe_obj,
temp_obj); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
const MyObj& GetObject(View v [[clang::lifetimebound]]);
void lifetimebound_return_reference() {
View v;
const MyObj* ptr;
{
MyObj obj;
View temp_v = obj; // expected-warning {{object whose reference is captured does not live long enough}}
const MyObj& ref = GetObject(temp_v);
ptr = &ref;
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
// FIXME: No warning for non gsl::Pointer types. Origin tracking is only supported for pointer types.
struct LifetimeBoundCtor {
LifetimeBoundCtor();
LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]);
};
void lifetimebound_ctor() {
LifetimeBoundCtor v;
{
MyObj obj;
v = obj;
}
(void)v;
}
View lifetimebound_return_of_local() {
MyObj stack;
return Identity(stack); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
const MyObj& lifetimebound_return_ref_to_local() {
MyObj stack;
return IdentityRef(stack); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
View lifetimebound_return_by_value_param(MyObj stack_param) {
return Identity(stack_param); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
View lifetimebound_return_by_value_multiple_param(int cond, MyObj a, MyObj b, MyObj c) {
if (cond == 1)
return Identity(a); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
if (cond == 2)
return Identity(b); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
return Identity(c); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
template<class T>
View lifetimebound_return_by_value_param_template(T t) {
return Identity(t); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
void use_lifetimebound_return_by_value_param_template() {
lifetimebound_return_by_value_param_template(MyObj{}); // expected-note {{in instantiation of}}
}
void lambda_uar_param() {
auto lambda = [](MyObj stack_param) {
return Identity(stack_param); // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
};
lambda(MyObj{});
}
// FIXME: This should be detected. We see correct destructors but origin flow breaks somewhere.
namespace VariadicTemplatedParamsUAR {
template<typename... Args>
View Max(Args... args [[clang::lifetimebound]]);
template<typename... Args>
View lifetimebound_return_of_variadic_param(Args... args) {
return Max(args...);
}
void test_variadic() {
lifetimebound_return_of_variadic_param(MyObj{1}, MyObj{2}, MyObj{3});
}
} // namespace VariadicTemplatedParamsUAR
// FIXME: Fails to diagnose UAF when a reference to a by-value param escapes via an out-param.
void uaf_from_by_value_param_failing(MyObj param, View* out_p) {
*out_p = Identity(param);
}
// Conditional operator.
void conditional_operator_one_unsafe_branch(bool cond) {
MyObj safe;
MyObj* p = &safe;
{
MyObj temp;
p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}}
: &safe;
} // expected-note {{destroyed here}}
// This is not a use-after-free for any value of `cond` but the analysis
// cannot reason this and marks the above as a false positive. This
// ensures safety regardless of cond's value.
if (cond)
p = &safe;
(void)*p; // expected-note {{later used here}}
}
void conditional_operator_two_unsafe_branches(bool cond) {
MyObj* p;
{
MyObj a, b;
p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
: &b; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note 2 {{destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
}
void conditional_operator_nested(bool cond) {
MyObj* p;
{
MyObj a, b, c, d;
p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}.
: &b // expected-warning {{object whose reference is captured does not live long enough}}.
: cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}.
: &d; // expected-warning {{object whose reference is captured does not live long enough}}.
} // expected-note 4 {{destroyed here}}
(void)*p; // expected-note 4 {{later used here}}
}
void conditional_operator_lifetimebound(bool cond) {
MyObj* p;
{
MyObj a, b;
p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
: &b); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note 2 {{destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
}
void conditional_operator_lifetimebound_nested(bool cond) {
MyObj* p;
{
MyObj a, b;
p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}}
: Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note 2 {{destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
}
void conditional_operator_lifetimebound_nested_deep(bool cond) {
MyObj* p;
{
MyObj a, b, c, d;
p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
: &b) // expected-warning {{object whose reference is captured does not live long enough}}
: Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}
: &d)); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note 4 {{destroyed here}}
(void)*p; // expected-note 4 {{later used here}}
}
void parentheses(bool cond) {
MyObj* p;
{
MyObj a;
p = &((((a)))); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
{
MyObj a;
p = ((GetPointer((a)))); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
{
MyObj a, b, c, d;
p = &(cond ? (cond ? a // expected-warning {{object whose reference is captured does not live long enough}}.
: b) // expected-warning {{object whose reference is captured does not live long enough}}.
: (cond ? c // expected-warning {{object whose reference is captured does not live long enough}}.
: d)); // expected-warning {{object whose reference is captured does not live long enough}}.
} // expected-note 4 {{destroyed here}}
(void)*p; // expected-note 4 {{later used here}}
{
MyObj a, b, c, d;
p = ((cond ? (((cond ? &a : &b))) // expected-warning 2 {{object whose reference is captured does not live long enough}}.
: &(((cond ? c : d))))); // expected-warning 2 {{object whose reference is captured does not live long enough}}.
} // expected-note 4 {{destroyed here}}
(void)*p; // expected-note 4 {{later used here}}
}
namespace GH162834 {
// https://github.com/llvm/llvm-project/issues/162834
template <class T>
struct StatusOr {
~StatusOr() {}
const T& value() const& [[clang::lifetimebound]] { return data; }
private:
T data;
};
StatusOr<View> getViewOr();
StatusOr<MyObj> getStringOr();
StatusOr<MyObj*> getPointerOr();
void foo() {
View view;
{
StatusOr<View> view_or = getViewOr();
view = view_or.value();
}
(void)view;
}
void bar() {
MyObj* pointer;
{
StatusOr<MyObj*> pointer_or = getPointerOr();
pointer = pointer_or.value();
}
(void)*pointer;
}
void foobar() {
View view;
{
StatusOr<MyObj> string_or = getStringOr();
view = string_or. // expected-warning {{object whose reference is captured does not live long enough}}
value();
} // expected-note {{destroyed here}}
(void)view; // expected-note {{later used here}}
}
} // namespace GH162834
namespace RangeBasedForLoop {
struct MyObjStorage {
MyObj objs[1];
MyObjStorage() {}
~MyObjStorage() {}
const MyObj *begin() const [[clang::lifetimebound]] { return objs; }
const MyObj *end() const { return objs + 1; }
};
// FIXME: Detect use-after-scope. Dereference pointer does not propagate the origins.
void range_based_for_use_after_scope() {
View v;
{
MyObjStorage s;
for (const MyObj &o : s) {
v = o;
}
}
v.use();
}
// FIXME: Detect use-after-return. Dereference pointer does not propagate the origins.
View range_based_for_use_after_return() {
MyObjStorage s;
for (const MyObj &o : s) {
return o;
}
return *s.begin();
}
void range_based_for_not_reference() {
View v;
{
MyObjStorage s;
for (MyObj o : s) { // expected-note {{destroyed here}}
v = o; // expected-warning {{object whose reference is captured may not live long enough}}
}
}
v.use(); // expected-note {{later used here}}
}
void range_based_for_no_error() {
View v;
MyObjStorage s;
for (const MyObj &o : s) {
v = o;
}
v.use();
}
} // namespace RangeBaseForLoop
namespace structured_binding {
struct Pair {
MyObj a;
MyObj b;
Pair() {}
~Pair() {}
};
// FIXME: Detect this.
void structured_binding_use_after_scope() {
View v;
{
Pair p;
auto &[a_ref, b_ref] = p;
v = a_ref;
}
v.use();
}
}
namespace MaxFnLifetimeBound {
template<class T>
T&& MaxT(T&& a [[clang::lifetimebound]], T&& b [[clang::lifetimebound]]);
const MyObj& call_max_with_obj() {
MyObj oa, ob;
return MaxT(oa, // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 2 {{returned here}}
ob); // expected-warning {{address of stack memory is returned later}}
}
MyObj* call_max_with_obj_error() {
MyObj oa, ob;
return &MaxT(oa, // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 2 {{returned here}}
ob); // expected-warning {{address of stack memory is returned later}}
}
const MyObj* call_max_with_ref_obj_error() {
MyObj oa, ob;
const MyObj& refa = oa; // expected-warning {{address of stack memory is returned later}}
const MyObj& refb = ob; // expected-warning {{address of stack memory is returned later}}
return &MaxT(refa, refb); // expected-note 2 {{returned here}}
}
const MyObj& call_max_with_ref_obj_return_ref_error() {
MyObj oa, ob;
const MyObj& refa = oa; // expected-warning {{address of stack memory is returned later}}
const MyObj& refb = ob; // expected-warning {{address of stack memory is returned later}}
return MaxT(refa, refb); // expected-note 2 {{returned here}}
}
MyObj call_max_with_obj_no_error() {
MyObj oa, ob;
return MaxT(oa, ob);
}
const MyObj& call_max_with_ref_obj_no_error(const MyObj& a, const MyObj& b) {
return MaxT(a, b);
}
const View& call_max_with_view_with_error() {
View va, vb;
return MaxT(va, // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 2 {{returned here}}
vb); // expected-warning {{address of stack memory is returned later}}
}
struct [[gsl::Pointer]] NonTrivialPointer { ~NonTrivialPointer(); };
const NonTrivialPointer& call_max_with_non_trivial_view_with_error() {
NonTrivialPointer va, vb;
return MaxT(va, // expected-warning {{address of stack memory is returned later}}
// expected-note@-1 2 {{returned here}}
vb); // expected-warning {{address of stack memory is returned later}}
}
namespace MultiPointerTypes {
int** return_2p() {
int a = 1;
int* b = &a; // expected-warning {{address of stack memory is returned later}}
int** c = &b; // expected-warning {{address of stack memory is returned later}}
return c; // expected-note 2 {{returned here}}
}
int** return_2p_one_is_safe(int& a) {
int* b = &a;
int** c = &b; // expected-warning {{address of stack memory is returned later}}
return c; // expected-note {{returned here}}
}
int*** return_3p() {
int a = 1;
int* b = &a; // expected-warning {{address of stack memory is returned later}}
int** c = &b; // expected-warning {{address of stack memory is returned later}}
int*** d = &c; // expected-warning {{address of stack memory is returned later}}
return d; // expected-note 3 {{returned here}}
}
View** return_view_p() {
MyObj a;
View b = a; // expected-warning {{address of stack memory is returned later}}
View* c = &b; // expected-warning {{address of stack memory is returned later}}
View** d = &c; // expected-warning {{address of stack memory is returned later}}
return d; // expected-note 3 {{returned here}}
}
} // namespace MultiPointerTypes
View call_max_with_view_without_error() {
View va, vb;
return MaxT(va, vb);
}
} // namespace StdMaxStyleLifetimeBound
namespace CppCoverage {
int getInt();
void ReferenceParam(unsigned Value, unsigned &Ref) {
Value = getInt();
Ref = getInt();
}
inline void normalize(int &exponent, int &mantissa) {
const int shift = 1;
exponent -= shift;
mantissa <<= shift;
}
void add(int c, MyObj* node) {
MyObj* arr[10];
arr[4] = node;
}
} // namespace CppCoverage