blob: 973c610eb58abc43bcbc6c1eb94de8d311328a2d [file]
// 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 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);
inline View redeclared_in_header(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}
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_source.cpp
#include "test_header.h"
#include "Inputs/lifetime-analysis.h"
View definition_before_header(View a) {
return a; // expected-note {{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}}
}
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);
View intra_tu_func_redecl(View a) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
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 {{object whose reference is captured 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 {{object whose reference is captured 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 {{object whose reference is captured 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);
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) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
return a; // expected-note {{param returned here}}
}
} // namespace incorrect_order_inference_view
namespace incorrect_order_inference_object {
MyObj* return_object_callee(MyObj* a);
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) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}.
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 {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
} // namespace simple_annotation_inference
namespace inference_in_order_with_redecls {
View inference_callee_return_identity(View a);
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);
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 {{address of stack memory is returned later}}
// expected-note@-1 {{returned here}}
}
} // namespace inference_in_order_with_redecls
namespace inference_with_templates {
template<typename T>
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<typename T>
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 {{address of stack memory is returned later}}
// 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 3 {{address of stack memory is returned later}} \
// 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 {{address of stack memory is returned later}}
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 {{address of stack memory is returned later}}
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 {{address of stack memory is returned later}}
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 {{address of stack memory is returned later}}
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 {{address of stack memory is returned later}}
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 {{address of stack memory is returned later}}
return x; // expected-note {{returned here}}
}
} // namespace capturing_constructor
namespace callable_wrappers {
std::function<void()> 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<void()> f = []() {};
{
int local;
f = return_lambda_capturing_param(local); // expected-warning {{object whose reference is captured 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<LifetimeBoundCtor> create_target(const MyObj& obj) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}}
return std::make_unique<LifetimeBoundCtor>(obj); // expected-note {{param returned here}}
}
void test_inference() {
std::unique_ptr<LifetimeBoundCtor> ptr;
{
MyObj obj;
ptr = create_target(obj); // expected-warning {{object whose reference is captured 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 {{object whose reference is captured 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 {{address of stack memory is returned later}}
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 {{address of stack memory escapes to a field}}
}
};
HasSetterField test_dangling_field_member_fn() {
MyObj obj;
HasSetterField x;
x.set(obj);
return x;
}
} // namespace new_allocation_suggestion
namespace GH193747 {
std::unique_ptr<int> create_up();
std::shared_ptr<int> create_sp() {
return create_up();
}
struct S {
int* field;
S(std::unique_ptr<int>&& up) : field(up.get()) { up.release(); }
};
S foo() {
return S(create_up());
}
} // namespace GH193747