[clang] [clang][Sema] Avoid out-of-memory crash on huge designated initializer indices (PR #205503)
Max Manolov via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 11 02:50:01 PDT 2026
https://github.com/maxmanolov updated https://github.com/llvm/llvm-project/pull/205503
>From 6ff1640a20a95d685e05b2173302417dada03212 Mon Sep 17 00:00:00 2001
From: Max Manolov <max.manolov at icloud.com>
Date: Wed, 24 Jun 2026 01:29:40 -0700
Subject: [PATCH 1/4] [clang][Sema] Avoid out-of-memory crash on huge
designated initializer indices
---
clang/docs/ReleaseNotes.md | 1 +
clang/lib/Sema/SemaInit.cpp | 19 +++++++++++++++++++
clang/test/Sema/designated-initializers.c | 19 +++++++++++++++++++
3 files changed, 39 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d9b9c92950c98..072dfdd538025 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -391,6 +391,7 @@ features cannot lower the translation-unit ABI level;
- Fixed an assertion crash when instantiating a nested requirement with an invalid constraint. (#GH213575)
- Clang now defines the GCC-compatible predefined macro `__SIG_ATOMIC_TYPE__`. (#GH213895)
- Fixed a bug where a stray closing curley brace in an OpenMP/OpenACC pragma could cause pragma parsing issues when inside of a member function. (#GH214195)
+- Clang now diagnoses inferred-size arrays with huge designated initializer indices instead of attempting to allocate an enormous initializer list and crashing with an out-of-memory error. (#GH205472)
#### Bug Fixes to Compiler Builtins
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 09d9f1eabd058..929dd58e729e4 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -39,6 +39,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <limits>
using namespace clang;
@@ -3375,6 +3376,24 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
DesignatedEndIndex.setIsUnsigned(true);
}
+ // The semantic form of an initializer list stores one pointer for every
+ // array element, including the elements omitted before a designator. Avoid
+ // creating an initializer list whose dense representation is too large for
+ // an unsigned-sized allocation.
+ constexpr unsigned MaxInitListElements =
+ std::numeric_limits<unsigned>::max() / sizeof(Stmt *);
+ if (DesignatedEndIndex.uge(MaxInitListElements)) {
+ if (!VerifyOnly) {
+ llvm::APSInt NumInits =
+ DesignatedEndIndex.extend(DesignatedEndIndex.getBitWidth() + 1);
+ ++NumInits;
+ SemaRef.Diag(IndexExpr->getBeginLoc(), diag::err_array_too_large)
+ << toString(NumInits, 10) << IndexExpr->getSourceRange();
+ }
+ ++Index;
+ return true;
+ }
+
bool IsStringLiteralInitUpdate =
StructuredList && StructuredList->isStringLiteralInit();
if (IsStringLiteralInitUpdate && VerifyOnly) {
diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c
index 11dc3a2308dee..179b8855fe1c8 100644
--- a/clang/test/Sema/designated-initializers.c
+++ b/clang/test/Sema/designated-initializers.c
@@ -4,6 +4,25 @@ int complete_array_from_init[] = { 1, 2, [10] = 5, 1, 2, [5] = 2, 6 };
int complete_array_from_init_check[((sizeof(complete_array_from_init) / sizeof(int)) == 13)? 1 : -1];
+int normal_sparse_designated_initializer[] = { [3] = 1 };
+typedef char normal_sparse_designated_initializer_size[
+ sizeof(normal_sparse_designated_initializer) / sizeof(int) == 4 ? 1 : -1];
+
+struct LargeDesignatedInitializerPoint {
+ int x, y;
+};
+
+void large_designated_initializer(void) {
+ static struct LargeDesignatedInitializerPoint pts[] = {
+ [0x80000000] = { .x = 10, .y = 20 }, // expected-error {{array is too large (2147483649 elements)}}
+ [0x80000001] = { .x = 30, .y = 40 } // expected-error {{array is too large (2147483650 elements)}}
+ };
+}
+
+int large_fixed_designated_initializer[4] = {
+ [0x80000000] = 1, // expected-error {{array designator index (2147483648) exceeds array bounds (4)}}
+};
+
int iarray[10] = {
[0] = 1,
[1 ... 5] = 2,
>From 3a6832abffa4e499de256221c03aa3f6b96e3de8 Mon Sep 17 00:00:00 2001
From: Max Manolov <max.manolov at icloud.com>
Date: Mon, 20 Jul 2026 14:43:32 -0700
Subject: [PATCH 2/4] Make initializer list element limit configurable
---
clang/docs/ReleaseNotes.md | 2 +-
clang/docs/UsersManual.md | 7 ++++++
clang/include/clang/Basic/LangOptions.def | 2 ++
clang/include/clang/Options/Options.td | 6 +++++
clang/lib/Driver/ToolChains/Clang.cpp | 1 +
clang/lib/Sema/SemaInit.cpp | 25 +++++++++++++------
clang/test/Driver/max-init-list-elements.c | 5 ++++
.../test/Sema/designated-initializer-limit.c | 24 ++++++++++++++++++
clang/test/Sema/designated-initializers.c | 2 ++
9 files changed, 65 insertions(+), 9 deletions(-)
create mode 100644 clang/test/Driver/max-init-list-elements.c
create mode 100644 clang/test/Sema/designated-initializer-limit.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 072dfdd538025..932fe87061fda 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -391,7 +391,7 @@ features cannot lower the translation-unit ABI level;
- Fixed an assertion crash when instantiating a nested requirement with an invalid constraint. (#GH213575)
- Clang now defines the GCC-compatible predefined macro `__SIG_ATOMIC_TYPE__`. (#GH213895)
- Fixed a bug where a stray closing curley brace in an OpenMP/OpenACC pragma could cause pragma parsing issues when inside of a member function. (#GH214195)
-- Clang now diagnoses inferred-size arrays with huge designated initializer indices instead of attempting to allocate an enormous initializer list and crashing with an out-of-memory error. (#GH205472)
+- Clang now diagnoses inferred-size arrays with huge designated initializer indices instead of attempting to allocate an enormous initializer list and crashing with an out-of-memory error. The limit defaults to 1048576 dense semantic initializer-list elements and can be adjusted with `-fmax-init-list-elements=`. (#GH205472)
#### Bug Fixes to Compiler Builtins
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 40834746e1218..dd554dc2701f6 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -4426,6 +4426,13 @@ Sets the limit for iterative calls to 'operator->' functions to N. The
default is 256.
:::
+:::{option} -fmax-init-list-elements=N
+
+Sets the maximum number of elements Clang may materialize for an array
+initializer's dense semantic representation. An array designator with an
+inclusive end index of N requires N + 1 elements. The default is 1048576.
+:::
+
(objc)=
## Objective-C Language Features
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index f945b60b6966d..f84b9c461d456 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -394,6 +394,8 @@ LANGOPT(EnableNewConstInterp, 1, CLANG_USE_EXPERIMENTAL_CONST_INTERP, Benign,
"enable the experimental new constant interpreter")
LANGOPT(BracketDepth, 32, 256, Benign,
"maximum bracket nesting depth")
+VALUE_LANGOPT(MaxInitListElements, 32, 1048576, Benign,
+ "maximum number of elements in a dense semantic initializer list")
LANGOPT(NumLargeByValueCopy, 32, 0, Benign,
"if non-zero, warn about parameter or return Warn if parameter/return value is larger in bytes than this setting. 0 is no check.")
VALUE_LANGOPT(MSCompatibilityVersion, 32, 0, NotCompatible, "Microsoft Visual C/C++ Version")
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index b354a475346df..9b5a9607ce7ed 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1664,6 +1664,12 @@ def emit_sgf_symbol_labels_for_testing: Flag<["--"], "emit-sgf-symbol-labels-for
Visibility<[CC1Option]>,
MarshallingInfoFlag<FrontendOpts<"EmitSymbolGraphSymbolLabelsForTesting">>;
def e : Separate<["-"], "e">, Flags<[LinkerInput]>, Group<Link_Group>;
+def fmax_init_list_elements_EQ :
+ Joined<["-"], "fmax-init-list-elements=">, Group<f_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"Set the maximum number of elements Clang may materialize for an "
+ "array initializer's dense semantic representation">,
+ MarshallingInfoInt<LangOpts<"MaxInitListElements">, "1048576">;
def fmax_tokens_EQ : Joined<["-"], "fmax-tokens=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Max total number of preprocessed tokens for -Wmax-tokens.">,
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index d2e22920aa432..18774fa1a1fed 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6815,6 +6815,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_foperator_arrow_depth_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ);
+ Args.AddLastArg(CmdArgs, options::OPT_fmax_init_list_elements_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fexperimental_library);
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 929dd58e729e4..cdee927962cae 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -39,6 +39,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
#include <limits>
using namespace clang;
@@ -3377,16 +3378,24 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
}
// The semantic form of an initializer list stores one pointer for every
- // array element, including the elements omitted before a designator. Avoid
- // creating an initializer list whose dense representation is too large for
- // an unsigned-sized allocation.
- constexpr unsigned MaxInitListElements =
+ // array element, including the elements omitted before a designator. Compute
+ // the required number of elements in a wider type so adding one cannot
+ // overflow.
+ llvm::APSInt NumInits = DesignatedEndIndex;
+ NumInits.setIsUnsigned(true);
+ NumInits = NumInits.extend(NumInits.getBitWidth() + 1);
+ ++NumInits;
+
+ // Keep a non-configurable ceiling so even an excessive command-line limit
+ // cannot request an initializer list too large for an unsigned-sized
+ // allocation.
+ constexpr unsigned MaxAllocatableInitListElements =
std::numeric_limits<unsigned>::max() / sizeof(Stmt *);
- if (DesignatedEndIndex.uge(MaxInitListElements)) {
+ const unsigned MaxInitListElements =
+ std::min(SemaRef.getLangOpts().MaxInitListElements,
+ MaxAllocatableInitListElements);
+ if (NumInits.ugt(MaxInitListElements)) {
if (!VerifyOnly) {
- llvm::APSInt NumInits =
- DesignatedEndIndex.extend(DesignatedEndIndex.getBitWidth() + 1);
- ++NumInits;
SemaRef.Diag(IndexExpr->getBeginLoc(), diag::err_array_too_large)
<< toString(NumInits, 10) << IndexExpr->getSourceRange();
}
diff --git a/clang/test/Driver/max-init-list-elements.c b/clang/test/Driver/max-init-list-elements.c
new file mode 100644
index 0000000000000..00fb749b6b0c4
--- /dev/null
+++ b/clang/test/Driver/max-init-list-elements.c
@@ -0,0 +1,5 @@
+// RUN: %clang -### -fsyntax-only -fmax-init-list-elements=4 %s 2>&1 | \
+// RUN: FileCheck %s
+
+// CHECK: "-cc1"
+// CHECK-SAME: "-fmax-init-list-elements=4"
diff --git a/clang/test/Sema/designated-initializer-limit.c b/clang/test/Sema/designated-initializer-limit.c
new file mode 100644
index 0000000000000..c4f3f9cb8938e
--- /dev/null
+++ b/clang/test/Sema/designated-initializer-limit.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown \
+// RUN: -fmax-init-list-elements=4 %s
+
+int designated_at_limit[] = { [3] = 1 };
+_Static_assert(sizeof(designated_at_limit) / sizeof(int) == 4, "");
+
+int range_at_limit[] = { [1 ... 3] = 1 };
+_Static_assert(sizeof(range_at_limit) / sizeof(int) == 4, "");
+
+int designated_over_limit[] = {
+ [4] = 1, // expected-error {{array is too large (5 elements)}}
+};
+
+int range_over_limit[] = {
+ [1 ... 4] = 1, // expected-error {{array is too large (5 elements)}}
+};
+
+int fixed_over_limit[5] = {
+ [4] = 1, // expected-error {{array is too large (5 elements)}}
+};
+
+int fixed_out_of_bounds[4] = {
+ [4] = 1, // expected-error {{array designator index (4) exceeds array bounds (4)}}
+};
diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c
index 179b8855fe1c8..5527c7229a697 100644
--- a/clang/test/Sema/designated-initializers.c
+++ b/clang/test/Sema/designated-initializers.c
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown %s
+// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown \
+// RUN: -fmax-init-list-elements=4294967295 %s
int complete_array_from_init[] = { 1, 2, [10] = 5, 1, 2, [5] = 2, 6 };
>From a5f4aaba51ad4b650a21e8d042f48b732459c539 Mon Sep 17 00:00:00 2001
From: Max Manolov <max.manolov at icloud.com>
Date: Thu, 30 Jul 2026 18:15:43 -0700
Subject: [PATCH 3/4] Remove initializer list allocation ceiling
---
clang/lib/Sema/SemaInit.cpp | 12 +-----------
clang/test/Sema/designated-initializers.c | 3 ---
2 files changed, 1 insertion(+), 14 deletions(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index cdee927962cae..17fb55a9f45b6 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -39,8 +39,6 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-#include <limits>
using namespace clang;
@@ -3386,15 +3384,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
NumInits = NumInits.extend(NumInits.getBitWidth() + 1);
++NumInits;
- // Keep a non-configurable ceiling so even an excessive command-line limit
- // cannot request an initializer list too large for an unsigned-sized
- // allocation.
- constexpr unsigned MaxAllocatableInitListElements =
- std::numeric_limits<unsigned>::max() / sizeof(Stmt *);
- const unsigned MaxInitListElements =
- std::min(SemaRef.getLangOpts().MaxInitListElements,
- MaxAllocatableInitListElements);
- if (NumInits.ugt(MaxInitListElements)) {
+ if (NumInits.ugt(SemaRef.getLangOpts().MaxInitListElements)) {
if (!VerifyOnly) {
SemaRef.Diag(IndexExpr->getBeginLoc(), diag::err_array_too_large)
<< toString(NumInits, 10) << IndexExpr->getSourceRange();
diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c
index 5527c7229a697..0e06e0390a880 100644
--- a/clang/test/Sema/designated-initializers.c
+++ b/clang/test/Sema/designated-initializers.c
@@ -1,7 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown %s
-// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown \
-// RUN: -fmax-init-list-elements=4294967295 %s
-
int complete_array_from_init[] = { 1, 2, [10] = 5, 1, 2, [5] = 2, 6 };
int complete_array_from_init_check[((sizeof(complete_array_from_init) / sizeof(int)) == 13)? 1 : -1];
>From e0123b3410c998f27ebb70168577634fceee0603 Mon Sep 17 00:00:00 2001
From: Max Manolov <max.manolov at icloud.com>
Date: Thu, 30 Jul 2026 19:02:43 -0700
Subject: [PATCH 4/4] Restore designated initializer test spacing
---
clang/test/Sema/designated-initializers.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/test/Sema/designated-initializers.c b/clang/test/Sema/designated-initializers.c
index 0e06e0390a880..179b8855fe1c8 100644
--- a/clang/test/Sema/designated-initializers.c
+++ b/clang/test/Sema/designated-initializers.c
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-unknown %s
+
int complete_array_from_init[] = { 1, 2, [10] = 5, 1, 2, [5] = 2, 6 };
int complete_array_from_init_check[((sizeof(complete_array_from_init) / sizeof(int)) == 13)? 1 : -1];
More information about the cfe-commits
mailing list