[clang] 4c17aa1 - [LifetimeSafety] Add origin tracking for pointer dereference (#170006)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 7 03:02:16 PST 2026
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 at -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 at -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;
More information about the cfe-commits
mailing list