[clang] [clang] Prevent stack overflow on deeply nested template arguments (PR #224393)
Daniel Petrovic via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 18 00:18:11 PDT 2026
https://github.com/daniel-petrovic updated https://github.com/llvm/llvm-project/pull/224393
>From 619ebfadb1e784b1b8f70c3a71b2da46735250d7 Mon Sep 17 00:00:00 2001
From: Daniel Petrovic <daniel-dev at hotmail.de>
Date: Thu, 17 Sep 2026 21:44:57 +0200
Subject: [PATCH] [clang] Prevent stack overflow on deeply nested template
arguments
Limit template argument nesting and diagnose before exhausting the
parser stack.
Fixes #224114
---
clang/docs/ReleaseNotes.md | 4 ++
clang/docs/UsersManual.md | 5 +++
.../clang/Basic/DiagnosticParseKinds.td | 6 +++
clang/include/clang/Parse/Parser.h | 8 ++++
clang/lib/Parse/ParseTemplate.cpp | 39 +++++++++++++++++++
clang/test/Parser/template-argument-depth.cpp | 27 +++++++++++++
6 files changed, 89 insertions(+)
create mode 100644 clang/test/Parser/template-argument-depth.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 18d2895531ea60..acd0b6158e64ad 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -600,6 +600,10 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when a using-declaration naming an unresolvable member of a
dependent base was shadowed by an invalid using-declaration. (#GH209427)
+- Fixed a stack overflow (segfault) when parsing deeply nested template
+ arguments such as ``S<S<S<...>>>``. The parser now rejects nesting deeper
+ than ``-ftemplate-depth`` with a diagnostic. (#GH224114)
+
- Fixed a CTAD bug when combining with concepts. (#GH124715)
- Fixed a regression where an internal-linkage function (e.g. a `static` or
diff --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 3bec20612c0489..4b21d1137c83bc 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -4446,6 +4446,11 @@ The default is 1048576, and the limit can be disabled with `-fconstexpr-steps=0`
Sets the limit for recursively nested template instantiations to N. The
default is 1024.
+
+This option also limits how deeply template arguments may be nested in the
+source (e.g. the number of levels in ``S<S<S<...>>>``). Nesting deeper than
+the limit is rejected with a diagnostic instead of overflowing the parser's
+stack.
:::
:::{option} -foperator-arrow-depth=N
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 594d158d25e928..bc5c58363a36ad 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -675,6 +675,12 @@ def err_bracket_depth_exceeded : Error<
"bracket nesting level exceeded maximum of %0">, DefaultFatal;
def note_bracket_depth : Note<
"use -fbracket-depth=N to increase maximum nesting level">;
+def err_template_argument_depth_exceeded : Error<
+ "template argument nesting depth exceeded maximum of %0">, DefaultFatal;
+def err_template_argument_stack_exhausted : Error<
+ "stack nearly exhausted while parsing nested template arguments">, DefaultFatal;
+def note_template_argument_depth : Note<
+ "use -ftemplate-depth=N to increase maximum template argument nesting depth">;
def err_misplaced_ellipsis_in_declaration : Error<
"'...' must %select{immediately precede declared identifier|"
"be innermost component of anonymous pack declaration}0">;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6913c42884a367..27fe45f013841d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7922,6 +7922,14 @@ class Parser : public CodeCompletionHandler {
/// The "depth" of the template parameters currently being parsed.
unsigned TemplateParameterDepth;
+ /// The current nesting depth of template argument lists being parsed. Each
+ /// level of nested template-ids (e.g. `A<B<C<...>>>`) recurses through the
+ /// parser's type/template disambiguation machinery, so without a limit the
+ /// parser's stack would overflow on deeply nested template arguments. This
+ /// is used to enforce a maximum nesting depth analogous to -ftemplate-depth
+ /// (see https://github.com/llvm/llvm-project/issues/224114).
+ unsigned TemplateArgumentDepth = 0;
+
/// RAII class that manages the template parameter depth.
class TemplateParameterDepthRAII {
unsigned &Depth;
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 1e5aa55338309e..5d0949128eb63c 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/DiagnosticParse.h"
+#include "clang/Basic/Stack.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
@@ -22,6 +23,23 @@
#include "clang/Sema/Scope.h"
using namespace clang;
+namespace {
+/// RAII class that manages the template argument nesting depth. Recursing into
+/// a nested template-id (e.g. the inner `S<...>` of `S<S<...>>`) dips through
+/// the parser's type/template disambiguation machinery, so deeply nested
+/// template arguments could overflow the parser's stack. The depth is tracked
+/// so that `Parser::ParseTemplateArgumentList` can enforce a maximum.
+class TemplateArgumentDepthRAII {
+ unsigned &Depth;
+
+public:
+ explicit TemplateArgumentDepthRAII(unsigned &Depth) : Depth(Depth) {
+ ++Depth;
+ }
+ ~TemplateArgumentDepthRAII() { --Depth; }
+};
+} // namespace
+
unsigned Parser::ReenterTemplateScopes(MultiParseScope &S, Decl *D) {
return Actions.ActOnReenterTemplateScope(D, [&] {
S.Enter(Scope::TemplateParamScope);
@@ -1384,6 +1402,27 @@ bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
ColonProtectionRAIIObject ColonProtection(*this, false);
+ // Nested template-ids (e.g. `S<S<S<...>>>`) recurse through the parser's
+ // type/template disambiguation machinery, which is deeply recursive. Bound
+ // the nesting depth to avoid overflowing the parser's stack (see
+ // https://github.com/llvm/llvm-project/issues/224114). The bounds follow
+ // -ftemplate-depth when available, and additionally bail out if we're
+ // genuinely low on stack space (which keeps even huge -ftemplate-depth
+ // overrides from overflowing the stack).
+ TemplateArgumentDepthRAII Depth(TemplateArgumentDepth);
+ bool DepthLimitExceeded =
+ TemplateArgumentDepth > getLangOpts().InstantiationDepth;
+ if (DepthLimitExceeded || isStackNearlyExhausted()) {
+ if (DepthLimitExceeded) {
+ Diag(OpenLoc, diag::err_template_argument_depth_exceeded)
+ << getLangOpts().InstantiationDepth;
+ Diag(OpenLoc, diag::note_template_argument_depth);
+ } else {
+ Diag(OpenLoc, diag::err_template_argument_stack_exhausted);
+ }
+ return true;
+ }
+
auto RunSignatureHelp = [&] {
if (!Template)
return QualType();
diff --git a/clang/test/Parser/template-argument-depth.cpp b/clang/test/Parser/template-argument-depth.cpp
new file mode 100644
index 00000000000000..a8eabf15dcde6c
--- /dev/null
+++ b/clang/test/Parser/template-argument-depth.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -ftemplate-depth=64 -DRAISED %s
+// RUN: not %clang_cc1 -fsyntax-only -std=c++20 -ftemplate-depth=16 -DFAIL %s 2>&1 | FileCheck %s
+//
+// Parsing deeply nested template argument lists such as S<S<S<...>>> used to
+// overflow the parser's stack and crash (see
+// https://github.com/llvm/llvm-project/issues/224114). The parser now enforces
+// a maximum template argument nesting depth controlled by -ftemplate-depth.
+//
+// CHECK: fatal error: template argument nesting depth exceeded maximum of 16
+// CHECK-NOT: stack nearly exhausted
+// expected-no-diagnostics
+
+template <typename T> struct S {};
+
+// This nesting is well within the default -ftemplate-depth limit.
+using NoError = S<S<S<S<S<int>>>>>;
+
+#ifdef FAIL
+// Nesting deeper than -ftemplate-depth=16 must be rejected, not crash.
+using Trigger = S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<int>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
+#endif
+
+#ifdef RAISED
+// Raising -ftemplate-depth increases the parser's allowance.
+using Raised = S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<int>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;
+#endif
More information about the cfe-commits
mailing list