https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/176805

>From 45af9400622db8746f960b42e96c3955136db947 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <[email protected]>
Date: Mon, 19 Jan 2026 19:28:46 +0000
Subject: [PATCH] Detect dangling references to field members

---
 .../Analyses/LifetimeSafety/FactsGenerator.h  |  1 +
 .../LifetimeSafety/FactsGenerator.cpp         | 15 ++++++++++
 .../Sema/warn-lifetime-analysis-nocfg.cpp     | 30 +++++++++----------
 .../Sema/warn-lifetime-safety-suggestions.cpp |  6 ++--
 clang/test/Sema/warn-lifetime-safety.cpp      | 28 +++++++++++++++++
 5 files changed, 62 insertions(+), 18 deletions(-)

diff --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index ee2f1aae4a4c5..a47505ee9f159 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -37,6 +37,7 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
   void VisitDeclRefExpr(const DeclRefExpr *DRE);
   void VisitCXXConstructExpr(const CXXConstructExpr *CCE);
   void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE);
+  void VisitMemberExpr(const MemberExpr *ME);
   void VisitCallExpr(const CallExpr *CE);
   void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N);
   void VisitImplicitCastExpr(const ImplicitCastExpr *ICE);
diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index bbab0627bdead..ccedd0522cbb3 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -203,6 +203,21 @@ void FactsGenerator::VisitCXXMemberCallExpr(const 
CXXMemberCallExpr *MCE) {
   }
 }
 
+void FactsGenerator::VisitMemberExpr(const MemberExpr *ME) {
+  if (isa<FieldDecl>(ME->getMemberDecl())) {
+    assert(ME->isGLValue() && "Field member should be GL value");
+    OriginList *Dst = getOriginsList(*ME);
+    assert(Dst && "Field member should have an origin list as it is GL value");
+    OriginList *Src = getOriginsList(*ME->getBase());
+    assert(Src && "Base expression should be a pointer/reference type");
+    // The field's glvalue (outermost origin) holds the same loans as the base
+    // expression.
+    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+        Dst->getOuterOriginID(), Src->getOuterOriginID(),
+        /*Kill=*/true));
+  }
+}
+
 static bool isStdMove(const FunctionDecl *FD) {
   return FD && FD->isInStdNamespace() && FD->getIdentifier() &&
          FD->getName() == "move";
diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp 
b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
index 5b6a548389bd5..cb06ec4930907 100644
--- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
+++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp
@@ -1009,15 +1009,12 @@ void 
operator_star_arrow_of_iterators_false_positive_no_cfg_analysis() {
   const char* q = (*v.begin()).second.data();
   const std::string& r = (*v.begin()).second;
 
-  // FIXME: Detect this using the CFG-based lifetime analysis.
-  //        Detect dangling references to struct field.
-  //        https://github.com/llvm/llvm-project/issues/176144
   auto temporary = []() { return std::vector<std::pair<int, std::string>>{{1, 
"1"}}; };
-  const char* x = temporary().begin()->second.data();
-  const char* y = (*temporary().begin()).second.data();
-  const std::string& z = (*temporary().begin()).second;
+  const char* x = temporary().begin()->second.data();   // cfg-warning 
{{object whose reference is captured does not live long enough}} cfg-note 
{{destroyed here}}
+  const char* y = (*temporary().begin()).second.data(); // cfg-warning 
{{object whose reference is captured does not live long enough}} cfg-note 
{{destroyed here}}
+  const std::string& z = (*temporary().begin()).second; // cfg-warning 
{{object whose reference is captured does not live long enough}} cfg-note 
{{destroyed here}}
 
-  use(p, q, r, x, y, z);
+  use(p, q, r, x, y, z); // cfg-note 3 {{later used here}}
 }
 } // namespace iterator_arrow
 
@@ -1057,24 +1054,27 @@ struct S {
 };
 struct Q {
   const S* get() const [[clang::lifetimebound]];
+  ~Q();
 };
 
 std::string_view foo(std::string_view sv [[clang::lifetimebound]]);
 
-// FIXME: Detect this using the CFG-based lifetime analysis.
-//        Detect dangling references to struct field.
-//        https://github.com/llvm/llvm-project/issues/176144
 void test1() {
   std::string_view k1 = S().sv; // OK
-  std::string_view k2 = S().s; // expected-warning {{object backing the 
pointer will}}
+  std::string_view k2 = S().s; // expected-warning {{object backing the 
pointer will}} \
+                               // cfg-warning {{object whose reference is 
captured does not live long enough}} cfg-note {{destroyed here}}
 
   std::string_view k3 = Q().get()->sv; // OK
-  std::string_view k4  = Q().get()->s; // expected-warning {{object backing 
the pointer will}}
+  std::string_view k4  = Q().get()->s; // expected-warning {{object backing 
the pointer will}} \
+                                       // cfg-warning {{object whose reference 
is captured does not live long enough}} cfg-note {{destroyed here}}
 
-  std::string_view lb1 = foo(S().s); // expected-warning {{object backing the 
pointer will}}
-  std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object 
backing the pointer will}}
 
-  use(k1, k2, k3, k4, lb1, lb2);
+  std::string_view lb1 = foo(S().s); // expected-warning {{object backing the 
pointer will}} \
+                                     // cfg-warning {{object whose reference 
is captured does not live long enough}} cfg-note {{destroyed here}}
+  std::string_view lb2 = foo(Q().get()->s); // expected-warning {{object 
backing the pointer will}} \
+                                            // cfg-warning {{object whose 
reference is captured does not live long enough}} cfg-note {{destroyed here}}
+
+  use(k1, k2, k3, k4, lb1, lb2);  // cfg-note 4 {{later used here}}
 }
 
 struct Bar {};
diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp 
b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
index 6e3a6f1fd9117..bb8fd3933b55d 100644
--- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
+++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp
@@ -110,9 +110,9 @@ struct Container {
   MyObj data;
   const MyObj& getData() [[clang::lifetimebound]] { return data; }
 };
-// FIXME: c.data does not forward loans
-View return_struct_field(const Container& c) {
-  return c.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}}
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index 13666b0402522..84a6bc16ba52a 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -1492,3 +1492,31 @@ const std::string& test_return() {
   return x; // expected-note {{later used here}}
 }
 } // namespace reference_type_decl_ref_expr
+
+namespace field_access{
+
+struct S {
+  std::string s;
+  std::string_view sv;
+};
+
+void uaf() {
+  std::string_view view;
+  {
+    S str;
+    S* p = &str;  // expected-warning {{object whose reference is captured 
does not live long enough}}
+    view = p->s;
+  } // expected-note {{destroyed here}}
+  (void)view;  // expected-note {{later used here}}
+}
+
+void not_uaf() {
+  std::string_view view;
+  {
+    S str;
+    S* p = &str;
+    view = p->sv;
+  }
+  (void)view;
+}
+} // namespace field_access

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to