[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
Thu Jul 30 18:16:17 PDT 2026
https://github.com/maxmanolov updated https://github.com/llvm/llvm-project/pull/205503
>From 268c083e3fa791cb184989f6a733a0f3580fee85 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 09ec3594ab31f..58c81474e294c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -720,6 +720,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Fixed an assertion where we improperly handled implicit conversions to integral types from an atomic-type with a conversion function. (#GH201770)
- Fixed assertion failures involving code completion with delayed default arguments and exception specifications. (#GH200879)
- Fixed a regression where calling a function that takes a class-type parameter by value inside `decltype` of a concept could be incorrectly rejected when used as a non-type template argument. (#GH175831)
+- 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 dad9c8c972dd9..ba7aeac2dc9ee 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 30865329a0aa5d2c5dd745a745afa351e970a3e4 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 | 6 ++++-
clang/docs/UsersManual.rst | 8 ++++++
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, 70 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 58c81474e294c..f4481cf3729b3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -720,7 +720,11 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Fixed an assertion where we improperly handled implicit conversions to integral types from an atomic-type with a conversion function. (#GH201770)
- Fixed assertion failures involving code completion with delayed default arguments and exception specifications. (#GH200879)
- Fixed a regression where calling a function that takes a class-type parameter by value inside `decltype` of a concept could be incorrectly rejected when used as a non-type template argument. (#GH175831)
-- 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.rst b/clang/docs/UsersManual.rst
index 05a1ab7c2022a..5f0f5df6b8362 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -4241,6 +4241,14 @@ Controlling implementation limits
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.
+ The effective limit is also capped by a non-configurable internal
+ allocation-safety limit.
+
.. _objc:
Objective-C Language Features
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index d68784b7efbcd..77cb25fc70d50 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -389,6 +389,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 c998446ac0a22..6897e2a6fec3a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1601,6 +1601,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 a3a3954bc464e..f0cb8b9060323 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6802,6 +6802,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 ba7aeac2dc9ee..cc6cbc9e12a4e 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 da6f2c39791827b9e0b81bca4098746672c86cd5 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 cc6cbc9e12a4e..2264377f60d6a 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 ee1eacba2105ee8d3f6f0a49094b19c5a0de1d28 Mon Sep 17 00:00:00 2001
From: Max Manolov <max.manolov at icloud.com>
Date: Thu, 30 Jul 2026 18:15:58 -0700
Subject: [PATCH 4/4] Update initializer list limit documentation
---
clang/docs/ReleaseNotes.md | 6 +-----
clang/docs/UsersManual.rst | 2 --
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f4481cf3729b3..598195a05fb75 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -720,11 +720,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Fixed an assertion where we improperly handled implicit conversions to integral types from an atomic-type with a conversion function. (#GH201770)
- Fixed assertion failures involving code completion with delayed default arguments and exception specifications. (#GH200879)
- Fixed a regression where calling a function that takes a class-type parameter by value inside `decltype` of a concept could be incorrectly rejected when used as a non-type template argument. (#GH175831)
-- 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)
+- 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.rst b/clang/docs/UsersManual.rst
index 5f0f5df6b8362..9f58b724db84e 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -4246,8 +4246,6 @@ Controlling implementation limits
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.
- The effective limit is also capped by a non-configurable internal
- allocation-safety limit.
.. _objc:
More information about the cfe-commits
mailing list