https://github.com/daniel-petrovic created 
https://github.com/llvm/llvm-project/pull/224393

Limit template argument nesting and diagnose before exhausting the parser stack.

Fixes #224114

Assisted by: Gemini

>From 619ebfadb1e784b1b8f70c3a71b2da46735250d7 Mon Sep 17 00:00:00 2001
From: Daniel Petrovic <[email protected]>
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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to