blob: 2803e73b5aee27b962d26ac9efb09fec3dd7c004 [file] [log] [blame] [edit]
// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -verify %s
struct MyObj {
int id;
~MyObj() {} // Non-trivial destructor
MyObj operator+(MyObj);
};
struct [[gsl::Pointer()]] View {
View(const MyObj&); // Borrows from MyObj
View();
void use() const;
};
//===----------------------------------------------------------------------===//
// 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 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}}
}
//===----------------------------------------------------------------------===//
// 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* 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}}
}
//===----------------------------------------------------------------------===//
// 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]]);
struct [[gsl::Pointer()]] LifetimeBoundView {
LifetimeBoundView();
LifetimeBoundView(const MyObj& obj [[clang::lifetimebound]]);
LifetimeBoundView pass() [[clang::lifetimebound]] { return *this; }
operator View() const [[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}}
}
void lifetimebound_member_function() {
LifetimeBoundView lbv, lbv2;
{
MyObj obj;
lbv = obj; // expected-warning {{object whose reference is captured does not live long enough}}
lbv2 = lbv.pass();
} // expected-note {{destroyed here}}
View v = lbv2; // expected-note {{later used here}}
v.use();
}
void lifetimebound_conversion_operator() {
View v;
{
MyObj obj;
LifetimeBoundView lbv = obj; // expected-warning {{object whose reference is captured does not live long enough}}
v = lbv; // Conversion operator is lifetimebound
} // 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}}
}
// FIXME: Creating reference from lifetimebound call doesn't propagate loans.
const MyObj& GetObject(View v [[clang::lifetimebound]]);
void lifetimebound_return_reference() {
View v;
const MyObj* ptr;
{
MyObj obj;
View temp_v = obj;
const MyObj& ref = GetObject(temp_v);
ptr = &ref;
}
(void)*ptr;
}
// 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}}
}
// FIXME: Fails to diagnose UAR when a reference to a by-value param escapes via the return value.
View lifetimebound_return_of_by_value_param(MyObj stack_param) {
return Identity(stack_param);
}
// 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}}
}