Skip to content

Restrict the maximum nesting level in the parser to avoid stack overflows #1030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 28, 2022

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Oct 26, 2022

The basic idea is that we start a new nesting level whenever we consume or synthesize an opening bracket (<, {, (, [ or #if) and reduce it when we read the matching closing bracket. This should be a pretty simple indicator for the current nesting level that doesn’t require custom logins in productions. IMO the benefit here is that this notion of nesting level is easy to describe and explain.

Productions that are known to be able to overflow (I just took the ones that introduce StructureMarkerRAII in the C++ parser) can check remainingTokensIfMaximumNestingLevelReached and stop parsing if the maximum nesting level has been exceeded.

It turns out that a nesting level of 256 (which is the current maximum nesting level in the C++ parser) is totally fine in Release builds of the parser, but debug builds fail with nesting levels > 30-ish.


Since lookahead cannot really bail out to prevent stack overflow, I rewrote skipSingle and skipUntil to work iteratively instead of recursively.

@ahoppen ahoppen requested review from DougGregor and CodaFi October 26, 2022 23:20
@ahoppen
Copy link
Member Author

ahoppen commented Oct 26, 2022

@swift-ci Please test

@ahoppen
Copy link
Member Author

ahoppen commented Oct 27, 2022

@swift-ci Please test macOS

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome, thank you!


/// A default maximum nesting level that is used if the client didn't
/// explicitly specify one.
public static let defaultMaximumNestingLevel = 256
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's terrible, but should we do some kind of #if DEBUG check to decide whether to use the lower limit or higher limit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s actually a good idea. The entire check is pretty horrible in itself, so a #if DEBUG check doesn’t make a huge difference anymore.

@@ -13,6 +13,27 @@
@_spi(RawSyntax) import SwiftSyntax

extension Parser {
/// Consumes and returns all remaining tokens in the source file.
mutating func consumeRemaingingTokens() -> [RawSyntax] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo Remainging


/// If the maximum nesting level has been reached, return the remaining tokens in the source file
/// as unexpected nodes that have the `isMaximumNestingLevelOverflow` bit set.
/// Check this in places that are likley to cause deep recursion and if this returns non-nil, abort parsing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo likley

|| fileURL.absoluteString.contains("_overflow")
|| fileURL.absoluteString.contains("parser-cutoff.swift")
}
name: "Swift tests", path: testDir, checkDiagnostics: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so fantastic, thank you!

@ahoppen ahoppen force-pushed the ahoppen/prevent-stack-overflow branch from d323cbb to c3c7599 Compare October 27, 2022 20:15
@ahoppen
Copy link
Member Author

ahoppen commented Oct 27, 2022

@swift-ci Please test

@ahoppen ahoppen force-pushed the ahoppen/prevent-stack-overflow branch from c3c7599 to 213ddc6 Compare October 27, 2022 23:25
@ahoppen
Copy link
Member Author

ahoppen commented Oct 27, 2022

@swift-ci Please test

@ahoppen ahoppen merged commit a78012d into swiftlang:main Oct 28, 2022
@ahoppen ahoppen deleted the ahoppen/prevent-stack-overflow branch October 28, 2022 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants