https://github.com/andyames-a11y updated 
https://github.com/llvm/llvm-project/pull/210649

>From 6b8cc1fa8411fa1a7f625174d63cc0f942284efe Mon Sep 17 00:00:00 2001
From: Andy Ames <[email protected]>
Date: Sun, 19 Jul 2026 21:59:48 -0700
Subject: [PATCH 1/3] [analyzer] Fix crash in RegionStoreManager::bindArray
 from constructor array-to-pointer decay

ProcessInitializer() strips implicit casts from a CXXCtorInitializer's
init expression via IgnoreImplicit(), then decides whether to treat
the initializer as a direct array-to-array member copy by checking
Init->getType()->isArrayType(). For a pointer member initialized via
array-to-pointer decay of a reference-to-array constructor parameter
(e.g. `Foo(T (&arr)[N]) : ptr_(arr) {}`), IgnoreImplicit() strips the
ArrayToPointerDecay cast, exposing the underlying array-typed
expression, so this check misfires even though the field itself is a
pointer, not an array. That branch fetches the raw region address of
the whole array, bypassing the normal decay logic (which produces an
ElementRegion), so the pointer member ends up holding the address of
the whole array typed as the array itself, instead of an ElementRegion
at index 0.

Later, dereferencing and storing through that mistyped pointer routes
into RegionStoreManager::bindArray() (instead of bindScalar()), which
unconditionally casts its Init value to nonloc::CompoundVal, asserting
in a debug build and segfaulting in a release build when Init is
anything else, e.g. a nonloc::LocAsInteger produced by round-tripping
a pointer through an integer type.

Fix the actual bug by checking the field's type instead of the
initializer expression's type. Also generalize bindArray()'s existing
guard (added by #178923 for issue #178797) from an enumeration of
specific SVal kinds to the same exhaustive
`!isa<nonloc::CompoundVal>()` check already used by its siblings
bindStruct() and bindVector(), so it doesn't need to be extended again
every time a new SVal kind reaches this path -- this is what actually
catches our case (nonloc::LocAsInteger), which the prior enumeration
didn't cover.

This is the same underlying bug behind #147686 (fixed by #153177,
which its own author noted was "more of a workaround") and #178797
(fixed by #178923); both those fixes patched symptoms at bindArray()
without addressing the ProcessInitializer() root cause. Fixing the
root cause also resolves two FIXME-annotated precision gaps in
clang/test/Analysis/initializer.cpp's gh147686 regression test.

Fixes #210183
---
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |  2 +-
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp |  8 ++++-
 clang/test/Analysis/initializer.cpp           |  8 ++---
 clang/test/Analysis/issue-210183.cpp          | 34 +++++++++++++++++++
 4 files changed, 45 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/Analysis/issue-210183.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 2d86c779ba850..678b07ebba1b0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1196,7 +1196,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer 
CFGInit,
       }
 
       SVal InitVal;
