Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -173,6 +173,9 @@ - ``-Wmisexpect`` warns when the branch weights collected during profiling conflict with those added by ``llvm.expect``. +- Clang now checks for stack resource exhaustion when recursively parsing + declarators in order to give a diagnostic before we run out of stack space. + This fixes `Issue 51642 `_. Non-comprehensive list of changes in this release ------------------------------------------------- Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -791,6 +791,16 @@ return PP.LookAhead(N-1); } + /// Warn that the stack is nearly exhausted. + void warnStackExhausted(SourceLocation Loc); + + /// Run some code with "sufficient" stack space. (Currently, at least 256K is + /// guaranteed). Produces a warning if we're low on stack space and allocates + /// more in that case. Use this in code that may recurse deeply (for example, + /// in template instantiation) to avoid stack overflow. + void runWithSufficientStackSpace(SourceLocation Loc, + llvm::function_ref Fn); + public: /// NextToken - This peeks ahead one token and returns it without /// consuming it. Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -5751,11 +5751,12 @@ } /// ParseDeclarator - Parse and verify a newly-initialized declarator. -/// void Parser::ParseDeclarator(Declarator &D) { /// This implements the 'declarator' production in the C grammar, then checks /// for well-formedness and issues diagnostics. - ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator); + runWithSufficientStackSpace(D.getBeginLoc(), [&] { + ParseDeclaratorInternal(D, &Parser::ParseDirectDeclarator); + }); } static bool isPtrOperatorToken(tok::TokenKind Kind, const LangOptions &Lang, @@ -5866,7 +5867,9 @@ D.ExtendWithDeclSpec(DS); // Recurse to parse whatever is left. - ParseDeclaratorInternal(D, DirectDeclParser); + runWithSufficientStackSpace(D.getBeginLoc(), [&] { + ParseDeclaratorInternal(D, DirectDeclParser); + }); // Sema will have to catch (syntactically invalid) pointers into global // scope. It has to catch pointers into namespace scope anyway. @@ -5915,7 +5918,8 @@ D.ExtendWithDeclSpec(DS); // Recursively parse the declarator. - ParseDeclaratorInternal(D, DirectDeclParser); + runWithSufficientStackSpace( + D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); }); if (Kind == tok::star) // Remember that we parsed a pointer type, and remember the type-quals. D.AddTypeInfo(DeclaratorChunk::getPointer( @@ -5960,7 +5964,8 @@ } // Recursively parse the declarator. - ParseDeclaratorInternal(D, DirectDeclParser); + runWithSufficientStackSpace( + D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); }); if (D.getNumTypeObjects() > 0) { // C++ [dcl.ref]p4: There shall be no references to references. Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -15,6 +15,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/Stack.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" @@ -2610,6 +2611,19 @@ return false; } +void Parser::warnStackExhausted(SourceLocation Loc) { + // Only warn about this once. + if (!Actions.WarnedStackExhausted) { + Diag(Loc, diag::warn_stack_exhausted); + Actions.WarnedStackExhausted = true; + } +} + +void Parser::runWithSufficientStackSpace(SourceLocation Loc, + llvm::function_ref Fn) { + clang::runWithSufficientStackSpace([&] { warnStackExhausted(Loc); }, Fn); +} + bool BalancedDelimiterTracker::diagnoseOverflow() { P.Diag(P.Tok, diag::err_bracket_depth_exceeded) << P.getLangOpts().BracketDepth; Index: clang/test/Parser/declarators-recursion-crash.c =================================================================== --- /dev/null +++ clang/test/Parser/declarators-recursion-crash.c @@ -0,0 +1,10 @@ +// RUN: not %clang_cc1 %s -fsyntax-only -verify + +#define PTR1 * * * * * * * * * * +#define PTR2 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 +#define PTR3 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 +#define PTR4 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 +#define PTR5 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 +#define PTR6 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 + +int PTR6 q3_var = 0; // expected-warning {{stack nearly exhausted; compilation time may suffer, and crashes due to stack overflow are likely}}