aaron.ballman created this revision. aaron.ballman added reviewers: erichkeane, rsmith, rjmccall. Herald added a project: All. aaron.ballman requested review of this revision. Herald added a project: clang.
With sufficiently tortured code, it's possible to cause a stack overflow when parsing declarators. Thus, we now check for resource exhaustion when recursively parsing declarators so that we can at least warn the user we're about to crash before we actually crash. This fixes https://github.com/llvm/llvm-project/issues/51642. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D124915 Files: clang/docs/ReleaseNotes.rst clang/include/clang/Parse/Parser.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/Parser.cpp clang/test/Parser/declarators-recursion-crash.c
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}} 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<void()> Fn) { + clang::runWithSufficientStackSpace([&] { warnStackExhausted(Loc); }, Fn); +} + bool BalancedDelimiterTracker::diagnoseOverflow() { P.Diag(P.Tok, diag::err_bracket_depth_exceeded) << P.getLangOpts().BracketDepth; 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/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<void()> Fn); + public: /// NextToken - This peeks ahead one token and returns it without /// consuming it. 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 <https://github.com/llvm/llvm-project/issues/51642>`_. Non-comprehensive list of changes in this release -------------------------------------------------
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits