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

Reply via email to