Repository navigation
Limit how deep the parser nests rules - #258
Merged
FlorianRappl merged 1 commit intoNov 1, 2026
Merged
Conversation
A style sheet nesting blocks deeply ended the process with a stack overflow, which no caller can catch: the parser recurses once per level of @media, @supports, @layer, @container, @scope and nested style rules. On a 256 KB thread stack, about 125 nested @media rules were enough (.NET 8, before the JIT optimizes); on a 1 MB stack about 580. - Add CssParserOptions.MaxNestingDepth (default 64). A rule nested in more blocks is dropped like an invalid rule, raising the new CssParseError.NestingTooDeep, and parsing continues after it - Add tests at depths that crashed before and at the limit, parsing on a 256 KB thread stack; with a default of 128 the test host crashes - Add a benchmark parsing nested @media and nested style rules This is a mitigation, not a fix: parsing still recurses once per level, and only the limit stops it. Raising the limit brings the crash back.
5 of 9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Types of Changes
Prerequisites
Please make sure you can check the following two boxes:
Contribution Type
What types of changes does your code introduce? Put an
xin all the boxes that apply:Description
The style sheet counterpart of AngleSharp #1402 (selector nesting) and AngleSharp.Xml #42 (element nesting). There is no separate issue.
The CSS parser recurses once per level of nested blocks:
@media,@supports,@layer,@container,@scope,@starting-style,@documentand nested style rules. A deeply nested style sheet overflows the stack, and aStackOverflowExceptioncannot be caught, so the whole process ends. One hostile style sheet is enough.With no limit, the deepest nesting that still parses (a fresh process per case, so before the JIT optimizes):
@media& b { … })The change
CssParserOptions.MaxNestingDepth, default 64 (CssParserOptions.DefaultMaxNestingDepth); 0 uses the default. It counts the blocks a rule is nested in: grouping rules and style rules holding nested style rules.CssParseError.NestingTooDeep, and parsing continues after it.@media, so 128 would not hold there (the tests show it). 64 leaves a 2x margin on such a thread and about 9x on a 1 MB stack. Real style sheets nest a few levels.Parsing/DeepNesting.cs) parse on a thread with a fixed 256 KB stack, as #1402 does: 10,000 levels for each grouping rule and for nested style rules, the limit itself (64 kept, 65 dropped with the error), custom limits, and declarations continuing after a dropped nested rule. Against the unlimited parser the test host crashes, and with a default of 128 it crashes too.DeepStyleSheetBenchmarks: nested@mediaand nested style rules at 10 and 60 levels. Not run yet; I'll post paired results.Mitigation, not a fix: the bug is still present
The limit only stops the input before it gets deep enough to crash. The parser still recurses once per level, so the crash is still there:
A raised limit brings it back. Anyone who sets
MaxNestingDepthhigher, or parses on a thread with a smaller stack, takes the risk back:The XML docs of the option say so, with the numbers above.
Trees built through the DOM are not covered. The HTML parser caps documents at 512 levels, but the DOM API does not. On a deeper tree, rendering (
IWindow.Render),GetInnerText,RenderNodeExtensions.FindandDownloadResourcesstill recurse once per element and overflow the stack at a few thousand levels. Your document-level depth limit from AngleSharp.Xml System.FormatException thown parsing CSS #39 would cover them; I also have explicit-stack versions of those walks if you want them, with benchmarks.Values and conditions (parentheses in
calc()and in@media/@supportsconditions) recurse too; that is a separate, smaller PR after this one.Suggested CHANGELOG line
I left
CHANGELOG.mdout so you can word it; suggestion:MaxNestingDepth) (#PR) @michaelthyregod🤖 Generated with Claude Code