-      if (Init->getType()->isArrayType()) {
+      if (Field->getType()->isArrayType()) {
         // Handle arrays of trivial type. We can represent this with a
         // primitive load/copy from the base array region.
         const ArraySubscriptExpr *ASE;
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 0f7e03ce50858..9ddcf9aebee6f 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2726,7 +2726,13 @@ 
RegionStoreManager::bindArray(LimitedRegionBindingsConstRef B,
     return bindAggregate(B, R, Init);
   }
 
-  if (isa<nonloc::SymbolVal, UnknownVal, UndefinedVal>(Init))
+  // We may get non-CompoundVal accidentally due to imprecise cast logic or
+  // that we are binding a genuinely symbolic/unknown/undefined array value.
+  // Preserve Init as a default binding rather than lossily converting to
+  // UnknownVal(), and handle every non-CompoundVal case exhaustively (like
+  // bindStruct()/bindVector() do) rather than enumerating specific SVal
+  // kinds one at a time.
+  if (!isa<nonloc::CompoundVal>(Init))
     return bindAggregate(B, R, Init);
 
   // Remaining case: explicit compound values.
diff --git a/clang/test/Analysis/initializer.cpp 
b/clang/test/Analysis/initializer.cpp
index 88758f7c3ac1d..dd0c91b7ead61 100644
--- a/clang/test/Analysis/initializer.cpp
+++ b/clang/test/Analysis/initializer.cpp
@@ -628,8 +628,7 @@ struct A {
 void test1() {
   A a;
   clang_analyzer_eval(a.m_buf[0] == 0); // expected-warning{{TRUE}}
-  // FIXME The next eval should result in TRUE.
-  clang_analyzer_eval(*a.m_ptr == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(*a.m_ptr == 0); // expected-warning{{TRUE}}
 }
 
 void test2() {
@@ -646,9 +645,8 @@ void test3() {
 
 void test3Bis(char arg) {
   A a(arg);
-  // FIXME This test should behave like test3.
-  clang_analyzer_eval(a.m_buf[0] == arg); // expected-warning{{FALSE}} // 
expected-warning{{TRUE}}
-  clang_analyzer_eval(*a.m_ptr == arg); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.m_buf[0] == arg); // expected-warning{{TRUE}}
+  clang_analyzer_eval(*a.m_ptr == arg); // expected-warning{{TRUE}}
 }
 
 void test4(char arg) {
diff --git a/clang/test/Analysis/issue-210183.cpp 
b/clang/test/Analysis/issue-210183.cpp
new file mode 100644
index 0000000000000..ba443aa82ba53
--- /dev/null
+++ b/clang/test/Analysis/issue-210183.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection 
-std=c++17 -verify %s
+
+// https://github.com/llvm/llvm-project/issues/210183
+//
+// A pointer member initialized via array-to-pointer decay of a
+// reference-to-array constructor parameter used to be modeled as the
+// address of the whole array (instead of its first element). Dereferencing
+// and storing through that mistyped pointer then reached
+// RegionStoreManager::bindArray() with a scalar Init value, crashing on an
+// unchecked castAs<nonloc::CompoundVal>().
+
+template <class T> void clang_analyzer_dump(T);
+
+template <class T> struct Span {
+  template <int N>
+  Span(T (&arr)[N]) : ptr_(arr) {}
+  T *data() { return ptr_; }
+  T *ptr_;
+};
+
+char *ptr();
+char buffer[10];
+
+void test() {
+  char *p = Span<char>(buffer).data();
+
+  // p and buffer must resolve to the same address: the first element of
+  // buffer, not the whole array.
+  clang_analyzer_dump(buffer); // expected-warning{{&Element{buffer,0 
S64b,char}}}
+  clang_analyzer_dump(p);      // expected-warning{{&Element{buffer,0 
S64b,char}}}
+
+  int v = (int)(long)ptr();
+  *p = v; // no-crash
+}

>From 056f2fbdb5d73a16f6c5ff2b4569a73560100f8a Mon Sep 17 00:00:00 2001
From: Andy Ames <[email protected]>
Date: Mon, 20 Jul 2026 09:41:54 -0700
Subject: [PATCH 2/3] [analyzer] Assert the expected non-CompoundVal kinds in
 bindArray

Per review feedback from @steakhal: the non-CompoundVal fallback path in
bindArray is only reached for SymbolVal/UnknownVal/UndefinedVal today, and
that invariant is exactly what led to discovering this crash in the first
place. Assert it explicitly so a future regression is caught immediately
rather than silently falling back to bindAggregate with the wrong kind of
value.
---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 9ddcf9aebee6f..01c792a9011f9 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2732,8 +2732,10 @@ 
RegionStoreManager::bindArray(LimitedRegionBindingsConstRef B,
   // UnknownVal(), and handle every non-CompoundVal case exhaustively (like
   // bindStruct()/bindVector() do) rather than enumerating specific SVal
   // kinds one at a time.
-  if (!isa<nonloc::CompoundVal>(Init))
+  if (!isa<nonloc::CompoundVal>(Init)) {
+    assert((isa<nonloc::SymbolVal, UnknownVal, UndefinedVal>(Init)));
     return bindAggregate(B, R, Init);
+  }
 
   // Remaining case: explicit compound values.
   const nonloc::CompoundVal& CV = Init.castAs<nonloc::CompoundVal>();

>From 12ecaf8485325826be118f9a66e2f166b87dd56b Mon Sep 17 00:00:00 2001
From: Andy Ames <[email protected]>
Date: Tue, 21 Jul 2026 07:45:26 -0700
Subject: [PATCH 3/3] Update clang/test/Analysis/issue-210183.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Balázs Benics <[email protected]>
---
 clang/test/Analysis/issue-210183.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/issue-210183.cpp 
b/clang/test/Analysis/issue-210183.cpp
index ba443aa82ba53..a9c2ff24bccb8 100644
--- a/clang/test/Analysis/issue-210183.cpp
+++ b/clang/test/Analysis/issue-210183.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection 
-std=c++17 -verify %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux-gnu 
-analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s
 
 // https://github.com/llvm/llvm-project/issues/210183
 //

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

Reply via email to