Author: Utkarsh Saxena Date: 2026-01-07T11:02:12Z New Revision: 4c17aa1d27ee674a7029a9378f0b7bf2148c5696
URL: https://github.com/llvm/llvm-project/commit/4c17aa1d27ee674a7029a9378f0b7bf2148c5696 DIFF: https://github.com/llvm/llvm-project/commit/4c17aa1d27ee674a7029a9378f0b7bf2148c5696.diff LOG: [LifetimeSafety] Add origin tracking for pointer dereference (#170006) Fix lifetime safety analysis for pointer dereference operations by properly tracking origin flow. Added support in `FactsGenerator::VisitUnaryOperator()` to handle the dereference operator (`UO_Deref`). This change ensures that when a pointer is dereferenced, the origin of the pointer is properly propagated to the dereference expression. Added: Modified: clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp clang/test/Sema/warn-lifetime-safety-dataflow.cpp clang/test/Sema/warn-lifetime-safety-suggestions.cpp clang/test/Sema/warn-lifetime-safety.cpp Removed: ################################################################################ diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 6a213a71afe12..546fe37491040 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -232,13 +232,23 @@ void FactsGenerator::VisitImplicitCastExpr(const ImplicitCastExpr *ICE) { } void FactsGenerator::VisitUnaryOperator(const UnaryOperator *UO) { - if (UO->getOpcode() == UO_AddrOf) { + switch (UO->getOpcode()) { + case UO_AddrOf: { const Expr *SubExpr = UO->getSubExpr(); // The origin of an address-of expression (e.g., &x) is the origin of // its sub-expression (x). This fact will cause the dataflow analysis // to propagate any loans held by the sub-expression's origin to the // origin of this UnaryOperator expression. killAndFlowOrigin(*UO, *SubExpr); + return; + } + case UO_Deref: { + const Expr *SubExpr = UO->getSubExpr(); + killAndFlowOrigin(*UO, *SubExpr); + return; + } + default: + return; } } diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp index 6d5711deba1cf..6fc7c776f935c 100644 --- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp +++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp @@ -152,6 +152,12 @@ void pointer_indirection() { // CHECK-NEXT: Dest: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *) // CHECK-NEXT: Src: [[O_PP_INNER]] (Decl: pp, Type : int *) // CHECK: OriginFlow: +// CHECK-NEXT: Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : int *&) +// CHECK-NEXT: Src: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int **) +// CHECK: OriginFlow: +// CHECK-NEXT: Dest: {{[0-9]+}} (Expr: UnaryOperator, Type : int *) +// CHECK-NEXT: Src: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *) +// CHECK: OriginFlow: // CHECK-NEXT: Dest: {{[0-9]+}} (Expr: ImplicitCastExpr, Type : int *) // CHECK-NEXT: Src: {{[0-9]+}} (Expr: UnaryOperator, Type : int *) // CHECK: OriginFlow: diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index 8a328dfbc8d9e..68107b45c34b1 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -94,9 +94,12 @@ MyObj* return_ptr_to_ref(MyObj& a) { // expected-warning {{parameter in intra-TU return &a; // expected-note {{param returned here}} } -// FIXME: Dereference does not propagate loans. -MyObj& return_ref_to_ptr(MyObj* a) { - return *a; +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]]}} diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index eb48d9e674d9a..5706d195c383b 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -596,10 +596,10 @@ const int* return_pointer_to_parameter_via_reference(int a, int b, bool cond) { 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* d = &a; // expected-warning {{address of stack memory is returned later}} + return *d; // expected-note {{returned here}} } const int& get_ref_to_local() { @@ -1119,24 +1119,24 @@ struct MyObjStorage { 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) { + for (const MyObj &o : s) { // expected-warning {{object whose reference is captured does not live long enough}} v = o; } - } - v.use(); + } // expected-note {{destroyed here}} + v.use(); // expected-note {{later used here}} } -// 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; + for (const MyObj &o : s) { // expected-warning {{address of stack memory is returned later}} + return o; // expected-note {{returned here}} } - return *s.begin(); + return *s.begin(); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} } void range_based_for_not_reference() { @@ -1161,6 +1161,66 @@ void range_based_for_no_error() { } // namespace RangeBaseForLoop +namespace UserDefinedDereference { +// Test user-defined dereference operators with lifetimebound +template<typename T> +struct SmartPtr { + T* ptr; + SmartPtr() {} + SmartPtr(T* p) : ptr(p) {} + T& operator*() const [[clang::lifetimebound]] { return *ptr; } + T* operator->() const [[clang::lifetimebound]] { return ptr; } +}; + +void test_user_defined_deref_uaf() { + MyObj* p; + { + MyObj obj; + SmartPtr<MyObj> smart_ptr(&obj); + p = &(*smart_ptr); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +MyObj& test_user_defined_deref_uar() { + MyObj obj; + SmartPtr<MyObj> smart_ptr(&obj); + return *smart_ptr; // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} + +void test_user_defined_deref_with_view() { + View v; + { + MyObj obj; + SmartPtr<MyObj> smart_ptr(&obj); + v = *smart_ptr; // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + v.use(); // expected-note {{later used here}} +} + +void test_user_defined_deref_arrow() { + MyObj* p; + { + MyObj obj; + SmartPtr<MyObj> smart_ptr(&obj); + p = smart_ptr.operator->(); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +void test_user_defined_deref_chained() { + MyObj* p; + { + MyObj obj; + SmartPtr<SmartPtr<MyObj>> double_ptr; + p = &(**double_ptr); // expected-warning {{object whose reference is captured does not live long enough}} + } // expected-note {{destroyed here}} + (void)*p; // expected-note {{later used here}} +} + +} // namespace UserDefinedDereference + namespace structured_binding { struct Pair { MyObj a; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
