https://github.com/maxmanolov updated https://github.com/llvm/llvm-project/pull/205503
>From 69ea223045f231e9d92673fdf1fd2635d03fc20f Mon Sep 17 00:00:00 2001 From: Max Manolov <[email protected]> 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 804f93cc37e68..71f4527bd084e 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -365,6 +365,7 @@ features cannot lower the translation-unit ABI level; - Fixed a bug where `__func__`, `__PRETTY_FUNCTION__` and `__FUNCTION__` were not resolving to the proper function when inside a lambda return type (#GH211811) - Fixed USR generation for declarations whose signature mentions a class-type non-type template parameter. (#GH212351) +- 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 494748a26f9a781951a6be8675bca3dda425aabd Mon Sep 17 00:00:00 2001 From: Max Manolov <[email protected]> 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 71f4527bd084e..52029d55debff 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -365,7 +365,7 @@ features cannot lower the translation-unit ABI level; - Fixed a bug where `__func__`, `__PRETTY_FUNCTION__` and `__FUNCTION__` were not resolving to the proper function when inside a lambda return type (#GH211811) - Fixed USR generation for declarations whose signature mentions a class-type non-type template parameter. (#GH212351) -- 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 bce811eed283b..700fd8c60f78e 100644 --- a/clang/docs/UsersManual.md +++ b/clang/docs/UsersManual.md @@ -4384,6 +4384,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 3d63b9677e4df..1a5beb936f9fe 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -391,6 +391,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 1478624265f79..2990e522beab7 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -1655,6 +1655,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 94f9a26aac39f..b0db66fdaea0d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6845,6 +6845,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 30ffd103df7e8d8be02a8424ea9d8261fb642a71 Mon Sep 17 00:00:00 2001 From: Max Manolov <[email protected]> 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 76d7f40bae133fbee559935cf6cefe1436114dcd Mon Sep 17 00:00:00 2001 From: Max Manolov <[email protected]> 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]; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
