[clang] [clang][analyzer] Extend lifetime of dynamic extent information (PR #163562)

Balázs Kéri via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 31 10:45:28 PDT 2025


https://github.com/balazske updated https://github.com/llvm/llvm-project/pull/163562

>From 8c8277f292ed2a908f72f012ac8d4ebe9a3312b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Fri, 10 Oct 2025 17:29:37 +0200
Subject: [PATCH 1/3] [clang][analyzer] Extend lifetime of dynamic extent
 information

Symbols used for dynamic extent information of memory regions
are now kept as live as long as the memory region exists.
---
 .../Core/PathSensitive/DynamicExtent.h         |  2 ++
 .../lib/StaticAnalyzer/Core/DynamicExtent.cpp  |  6 ++++++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp   |  2 ++
 clang/test/Analysis/ArrayBound/verbose-tests.c | 18 ------------------
 4 files changed, 10 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h
index 1a9bef06b15a4..440603fb4d8c7 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h
@@ -58,6 +58,8 @@ SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV);
 DefinedOrUnknownSVal getDynamicElementCountWithOffset(ProgramStateRef State,
                                                       SVal BufV, QualType Ty);
 
+void markAllDynamicExtentLive(ProgramStateRef State, SymbolReaper &SymReaper);
+
 } // namespace ento
 } // namespace clang
 
diff --git a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
index 34078dbce0b68..e436b186a2148 100644
--- a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
@@ -128,5 +128,11 @@ ProgramStateRef setDynamicExtent(ProgramStateRef State, const MemRegion *MR,
   return State->set<DynamicExtentMap>(MR->StripCasts(), Size);
 }
 
+void markAllDynamicExtentLive(ProgramStateRef State, SymbolReaper &SymReaper) {
+  for (const auto &I : State->get<DynamicExtentMap>())
+    if (SymbolRef Sym = I.second.getAsSymbol())
+      SymReaper.markLive(Sym);
+}
+
 } // namespace ento
 } // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 785cdfa15bf04..d9ddc12c54985 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1064,6 +1064,8 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
       SymReaper.markLive(MR);
   }
 
+  markAllDynamicExtentLive(CleanedState, SymReaper);
+
   getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
 
   // Create a state in which dead bindings are removed from the environment
diff --git a/clang/test/Analysis/ArrayBound/verbose-tests.c b/clang/test/Analysis/ArrayBound/verbose-tests.c
index e3416886d13e5..9ee290ab6b5b8 100644
--- a/clang/test/Analysis/ArrayBound/verbose-tests.c
+++ b/clang/test/Analysis/ArrayBound/verbose-tests.c
@@ -381,30 +381,12 @@ int *symbolicExtent(int arg) {
     return 0;
   int *mem = (int*)malloc(arg);
 
-  // TODO: without the following reference to 'arg', the analyzer would discard
-  // the range information about (the symbolic value of) 'arg'. This is
-  // incorrect because while the variable itself is inaccessible, it becomes
-  // the symbolic extent of 'mem', so we still want to reason about its
-  // potential values.
-  (void)arg;
-
   mem[8] = -2;
   // expected-warning at -1 {{Out of bound access to memory after the end of the heap area}}
   // expected-note at -2 {{Access of 'int' element in the heap area at index 8}}
   return mem;
 }
 
-int *symbolicExtentDiscardedRangeInfo(int arg) {
-  // This is a copy of the case 'symbolicExtent' without the '(void)arg' hack.
-  // TODO: if the analyzer can detect the out-of-bounds access within this
-  // testcase, then remove this and the `(void)arg` hack from `symbolicExtent`.
-  if (arg >= 5)
-    return 0;
-  int *mem = (int*)malloc(arg);
-  mem[8] = -2;
-  return mem;
-}
-
 void symbolicIndex(int arg) {
   // expected-note at +2 {{Assuming 'arg' is >= 12}}
   // expected-note at +1 {{Taking true branch}}

>From b7d9cc05efb1a69b2753b055843316499168617b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Mon, 20 Oct 2025 16:33:30 +0200
Subject: [PATCH 2/3] make symbol live only if region is live

---
 clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp | 3 ++-
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp    | 7 +++++--
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
index e436b186a2148..e5d5e0113eb2c 100644
--- a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
@@ -131,7 +131,8 @@ ProgramStateRef setDynamicExtent(ProgramStateRef State, const MemRegion *MR,
 void markAllDynamicExtentLive(ProgramStateRef State, SymbolReaper &SymReaper) {
   for (const auto &I : State->get<DynamicExtentMap>())
     if (SymbolRef Sym = I.second.getAsSymbol())
-      SymReaper.markLive(Sym);
+      if (SymReaper.isLiveRegion(I.first))
+        SymReaper.markLive(Sym);
 }
 
 } // namespace ento
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d9ddc12c54985..b116d9d39e160 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1064,8 +1064,6 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
       SymReaper.markLive(MR);
   }
 
-  markAllDynamicExtentLive(CleanedState, SymReaper);
-
   getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
 
   // Create a state in which dead bindings are removed from the environment
@@ -1081,6 +1079,11 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
   getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper,
                                                 DiagnosticStmt, *this, K);
 
+  // Extend lifetime of symbols used for dynamic extent while the parent region
+  // is live. In this way size information about memory allocations is not lost
+  // if the region remains live.
+  markAllDynamicExtentLive(CleanedState, SymReaper);
+
   // For each node in CheckedSet, generate CleanedNodes that have the
   // environment, the store, and the constraints cleaned up but have the
   // user-supplied states as the predecessors.

>From 03187735535465df7c9fb546ef38a298ba6413fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Fri, 31 Oct 2025 18:44:58 +0100
Subject: [PATCH 3/3] updated test comment

---
 clang/test/Analysis/ArrayBound/verbose-tests.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/test/Analysis/ArrayBound/verbose-tests.c b/clang/test/Analysis/ArrayBound/verbose-tests.c
index 9ee290ab6b5b8..bc1915f734a25 100644
--- a/clang/test/Analysis/ArrayBound/verbose-tests.c
+++ b/clang/test/Analysis/ArrayBound/verbose-tests.c
@@ -408,8 +408,9 @@ int *nothingIsCertain(int x, int y) {
   //   {{Access of 'int' element in the heap area at an overflowing index}}
   // but apparently the analyzer isn't smart enough to deduce this.
 
-  // Keep constraints alive. (Without this, the overeager garbage collection of
-  // constraints would _also_ prevent the intended behavior in this testcase.)
+  // Keep constraints alive. (Without this, it is possible that the overeager
+  // garbage collection of constraints _also_ prevents the intended behavior in
+  // this testcase.)
   (void)x;
 
   return mem;



More information about the cfe-commits mailing list