Skip to content

Commit 1fd5b4e

Browse files
committed
Error when reaching maximum nesting depth
Avoids a stack overflow panic
1 parent f75c8e2 commit 1fd5b4e

File tree

2 files changed

+19
-9
lines changed

2 files changed

+19
-9
lines changed

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ pub enum ParserError<'i> {
8989
UnexpectedNamespaceRule,
9090
/// An unexpected token was encountered.
9191
UnexpectedToken(#[serde(skip)] Token<'i>),
92+
/// Maximum nesting depth was reached.
93+
MaximumNestingDepth,
9294
}
9395

9496
impl<'i> fmt::Display for ParserError<'i> {
@@ -114,6 +116,7 @@ impl<'i> fmt::Display for ParserError<'i> {
114116
"@namespaces rules must precede all rules aside from @charset, @import, and @layer statements"
115117
),
116118
UnexpectedToken(token) => write!(f, "Unexpected token {:?}", token),
119+
MaximumNestingDepth => write!(f, "Overflowed the maximum nesting depth"),
117120
}
118121
}
119122
}

src/properties/custom.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<'i> CustomProperty<'i> {
3838
input: &mut Parser<'i, 't>,
3939
options: &ParserOptions,
4040
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
41-
let value = TokenList::parse(input, options)?;
41+
let value = TokenList::parse(input, options, 0)?;
4242
Ok(CustomProperty { name, value })
4343
}
4444
}
@@ -65,7 +65,7 @@ impl<'i> UnparsedProperty<'i> {
6565
input: &mut Parser<'i, 't>,
6666
options: &ParserOptions,
6767
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
68-
let value = TokenList::parse(input, options)?;
68+
let value = TokenList::parse(input, options, 0)?;
6969
Ok(UnparsedProperty { property_id, value })
7070
}
7171

@@ -131,10 +131,11 @@ impl<'i> TokenList<'i> {
131131
fn parse<'t>(
132132
input: &mut Parser<'i, 't>,
133133
options: &ParserOptions,
134+
depth: usize,
134135
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
135136
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
136137
let mut tokens = vec![];
137-
TokenList::parse_into(input, &mut tokens, options)?;
138+
TokenList::parse_into(input, &mut tokens, options, depth)?;
138139

139140
// Slice off leading and trailing whitespace if there are at least two tokens.
140141
// If there is only one token, we must preserve it. e.g. `--foo: ;` is valid.
@@ -157,7 +158,12 @@ impl<'i> TokenList<'i> {
157158
input: &mut Parser<'i, 't>,
158159
tokens: &mut Vec<TokenOrValue<'i>>,
159160
options: &ParserOptions,
161+
depth: usize,
160162
) -> Result<(), ParseError<'i, ParserError<'i>>> {
163+
if depth > 500 {
164+
return Err(input.new_custom_error(ParserError::MaximumNestingDepth));
165+
}
166+
161167
let mut last_is_delim = false;
162168
let mut last_is_whitespace = false;
163169
loop {
@@ -189,15 +195,15 @@ impl<'i> TokenList<'i> {
189195
last_is_whitespace = false;
190196
} else if f == "var" {
191197
let var = input.parse_nested_block(|input| {
192-
let var = Variable::parse(input, options)?;
198+
let var = Variable::parse(input, options, depth + 1)?;
193199
Ok(TokenOrValue::Var(var))
194200
})?;
195201
tokens.push(var);
196202
last_is_delim = true;
197203
last_is_whitespace = false;
198204
} else {
199205
tokens.push(Token::Function(f).into());
200-
input.parse_nested_block(|input| TokenList::parse_into(input, tokens, options))?;
206+
input.parse_nested_block(|input| TokenList::parse_into(input, tokens, options, depth + 1))?;
201207
tokens.push(Token::CloseParenthesis.into());
202208
last_is_delim = true; // Whitespace is not required after any of these chars.
203209
last_is_whitespace = false;
@@ -229,7 +235,7 @@ impl<'i> TokenList<'i> {
229235
_ => unreachable!(),
230236
};
231237

232-
input.parse_nested_block(|input| TokenList::parse_into(input, tokens, options))?;
238+
input.parse_nested_block(|input| TokenList::parse_into(input, tokens, options, depth + 1))?;
233239

234240
tokens.push(closing_delimiter.into());
235241
last_is_delim = true; // Whitespace is not required after any of these chars.
@@ -758,11 +764,12 @@ impl<'i> Variable<'i> {
758764
fn parse<'t>(
759765
input: &mut Parser<'i, 't>,
760766
options: &ParserOptions,
767+
depth: usize,
761768
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
762769
let name = DashedIdentReference::parse_with_options(input, options)?;
763770

764771
let fallback = if input.try_parse(|input| input.expect_comma()).is_ok() {
765-
Some(TokenList::parse(input, options)?)
772+
Some(TokenList::parse(input, options, depth)?)
766773
} else {
767774
None
768775
};
@@ -833,15 +840,15 @@ impl<'i> UnresolvedColor<'i> {
833840
input.parse_nested_block(|input| {
834841
let (r, g, b) = parse_rgb_components(input, &parser)?;
835842
input.expect_delim('/')?;
836-
let alpha = TokenList::parse(input, options)?;
843+
let alpha = TokenList::parse(input, options, 0)?;
837844
Ok(UnresolvedColor::RGB { r, g, b, alpha })
838845
})
839846
},
840847
"hsl" => {
841848
input.parse_nested_block(|input| {
842849
let (h, s, l) = parse_hsl_hwb_components(input, &parser)?;
843850
input.expect_delim('/')?;
844-
let alpha = TokenList::parse(input, options)?;
851+
let alpha = TokenList::parse(input, options, 0)?;
845852
Ok(UnresolvedColor::HSL { h, s, l, alpha })
846853
})
847854
},

0 commit comments

Comments
 (0)