// RUN: rm -rf %t // RUN: split-file %s %t // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -verify %t/test_source.cpp // RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -verify %t/test_source.cpp // RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wno-dangling -I%t -I%S -Werror=lifetime-safety-suggestions %t/test_source.cpp View definition_before_header(View a); //--- test_header.h #ifndef TEST_HEADER_H #define TEST_HEADER_H struct View; struct [[gsl::Owner]] MyObj { int id; MyObj(int i) : id(i) {} MyObj() {} ~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; }; View definition_before_header(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View redeclared_before_header_include(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View return_view_directly(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View conditional_return_view( View a, // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View b, // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} bool c); int* return_pointer_directly(int* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} MyObj* return_pointer_object(MyObj* a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} inline View inline_header_return_view(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return a; // expected-note {{param returned here}} } View redeclared_in_header(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} inline View redeclared_in_header(View a) { return a; // expected-note {{param returned here}} } View return_unnamed_view(View); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} MyObj& return_unnamed_ref(MyObj&, bool); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} struct ReturnThis { const ReturnThis& get() const; // expected-warning {{implicit this in cross-TU function should be marked [[clang::lifetimebound]]}}. }; struct ReturnThisPointer { const ReturnThisPointer* get() const; // expected-warning {{implicit this in cross-TU function should be marked [[clang::lifetimebound]]}}. }; #endif // TEST_HEADER_H //--- test_redecls_header.h View earliest_decl_in_header(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View earliest_decl_in_header(View a); View multi_redecl_one_file(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} View multi_redecl_one_file(View a); View multi_redecl_one_file(View a); View source_and_header(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} //--- test_redecls_header_1.h View in_two_headers(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} //--- test_redecls_header_2.h View in_two_headers(View a); // expected-warning {{parameter in cross-TU function should be marked [[clang::lifetimebound]]}} //--- test_source.cpp struct View; View redeclared_before_header_include(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} View source_and_header(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} #include "test_header.h" #include "test_redecls_header.h" #include "test_redecls_header_1.h" #include "test_redecls_header_2.h" #include "Inputs/lifetime-analysis.h" View earliest_decl_in_source(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} View earliest_decl_in_source(View a); View definition_before_header(View a) { return a; // expected-note {{param returned here}} } View redeclared_before_header_include(View a) { return a; // expected-note 2 {{param returned here}} } View return_view_directly(View a) { return a; // expected-note {{param returned here}} } View conditional_return_view(View a, View b, bool c) { View res; if (c) res = a; else res = b; return res; // expected-note 2 {{param returned here}} } int* return_pointer_directly(int* a) { return a; // expected-note {{param returned here}} } MyObj* return_pointer_object(MyObj* a) { return a; // expected-note {{param returned here}} } View return_unnamed_view(View a) { return a; // expected-note {{param returned here}} } MyObj& return_unnamed_ref(MyObj& a, bool c) { return a; // expected-note {{param returned here}} } View earliest_decl_in_header(View a) { return a; // expected-note {{param returned here}} } View earliest_decl_in_source(View a) { return a; // expected-note {{param returned here}} } View multi_redecl_one_file(View a) { return a; // expected-note {{param returned here}} } View in_two_headers(View a) { return a; // expected-note 2 {{param returned here}} } View source_and_header(View a) { return a; // expected-note 2 {{param returned here}} } MyObj& return_reference(MyObj& a, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} MyObj& b, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} bool c) { if(c) { return a; // expected-note {{param returned here}} } return b; // expected-note {{param returned here}} } const MyObj& return_reference_const(const MyObj& a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return a; // expected-note {{param returned here}} } MyObj* return_ptr_to_ref(MyObj& a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return &a; // expected-note {{param returned here}} } MyObj& return_ref_to_ptr(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return *a; // expected-note {{param returned here}} } View return_ref_to_ptr_multiple(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return *(&(*(&(*a)))); // expected-note {{param returned here}} } View return_view_from_reference(MyObj& p) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return p; // expected-note {{param returned here}} } struct Container { MyObj data; const MyObj& getData() [[clang::lifetimebound]] { return data; } }; View return_struct_field(const Container& c) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return c.data; // expected-note {{param returned here}} } View return_struct_lifetimebound_getter(Container& c) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return c.getData().getView(); // expected-note {{param returned here}} } View return_view_from_reference_lifetimebound_member(MyObj& p) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return p.getView(); // expected-note {{param returned here}} } View return_cross_tu_func_View(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_view_directly(a); // expected-note {{param returned here}} } MyObj* return_cross_tu_func_obj(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_pointer_object(a); // expected-note {{param returned here}} } int* return_cross_tu_func_pointer(int* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_pointer_directly(a); // expected-note {{param returned here}} } namespace { View only_one_paramter_annotated(View a [[clang::lifetimebound]], View b, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. bool c) { if(c) return a; return b; // expected-note {{param returned here}} } View reassigned_to_another_parameter( View a, View b) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. a = b; return a; // expected-note {{param returned here}} } View intra_tu_func_redecl(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. View intra_tu_func_redecl(View a) { return a; // expected-note {{param returned here}} } } static View return_view_static(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } const ReturnThis& ReturnThis::get() const { return *this; // expected-note {{param returned here}} } const ReturnThisPointer* ReturnThisPointer::get() const { return this; // expected-note {{param returned here}} } struct ReturnsSelf { ReturnsSelf() {} ~ReturnsSelf() {} const ReturnsSelf& get() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return *this; // expected-note {{param returned here}} } }; struct ReturnThisAnnotated { const ReturnThisAnnotated& get() [[clang::lifetimebound]] { return *this; } }; struct ViewProvider { ViewProvider(int d) : data(d) {} ~ViewProvider() {} MyObj data; View getView() const { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return data; // expected-note {{param returned here}} } }; View return_view_field(const ViewProvider& v) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return v.data; // expected-note {{param returned here}} } void test_get_on_temporary_pointer() { const ReturnsSelf* s_ref = &ReturnsSelf().get(); // expected-warning {{local temporary object does not live long enough}}. // expected-note@-1 {{destroyed here}} (void)s_ref; // expected-note {{later used here}} } void test_get_on_temporary_ref() { const ReturnsSelf& s_ref = ReturnsSelf().get(); // expected-warning {{local temporary object does not live long enough}}. // expected-note@-1 {{destroyed here}} (void)s_ref; // expected-note {{later used here}} } void test_getView_on_temporary() { View sv = ViewProvider{1}.getView(); // expected-warning {{local temporary object does not live long enough}}. // expected-note@-1 {{destroyed here}} (void)sv; // expected-note {{later used here}} } void test_get_on_temporary_copy() { ReturnsSelf copy = ReturnsSelf().get(); (void)copy; } struct MemberReturn { MyObj data; MyObj& getRef() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return data; // expected-note {{param returned here}} } MyObj& getRefExplicit() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return this->data; // expected-note {{param returned here}} } MyObj& getRefDereference() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return (*this).data; // expected-note {{param returned here}} } const MyObj* getPtr() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return &data; // expected-note {{param returned here}} } const MyObj* getPtrExplicit() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}}. return &(this->data); // expected-note {{param returned here}} } }; //===----------------------------------------------------------------------===// // Annotation Inference Test Cases //===----------------------------------------------------------------------===// namespace correct_order_inference { View return_view_by_func(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_view_directly(a); // expected-note {{param returned here}} } MyObj* return_pointer_by_func(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_pointer_object(a); // expected-note {{param returned here}} } } // namespace correct_order_inference namespace incorrect_order_inference_view { View return_view_callee(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. View return_view_caller(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_view_callee(a); // expected-note {{param returned here}} } View return_view_callee(View a) { return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_view namespace incorrect_order_inference_object { MyObj* return_object_callee(MyObj* a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. MyObj* return_object_caller(MyObj* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return return_object_callee(a); // expected-note {{param returned here}} } MyObj* return_object_callee(MyObj* a) { return a; // expected-note {{param returned here}} } } // namespace incorrect_order_inference_object namespace simple_annotation_inference { View inference_callee_return_identity(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } View inference_caller_forwards_callee(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return inference_callee_return_identity(a); // expected-note {{param returned here}} } View inference_top_level_return_stack_view() { MyObj local_stack; return inference_caller_forwards_callee(local_stack); // expected-warning {{stack memory associated with local variable 'local_stack' is returned}} // expected-note@-1 {{returned here}} } } // namespace simple_annotation_inference namespace inference_in_order_with_redecls { View inference_callee_return_identity(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. View inference_callee_return_identity(View a) { return a; // expected-note {{param returned here}} } View inference_caller_forwards_callee(View a); // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. View inference_caller_forwards_callee(View a) { return inference_callee_return_identity(a); // expected-note {{param returned here}} } View inference_top_level_return_stack_view() { MyObj local_stack; return inference_caller_forwards_callee(local_stack); // expected-warning {{stack memory associated with local variable 'local_stack' is returned}} // expected-note@-1 {{returned here}} } } // namespace inference_in_order_with_redecls namespace inference_with_templates { template T* template_identity(T* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return a; // expected-note {{param returned here}} } template T* template_caller(T* a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}. return template_identity(a); // expected-note {{param returned here}} } MyObj* test_template_inference_with_stack() { MyObj local_stack; return template_caller(&local_stack); // expected-warning {{stack memory associated with local variable 'local_stack' is returned}} // expected-note@-1 {{returned here}} } } // namespace inference_with_templates namespace trailing_return { struct TrailingReturn { TrailingReturn() {} ~TrailingReturn() {} MyObj data; auto get_view() -> View { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return data; // expected-note {{param returned here}} } auto get_view_const() const -> View { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return data; // expected-note {{param returned here}} } auto get_ref() const -> const MyObj& { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return data; // expected-note {{param returned here}} } }; } // namespace trailing_return //===----------------------------------------------------------------------===// // Negative Test Cases //===----------------------------------------------------------------------===// View already_annotated(View a [[clang::lifetimebound]]) { return a; } MyObj return_obj_by_value(MyObj& p) { return p; } MyObj GlobalMyObj; View Global = GlobalMyObj; View Reassigned(View a) { a = Global; return a; } namespace lambda_captures { struct NoSuggestionForThisCapturedByLambda { MyObj s; bool cond; void foo() { auto x = [&]() { return cond > 0 ? &this->s : &s; }; } }; void Foo(int, int*, const MyObj&, View); auto implicit_ref_capture(int integer, int* ptr, const MyObj& ref, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} View view) { return [&]() { Foo(integer, ptr, ref, view); }; // expected-warning {{stack memory associated with parameter 'ptr' is returned}} \ // expected-warning {{stack memory associated with parameter 'view' is returned}} \ // expected-warning {{stack memory associated with parameter 'integer' is returned}} \ // expected-note 3 {{returned here}} \ // expected-note {{param returned here}} } auto implicit_value_capture(int integer, int* ptr, // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} const MyObj& ref, View view) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return [=]() { Foo(integer, ptr, ref, view); }; // expected-note 2 {{param returned here}} } } // namespace lambda_captures namespace array { struct MemberArrayReturn { int arr[10]; int* getFirst() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return &arr[0]; // expected-note {{param returned here}} } int* getData() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return arr; // expected-note {{param returned here}} } int* getLast() { // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} return arr + 10; // expected-note {{param returned here}} } }; } // namespace array namespace track_origins_for_lifetimebound_record_type { struct S { View view; }; S getS(const MyObj &obj [[clang::lifetimebound]]); S forward(const MyObj &obj) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return getS(obj); // expected-note {{param returned here}} } } // namespace track_origins_for_lifetimebound_record_type namespace capturing_constructor { struct CaptureRefToView { View v; // expected-note {{escapes to this field}} CaptureRefToView(const MyObj& obj) : v(obj) {} // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} }; CaptureRefToView test_ref_to_view() { MyObj obj; CaptureRefToView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} return x; // expected-note {{returned here}} } struct CaptureRefToPtr { const MyObj* p; // expected-note {{escapes to this field}} CaptureRefToPtr(const MyObj& obj) : p(&obj) {} // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} }; CaptureRefToPtr test_ref_to_ptr() { MyObj obj; CaptureRefToPtr x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} return x; // expected-note {{returned here}} } struct CaptureViewToView { View v; // expected-note {{escapes to this field}} CaptureViewToView(View v_param) : v(v_param) {} // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} }; CaptureViewToView test_view_to_view() { MyObj obj; View v(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} CaptureViewToView x(v); return x; // expected-note {{returned here}} } struct CapturePtrToPtr { const MyObj* p; // expected-note {{escapes to this field}} CapturePtrToPtr(const MyObj* p_param) : p(p_param) {} // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} }; CapturePtrToPtr test_ptr_to_ptr() { MyObj obj; CapturePtrToPtr x(&obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} return x; // expected-note {{returned here}} } struct CaptureRefToRef { const MyObj& r; // expected-note {{escapes to this field}} CaptureRefToRef(const MyObj& obj) : r(obj) {} // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} }; CaptureRefToRef test_ref_to_ref() { MyObj obj; CaptureRefToRef x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} return x; // expected-note {{returned here}} } struct BaseWithView { View v; // expected-note {{escapes to this field}} }; struct CaptureRefToBaseView : BaseWithView { CaptureRefToBaseView(const MyObj& obj) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} v = obj; } }; CaptureRefToBaseView test_ref_to_base_view() { MyObj obj; CaptureRefToBaseView x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} return x; // expected-note {{returned here}} } } // namespace capturing_constructor namespace callable_wrappers { std::function return_lambda_capturing_param(int &x) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return [&]() { (void)x; }; // expected-note {{param returned here}} } void uaf_via_inferred_lifetimebound() { std::function f = []() {}; { int local; f = return_lambda_capturing_param(local); // expected-warning {{local variable 'local' does not live long enough}} } // expected-note {{destroyed here}} (void)f; // expected-note {{later used here}} } } // namespace callable_wrappers namespace make_unique_suggestion { struct LifetimeBoundCtor { View v; // FIXME: This test fails to propagate the lifetimebound in ctor if this is inferred (instead of the current explicit annotation). LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]): v(obj) {} }; std::unique_ptr create_target(const MyObj& obj) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return std::make_unique(obj); // expected-note {{param returned here}} } void test_inference() { std::unique_ptr ptr; { MyObj obj; ptr = create_target(obj); // expected-warning {{local variable 'obj' does not live long enough}} } // expected-note {{destroyed here}} (void)ptr; // expected-note {{later used here}} } } // namespace make_unique_suggestion namespace new_allocation_suggestion { View* MakeView(const MyObj& in) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} return new View(in); // expected-note {{param returned here}} {{destroyed here}} } void test_new_allocation() { View* v = MakeView(MyObj{}); // expected-warning {{local temporary object does not live long enough}} \ // expected-note {{destroyed here}} (void)v; // expected-note {{later used here}} } struct LifetimeBoundCtor { View v; LifetimeBoundCtor(); LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]) : v(obj) {} }; struct HasCtorField { LifetimeBoundCtor* field; // expected-note {{escapes to this field}} HasCtorField(const MyObj& obj) : field(new LifetimeBoundCtor(obj)) {} // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} }; HasCtorField test_dangling_field_ctor() { MyObj obj; HasCtorField x(obj); // expected-warning {{stack memory associated with local variable 'obj' is returned}} return x; // expected-note {{returned here}} } struct HasSetterField { LifetimeBoundCtor* field; // expected-note {{this field dangles}} // FIXME: Does not currently suggest `lifetime_capture_by(this)` (even without `new`) void set(const MyObj& obj) { field = new LifetimeBoundCtor(obj); } void reset() { MyObj obj; field = new LifetimeBoundCtor(obj); // expected-warning {{stack memory associated with local variable 'obj' escapes to the field 'field' which will dangle}} } }; HasSetterField test_dangling_field_member_fn() { MyObj obj; HasSetterField x; x.set(obj); return x; } } // namespace new_allocation_suggestion namespace GH193747 { std::unique_ptr create_up(); std::shared_ptr create_sp() { return create_up(); } struct S { int* field; S(std::unique_ptr&& up) : field(up.get()) { up.release(); } }; S foo() { return S(create_up()); } } // namespace GH193747