Skip to content

Commit 469a0f3

Browse files
modelorganismrintaro
authored andcommitted
fix stack overflow on deeply nested parens [SR-4866] (swiftlang#19631)
Make sure StructureMarkerRAII checks structure nesting level on all paths. Previously swift crashed with no diagnostic on deeply nested '('. Now we print an error when more than 256 parens deep, just as we always have for '['. fixes SR-4866
1 parent c4b459c commit 469a0f3

File tree

9 files changed

+321
-36
lines changed

9 files changed

+321
-36
lines changed

include/swift/Parse/Parser.h

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -306,30 +306,34 @@ class Parser {
306306

307307
/// An RAII object that notes when we have seen a structure marker.
308308
class StructureMarkerRAII {
309-
Parser &P;
309+
Parser *const P;
310310

311311
/// Max nesting level
312312
// TODO: customizable.
313313
enum { MaxDepth = 256 };
314314

315-
void diagnoseOverflow();
315+
StructureMarkerRAII(Parser *parser) : P(parser) {}
316+
317+
/// Have the parser start the new Structure or fail if already too deep.
318+
bool pushStructureMarker(Parser &parser, SourceLoc loc,
319+
StructureMarkerKind kind);
316320

317321
public:
318-
StructureMarkerRAII(Parser &parser, SourceLoc loc,
319-
StructureMarkerKind kind)
320-
: P(parser) {
321-
P.StructureMarkers.push_back({loc, kind, None});
322-
323-
if (P.StructureMarkers.size() >= MaxDepth) {
324-
diagnoseOverflow();
325-
P.cutOffParsing();
326-
}
327-
}
322+
StructureMarkerRAII(Parser &parser, SourceLoc loc, StructureMarkerKind kind)
323+
: StructureMarkerRAII(
324+
pushStructureMarker(parser, loc, kind) ? &parser : nullptr) {}
328325

329326
StructureMarkerRAII(Parser &parser, const Token &tok);
330327

328+
/// Did we fail to push the new structure?
329+
bool isFailed() {
330+
return P == nullptr;
331+
}
332+
331333
~StructureMarkerRAII() {
332-
P.StructureMarkers.pop_back();
334+
if (P != nullptr) {
335+
P->StructureMarkers.pop_back();
336+
}
333337
}
334338
};
335339
friend class StructureMarkerRAII;

lib/Parse/ParseExpr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2888,6 +2888,10 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok,
28882888
trailingClosure = nullptr;
28892889

28902890
StructureMarkerRAII ParsingExprList(*this, Tok);
2891+
2892+
if (ParsingExprList.isFailed()) {
2893+
return makeParserError();
2894+
}
28912895

28922896
leftLoc = consumeToken(leftTok);
28932897
ParserStatus status = parseList(rightTok, leftLoc, rightLoc,

lib/Parse/ParseGeneric.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc,
5555
// Note that we're parsing a declaration.
5656
StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
5757
StructureMarkerKind::Declaration);
58+
59+
if (ParsingDecl.isFailed()) {
60+
return makeParserError();
61+
}
5862

5963
// Parse attributes.
6064
DeclAttributes attributes;

lib/Parse/ParsePattern.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,9 @@ ParserResult<Pattern> Parser::parsePatternTuple() {
10381038
SyntaxParsingContext TuplePatternCtxt(SyntaxContext,
10391039
SyntaxKind::TuplePattern);
10401040
StructureMarkerRAII ParsingPatternTuple(*this, Tok);
1041+
if (ParsingPatternTuple.isFailed()) {
1042+
return makeParserError();
1043+
}
10411044
SourceLoc LPLoc = consumeToken(tok::l_paren);
10421045
SourceLoc RPLoc;
10431046

lib/Parse/ParseStmt.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,11 @@ ParserResult<PoundAvailableInfo> Parser::parseStmtConditionPoundAvailable() {
12501250
}
12511251

12521252
StructureMarkerRAII ParsingAvailabilitySpecList(*this, Tok);
1253+
1254+
if (ParsingAvailabilitySpecList.isFailed()) {
1255+
return makeParserError();
1256+
}
1257+
12531258
SourceLoc LParenLoc = consumeToken(tok::l_paren);
12541259

12551260
SmallVector<AvailabilitySpec *, 5> Specs;

lib/Parse/ParseType.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,11 @@ ParserResult<TypeRepr> Parser::parseOldStyleProtocolComposition() {
852852
ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() {
853853
SyntaxParsingContext TypeContext(SyntaxContext, SyntaxKind::TupleType);
854854
Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok);
855+
856+
if (ParsingTypeTuple.isFailed()) {
857+
return makeParserError();
858+
}
859+
855860
SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren);
856861
SourceLoc EllipsisLoc;
857862
unsigned EllipsisIdx;

lib/Parse/Parser.cpp

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -757,39 +757,42 @@ bool Parser::parseEndIfDirective(SourceLoc &Loc) {
757757
return false;
758758
}
759759

760-
Parser::StructureMarkerRAII::StructureMarkerRAII(Parser &parser,
761-
const Token &tok)
762-
: P(parser)
763-
{
760+
static Parser::StructureMarkerKind
761+
getStructureMarkerKindForToken(const Token &tok) {
764762
switch (tok.getKind()) {
765763
case tok::l_brace:
766-
P.StructureMarkers.push_back({tok.getLoc(),
767-
StructureMarkerKind::OpenBrace,
768-
None});
769-
break;
770-
764+
return Parser::StructureMarkerKind::OpenBrace;
771765
case tok::l_paren:
772-
P.StructureMarkers.push_back({tok.getLoc(),
773-
StructureMarkerKind::OpenParen,
774-
None});
775-
break;
776-
766+
return Parser::StructureMarkerKind::OpenParen;
777767
case tok::l_square:
778-
P.StructureMarkers.push_back({tok.getLoc(),
779-
StructureMarkerKind::OpenSquare,
780-
None});
781-
break;
782-
768+
return Parser::StructureMarkerKind::OpenSquare;
783769
default:
784770
llvm_unreachable("Not a matched token");
785771
}
786772
}
787773

788-
void Parser::StructureMarkerRAII::diagnoseOverflow() {
789-
auto Loc = P.StructureMarkers.back().Loc;
790-
P.diagnose(Loc, diag::structure_overflow, MaxDepth);
791-
}
774+
Parser::StructureMarkerRAII::StructureMarkerRAII(Parser &parser,
775+
const Token &tok)
776+
: StructureMarkerRAII(parser, tok.getLoc(),
777+
getStructureMarkerKindForToken(tok)) {}
792778

779+
bool Parser::StructureMarkerRAII::pushStructureMarker(
780+
Parser &parser, SourceLoc loc,
781+
StructureMarkerKind kind) {
782+
783+
if (parser.StructureMarkers.size() < MaxDepth) {
784+
parser.StructureMarkers.push_back({loc, kind, None});
785+
return true;
786+
} else {
787+
parser.diagnose(loc, diag::structure_overflow, MaxDepth);
788+
// We need to cut off parsing or we will stack-overflow.
789+
// But `cutOffParsing` changes the current token to eof, and we may be in
790+
// a place where `consumeToken()` will be expecting e.g. '[',
791+
// since we need that to get to the callsite, so this can cause an assert.
792+
parser.cutOffParsing();
793+
return false;
794+
}
795+
}
793796
//===----------------------------------------------------------------------===//
794797
// Primitive Parsing
795798
//===----------------------------------------------------------------------===//
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-frontend -parse %s 2>&1 | %FileCheck -allow-empty %s
2+
3+
// Test that we can have the allowed # of parens.
4+
// CHECK-NOT: error
5+
let a = (((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((
6+
(((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((
7+
(((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((
8+
(((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((
9+
1
10+
)))))))))))))))))))))))))))))) ))))))))))))))))))))))))))))))))
11+
)))))))))))))))))))))))))))))))) ))))))))))))))))))))))))))))))))
12+
)))))))))))))))))))))))))))))))) ))))))))))))))))))))))))))))))))
13+
)))))))))))))))))))))))))))))))) ))))))))))))))))))))))))))))))))
14+
15+
// Test that we can have the allowed # of curly braces.
16+
/// CHECK-NOT: error
17+
let b = {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
18+
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
19+
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
20+
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
21+
1
22+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
23+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
24+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
25+
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
26+
27+
// Test that we can have the allowed # of mixed brackets.
28+
// CHECK-NOT: error
29+
let c = ({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({({
30+
({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({({
31+
({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({({
32+
({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({([ ({({({({ ({({({
33+
1
34+
})})}) })})})}) ])})})}) })})})}) ])})})}) })})})}) ])})})}) })})})})
35+
})})})}) })})})}) ])})})}) })})})}) ])})})}) })})})}) ])})})}) })})})})
36+
})})})}) })})})}) ])})})}) })})})}) ])})})}) })})})}) ])})})}) })})})})
37+
})})})}) })})})}) ])})})}) })})})}) ])})})}) })})})}) ])})})}) })})})})

0 commit comments

Comments
 (0)