https://github.com/akash-manna-sky created
https://github.com/llvm/llvm-project/pull/226899
Fixes #173728
An array of zero-sized elements like `struct T {} s[-sizeof(0)][0]` slipped
past Sema's size check, because that check only looks at the total size in
bytes, and zero times anything is zero. So we ended up with an array of 2^64 -
4 elements. The constant evaluator, which runs on this code on its own (e.g.
`isEvaluatable` in codegen or range checks for `-W` warnings), then tried to
default-construct it: the element count got truncated to `unsigned` and we
either ran out of memory allocating an `APValue` per element, or hit the
"bounds check failed for in-bounds index" assertion in `adjustIndex` first.
This goes back to at least Clang 3.4.
Sema now checks the element count against the same limit as the byte size, so
these arrays are rejected with the usual "array is too large" error, like GCC
does. For counts that are still allowed, the array paths in
`VisitCXXConstructExpr` and `VisitArrayInitLoopExpr` now go through the
existing `CheckArraySize` guard, the same one used for `new` and array
destruction, so evaluation just gives up instead of trying to build billions of
elements.
>From 7fa7a3ec8b0f286948cc4369196a2da1d93d77da Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 28 Sep 2026 12:31:57 +0530
Subject: [PATCH] [clang] Fix crash constant-evaluating huge arrays of
zero-sized elements
The array size limit in Sema only considered the total size in bytes, so
an array of zero-sized elements such as `T s[-sizeof(0)][0]` was accepted
with any element count. When the constant evaluator later default-
constructed or copied such an array, it truncated the element count to
unsigned and tried to allocate an APValue for every element, running out
of memory, or asserted in SubobjectDesignator::adjustIndex.
Check the element count against the limit as well, and route the array
construction and ArrayInitLoopExpr paths in the evaluator through the
existing CheckArraySize guard.
Fixes #173728
---
clang/docs/ReleaseNotes.md | 4 ++
clang/lib/AST/ExprConstant.cpp | 4 ++
clang/lib/Sema/SemaType.cpp | 14 +++---
clang/test/CodeGenCXX/stmtexpr.cpp | 5 +++
clang/test/Sema/array-size-64.c | 7 +++
.../cxx2a-constexpr-dynalloc-limits.cpp | 34 +++++++++++++++
clang/test/SemaCXX/zero-length-arrays.cpp | 43 ++++++++++++++++++-
7 files changed, 103 insertions(+), 8 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f6..9e1006db2c423b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -792,6 +792,10 @@ features cannot lower the translation-unit ABI level;
that was inherited from a different declarator, for example when
``__typeof__`` resolves to the type of another, already-processed
declaration. (#GH217489)
+- Fixed a crash when constant-evaluating a default-constructed or copied local
+ array with a huge number of zero-sized elements, e.g. ``T s[N][0]``. Such
+ arrays are now also diagnosed as too large when their element count exceeds
+ the limit that already applies to their size in bytes. (#GH173728)
- Fixed an assertion failure when instantiating a block that captures
`this` via a member access through a dependent base class.
- Fixed `DiagnoseUnguardedAvailability::TraverseIfStmt` dereferencing a nullptr
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2df754dc9007f2..5e3cfb6edba85d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15860,6 +15860,8 @@ bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const
ArrayInitLoopExpr *E) {
return false;
auto *CAT = cast<ConstantArrayType>(E->getType()->castAsArrayTypeUnsafe());
+ if (!CheckArraySize(Info, CAT, E->getExprLoc()))
+ return false;
uint64_t Elements = CAT->getZExtSize();
Result = APValue(APValue::UninitArray(), Elements, Elements);
@@ -15906,6 +15908,8 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const
CXXConstructExpr *E,
bool HadZeroInit = Value->hasValue();
if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(Type)) {
+ if (!CheckArraySize(Info, CAT, E->getExprLoc()))
+ return false;
unsigned FinalSize = CAT->getZExtSize();
// Preserve the array filler if we had prior zero-initialization.
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index b5c71d72a23ff6..85139ec4dd1455 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2307,12 +2307,14 @@ QualType Sema::BuildArrayType(QualType T,
ArraySizeModifier ASM,
return QualType();
}
- // Is the array too large?
- unsigned ActiveSizeBits =
- (!T->isDependentType() && !T->isVariablyModifiedType() &&
- !T->isIncompleteType() && !T->isUndeducedType())
- ? ConstantArrayType::getNumAddressingBits(Context, T, ConstVal)
- : ConstVal.getActiveBits();
+ // Is the array too large? Check the element count too, for zero-sized
+ // elements.
+ unsigned ActiveSizeBits = ConstVal.getActiveBits();
+ if (!T->isDependentType() && !T->isVariablyModifiedType() &&
+ !T->isIncompleteType() && !T->isUndeducedType())
+ ActiveSizeBits = std::max(
+ ActiveSizeBits,
+ ConstantArrayType::getNumAddressingBits(Context, T, ConstVal));
if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context)) {
Diag(ArraySize->getBeginLoc(), diag::err_array_too_large)
<< toString(ConstVal, 10, ConstVal.isSigned(),
diff --git a/clang/test/CodeGenCXX/stmtexpr.cpp
b/clang/test/CodeGenCXX/stmtexpr.cpp
index 6e19ce864813f6..ff3802b417c1c4 100644
--- a/clang/test/CodeGenCXX/stmtexpr.cpp
+++ b/clang/test/CodeGenCXX/stmtexpr.cpp
@@ -78,6 +78,11 @@ int foo5(bool b) {
G: return y;
}
+// CHECK-LABEL: define{{.*}} i32 @gh173728()
+extern "C" int gh173728() {
+ return ({ struct T {} s[0xFFFFFFFFu][0]; 0; });
+}
+
// When we emit a full expression with cleanups that contains branches out of
// the full expression, the result of the inner expression (the call to
// call_with_cleanups in this case) may not dominate the fallthrough
destination
diff --git a/clang/test/Sema/array-size-64.c b/clang/test/Sema/array-size-64.c
index 3e6339bd6a640b..1a0c1923ab3f5c 100644
--- a/clang/test/Sema/array-size-64.c
+++ b/clang/test/Sema/array-size-64.c
@@ -10,3 +10,10 @@ void pr8256(void) {
typedef char b[(long long)sizeof(a)-1];
}
+void gh173728(void) {
+ struct S {} a[-sizeof(0)][0]; // expected-error {{array is too large}}
+ int b[1ULL << 61][0]; // expected-error {{array is too large}}
+ int c[(1ULL << 61) - 1][0];
+ int d[1ULL << 40][0];
+}
+
diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp
b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp
index 7537b47780aeb6..73ce6d14108f38 100644
--- a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp
+++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp
@@ -99,3 +99,37 @@ void ohno() {
}
}
+
+namespace GH173728 {
+struct T {};
+
+template <auto N>
+constexpr int default_construct() {
+ T s[N][0]; // #gh173728-construct
+ return 0;
+}
+
+template <auto N>
+constexpr int capture_copy() {
+ T s[N][0] = {};
+ return [s] { return 0; }(); // #gh173728-capture
+}
+
+static_assert(default_construct<4>() == 0);
+static_assert(capture_copy<4>() == 0);
+
+static_assert(default_construct<1025>() == 0); // expected-error {{static
assertion expression is not an integral constant expression}} \
+ // expected-note {{in call}}
+// expected-note@#gh173728-construct {{cannot allocate array; evaluated array
bound 1025 exceeds the limit (1024)}}
+// expected-note@#gh173728-construct {{use -fconstexpr-steps}}
+
+#if __SIZEOF_SIZE_T__ == 8
+static_assert(default_construct<(1ULL << 33) - 1>() == 0); // expected-error
{{static assertion expression is not an integral constant expression}} \
+ // expected-note
{{in call}}
+// expected-note@#gh173728-construct {{cannot allocate array; evaluated array
bound 8589934591 is too large}}
+
+static_assert(capture_copy<(1ULL << 33) - 1>() == 0); // expected-error
{{static assertion expression is not an integral constant expression}} \
+ // expected-note {{in
call}}
+// expected-note@#gh173728-capture {{cannot allocate array; evaluated array
bound 8589934591 is too large}}
+#endif
+}
diff --git a/clang/test/SemaCXX/zero-length-arrays.cpp
b/clang/test/SemaCXX/zero-length-arrays.cpp
index 6bfc7a5fd2e352..af1b61d4c0dd52 100644
--- a/clang/test/SemaCXX/zero-length-arrays.cpp
+++ b/clang/test/SemaCXX/zero-length-arrays.cpp
@@ -29,8 +29,6 @@ void testBar() {
Bar b2(b);
#if __cplusplus >= 201103L
// expected-error@-2 {{call to implicitly-deleted copy constructor of 'Bar}}
-#else
-// expected-no-diagnostics
#endif
b = b2;
}
@@ -48,3 +46,44 @@ void test () {
}
#endif
}
+
+namespace GH173728 {
+#if __SIZEOF_SIZE_T__ == 8
+int reduced() {
+ int i;
+ return ({
+ struct T {
+ } s[-sizeof(0)][0 == sizeof(i < 0)]; // expected-error {{array is too
large}}
+ 0;
+ });
+}
+
+int original() {
+ int i = 0;
+ return 1 + ({
+ struct tree_el {
+ int val;
+ struct tree_el **right, *left;
+ } state_t[1 + -(sizeof(0x1c))][0 == sizeof(sizeof(i))]; // expected-error
{{array is too large}}
+ 0x97 < 10000;
+ });
+}
+
+int too_large() {
+ return 1 + ({ struct T {} s[(1ULL << 33) - 1][0]; 0x97 < 10000; });
+}
+
+signed char too_large_no_fold() {
+ return ({ struct T {} s[(1ULL << 33) - 1][0]; 1000; });
+}
+#endif
+
+int over_limit() {
+ return 1 + ({ struct T {} s[0xFFFFFFFFu][0]; 0x97 < 10000; });
+}
+
+void small() {
+ signed char a = ({ struct T {} s[4]; 1000; }); // expected-warning
{{implicit conversion from 'int' to 'signed char' changes value from 1000 to
-24}}
+ signed char b = ({ struct T {} s[4][0]; 1000; }); // expected-warning
{{implicit conversion from 'int' to 'signed char' changes value from 1000 to
-24}}
+}
+}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits