https://github.com/benedekaibas created 
https://github.com/llvm/llvm-project/pull/211552

Currently the `DanglingPtrDeref` checker only records the whole object's region 
as deallocated. However, dangling pointers often refer to subobjects (e.g., a 
struct field or an element of an array). With the change in this PR, 
`isDeallocated` now checks whether a region's base region is in the 
`DeallocatedSourceSet`.

>From 28b8e2424a5e3c947765457f2de5f2bf937d100b Mon Sep 17 00:00:00 2001
From: benedekaibas <[email protected]>
Date: Thu, 23 Jul 2026 15:01:21 +0200
Subject: [PATCH] [analyzer] Match dangling subobjects by their base region in
 DanglingPtrDeref

---
 .../Checkers/DanglingPtrDeref.cpp             | 14 +++--
 .../Checkers/LifetimeModeling.cpp             |  2 +-
 clang/test/Analysis/dangling-ptr-deref.cpp    | 53 +++++++++++++++++++
 3 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp 
b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
index ff2087e1db933..41fcc68f8f109 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
@@ -66,13 +66,21 @@ void DanglingPtrDeref::checkPostCall(const CallEvent &Call,
   }
 }
 
+static std::string getRegionName(const MemRegion *Reg) {
+  // FIXME: Once the checker supports heap allocation, more region kinds
+  // should be handled to produce the correct descriptive name.
+  if (const std::string RegName = Reg->getDescriptiveName(); !RegName.empty())
+    return RegName;
+  return "the region";
+}
+
 void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region,
                                            ExplodedNode *N,
                                            CheckerContext &C) const {
   auto BR = std::make_unique<PathSensitiveBugReport>(
       BugMsg,
-      (llvm::Twine("Use of '") + Region->getString() +
-       "' after its lifetime ended."),
+      (llvm::Twine("Use of ") + getRegionName(Region) +
+       " after its lifetime ended."),
       N);
   BR->addVisitor<DanglingPtrDerefBRVisitor>(Region);
   C.emitReport(std::move(BR));
@@ -99,7 +107,7 @@ DanglingPtrDerefBRVisitor::VisitNode(const ExplodedNode *N,
       S, BRC.getSourceManager(), N->getStackFrame());
   return std::make_shared<PathDiagnosticEventPiece>(
       Pos,
-      (llvm::Twine("'") + SourceRegion->getString() + "' is destroyed here")
+      (llvm::Twine() + getRegionName(SourceRegion) + " is destroyed here")
           .str(),
       true);
 }
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 2b6f5dae4243f..ef0b1cb264b18 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -73,7 +73,7 @@ std::vector<const MemRegion *> 
lifetime_modeling::getDanglingRegionsAfterReturn(
 
 bool lifetime_modeling::isDeallocated(ProgramStateRef State,
                                       const MemRegion *Region) {
-  return State->contains<DeallocatedSourceSet>(Region);
+  return State->contains<DeallocatedSourceSet>(Region->getBaseRegion());
 }
 
 static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal,
diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp 
b/clang/test/Analysis/dangling-ptr-deref.cpp
index e661eff9ccbb6..9deda4b2502a4 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -112,3 +112,56 @@ void inlined_callee_single_report() {
   // expected-note@-1 {{Calling 'deref_param'}}
   (void)r;
 }
+
+struct MyBuffer {
+  char buffer[8];
+};
+
+void member_subregion_dangling_deref() {
+  const char *p = nullptr;
+  {
+    struct MyBuffer tmp_buffer = {};
+    p = tmp_buffer.buffer;
+  }
+  // expected-note@-1 {{'tmp_buffer.buffer[0]' is destroyed here}}
+  char c = *p;
+  // expected-warning@-1 {{Use of 'tmp_buffer.buffer[0]' after its lifetime 
ended}}
+  // expected-note@-2    {{Use of 'tmp_buffer.buffer[0]' after its lifetime 
ended}}
+  (void)c;
+}
+
+void opaque(const char *);
+
+void passing_dangling_to_call() {
+  const char *p = nullptr;
+  {
+    struct MyBuffer tmp_buffer = {};
+    p = tmp_buffer.buffer;
+  }
+  // expected-note@-1 {{'tmp_buffer.buffer[0]' is destroyed here}}
+  opaque(p);
+  // expected-warning@-1 {{Use of 'tmp_buffer.buffer[0]' after its lifetime 
ended}}
+  // expected-note@-2    {{Use of 'tmp_buffer.buffer[0]' after its lifetime 
ended}}
+}
+
+void member_subregion_alive_deref() {
+  {
+    struct MyBuffer tmp_buffer = {};
+    const char *p = tmp_buffer.buffer;
+    opaque(p); //   no-warning
+    char c = *p; // no-warning
+    (void)c;
+  }
+}
+
+void arr_elem_subreg_dangling_deref() {
+  int *ptr = nullptr;
+  {
+    int local_arr[5];
+    ptr = &local_arr[1];
+  }
+  // expected-note@-1 {{'local_arr[1]' is destroyed here}}
+  *ptr = 7;
+  // expected-warning@-1 {{Use of 'local_arr[1]' after its lifetime ended}}
+  // expected-note@-2    {{Use of 'local_arr[1]' after its lifetime ended}}
+}

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

Reply via email to