// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -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}} } //===----------------------------------------------------------------------===// // Potential (Maybe) Use-After-Free (-W...strict) // These are cases where the pointer *may* become dangling, depending on the path taken. //===----------------------------------------------------------------------===// 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}} (void)*p; // expected-note {{later used here}} } void potential_if_branch_gsl(bool cond) { MyObj safe; View v = safe; if (cond) { MyObj temp; v = temp; // expected-warning {{object whose reference is captured may 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; 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}} (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; } //===----------------------------------------------------------------------===// // Control Flow Tests //===----------------------------------------------------------------------===// 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; for (int i = 0; i < 1; ++i) { (void)*p; // expected-note {{later used here}} MyObj s; p = &s; // expected-warning {{may not live long enough}} } // expected-note {{destroyed here}} (void)*p; } void potential_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 {{may not live long enough}} break; // expected-note {{destroyed here}} } } (void)*p; // expected-note {{later used here}} } void potential_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 may 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. MyObj safe; MyObj* p = &safe; for (int i = 0; i < 10; ++i) { MyObj unsafe; if (cond) { p = &unsafe; // expected-warning {{may 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 {{may 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 {{may not live long enough}} 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; } } (void)*p; // expected-note {{later used here}} } void definite_switch(int mode) { MyObj safe; MyObj* p = &safe; // All cases are UaF --> 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}} } //===----------------------------------------------------------------------===// // 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. } //===----------------------------------------------------------------------===// // Lifetimebound Attribute Tests //===----------------------------------------------------------------------===// View Identity(View 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 may not live long enough}} obj2); // expected-warning {{object whose reference is captured may 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 may 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; }