Skip to content

Commit

Permalink
fix(parser): apply recursion limit everywhere, reduce default to 500 (#…
Browse files Browse the repository at this point in the history
…662)

The limit was only tracked for nested selection sets, but the parser turns out
to use recursion in other cases too. Issue #666 tracks reducing them.
Stack overflow was observed with little more than 2000
nesting levels or repetitions in the new test.
Defaulting to a quarter of that leaves a comfortable margin.
  • Loading branch information
SimonSapin authored Sep 27, 2023
1 parent e05abbf commit 865be96
Show file tree
Hide file tree
Showing 135 changed files with 427 additions and 173 deletions.
5 changes: 4 additions & 1 deletion crates/apollo-compiler/src/database/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,10 @@ mod tests {
assert_eq!(ast.errors().len(), 1);
assert_eq!(
ast.errors().next(),
Some(&apollo_parser::Error::limit("parser limit(3) reached", 121))
Some(&apollo_parser::Error::limit(
"parser recursion limit reached",
121
))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@
- [email protected] "String"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 2
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 0
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@
- [email protected]
- [email protected] "\"https://tools.ietf.org/html/rfc3986\""
- [email protected] ")"
recursion limit: 4096, high: 0
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@
- [email protected] "ACANA"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@
- [email protected] "SearchResult"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,4 @@
- [email protected] "String"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,4 @@
- [email protected] "!"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@
- [email protected] "Float"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -477,4 +477,4 @@
- [email protected] "}"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 8
recursion limit: 500, high: 8
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 3
2 changes: 1 addition & 1 deletion crates/apollo-compiler/test_data/ok/0014_float_values.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 0
recursion limit: 500, high: 4
2 changes: 1 addition & 1 deletion crates/apollo-compiler/test_data/ok/0015_supergraph.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4200,4 +4200,4 @@
- [email protected] "String"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 7
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,4 @@
- [email protected] "!"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 2
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,4 @@
- [email protected] "Boolean"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 2
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 2
2 changes: 1 addition & 1 deletion crates/apollo-compiler/test_data/ok/0019_extensions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 0
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -541,4 +541,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 1
recursion limit: 500, high: 5
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 1
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@
- [email protected] "}"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 4
recursion limit: 500, high: 4
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@
- [email protected] "__typename"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 1
recursion limit: 500, high: 1
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@
- [email protected] "}"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 2
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 4
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,4 @@
- [email protected] "First"
- [email protected] "\n"
- [email protected] "}"
recursion limit: 4096, high: 0
recursion limit: 500, high: 2
Original file line number Diff line number Diff line change
Expand Up @@ -712,4 +712,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 500, high: 3
Original file line number Diff line number Diff line change
Expand Up @@ -2808,4 +2808,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n"
recursion limit: 4096, high: 3
recursion limit: 500, high: 8
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,4 @@
- [email protected] "\n"
- [email protected] "}"
- [email protected] "\n\n"
recursion limit: 4096, high: 2
recursion limit: 500, high: 4
8 changes: 8 additions & 0 deletions crates/apollo-parser/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

# [x.x.x] (unreleased) - 2023-mm-dd
## Fixes
- **apply recursion limit where needed, reduce its default from 4096 to 500 [SimonSapin], [pull/662]**
The limit was only tracked for nested selection sets, but the parser turns out
to use recursion in other cases too. [Issue 666] tracks reducing them.
Stack overflow was observed with little more than 2000
nesting levels or repetitions in the new test.
Defaulting to a quarter of that leaves a comfortable margin.
- **fixes lexing of plus and minus signs in numbers - [SimonSapin], [pull/646]**
Plus signs are errors in GraphQL syntax.
Minus signs are errors if they’re not followed by a digit, even if followed by EOF.

[pull/646]: https://github.com/apollographql/apollo-rs/pull/646
[pull/662]: https://github.com/apollographql/apollo-rs/pull/662
[Issue 666]: https://github.com/apollographql/apollo-rs/issues/666

# [0.6.2](https://crates.io/crates/apollo-parser/0.6.2) - 2023-09-08
## Fixes
Expand Down
3 changes: 1 addition & 2 deletions crates/apollo-parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ impl<'a> Iterator for Lexer<'a> {
return None;
}

self.limit_tracker.consume();
if self.limit_tracker.limited() {
if self.limit_tracker.check_and_increment() {
self.finished = true;
return Some(Err(Error::limit(
"token limit reached, aborting lexing",
Expand Down
30 changes: 12 additions & 18 deletions crates/apollo-parser/src/limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,13 @@ use std::fmt;
/// ```
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct LimitTracker {
current: usize,
pub(crate) current: usize,
/// High Water mark for this limit
pub high: usize,
/// Limit.
pub limit: usize,
}

impl Default for LimitTracker {
fn default() -> Self {
Self {
current: 0,
high: 0,
limit: 4_096, // Recursion limit derived from router experimentation
}
}
}

impl LimitTracker {
pub fn new(limit: usize) -> Self {
Self {
Expand All @@ -65,19 +55,23 @@ impl LimitTracker {
}
}

pub fn limited(&self) -> bool {
self.current > self.limit
}

pub fn consume(&mut self) {
/// Return whether the limit was reached
#[must_use]
pub fn check_and_increment(&mut self) -> bool {
self.current += 1;
if self.current > self.high {
self.high = self.current;
}
let reached = self.current > self.limit;
if reached {
// Caller is gonna return early, keep increments and decrements balanced:
self.decrement()
}
reached
}

pub fn reset(&mut self) {
self.current = 0;
pub fn decrement(&mut self) {
self.current -= 1;
}
}

Expand Down
9 changes: 8 additions & 1 deletion crates/apollo-parser/src/parser/grammar/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ pub(crate) fn argument(p: &mut Parser, mut is_argument: bool) {
is_argument = true;
if p.peek().is_some() {
guard.finish_node();
return argument(p, is_argument);
// TODO: use a loop instead of recursion
if p.recursion_limit.check_and_increment() {
p.limit_err("parser recursion limit reached");
return;
}
argument(p, is_argument);
p.recursion_limit.decrement();
return;
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions crates/apollo-parser/src/parser/grammar/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ pub(crate) fn document(p: &mut Parser) {
let doc = p.start_node(SyntaxKind::DOCUMENT);

while let Some(node) = p.peek() {
if p.recursion_limit.limited() {
break;
}
assert_eq!(
p.recursion_limit.current, 0,
"unbalanced limit increment / decrement"
);

match node {
TokenKind::StringValue => {
Expand Down
9 changes: 8 additions & 1 deletion crates/apollo-parser/src/parser/grammar/enum_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,14 @@ pub(crate) fn enum_value_definition(p: &mut Parser) {
}
if p.peek().is_some() {
guard.finish_node();
return enum_value_definition(p);
// TODO: use a loop instead of recursion
if p.recursion_limit.check_and_increment() {
p.limit_err("parser recursion limit reached");
return;
}
enum_value_definition(p);
p.recursion_limit.decrement();
return;
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/apollo-parser/src/parser/grammar/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub(crate) fn fragment_definition(p: &mut Parser) {
}

match p.peek() {
Some(T!['{']) => selection::top_selection_set(p),
Some(T!['{']) => selection::selection_set(p),
_ => p.err("expected a Selection Set"),
}
}
Expand Down Expand Up @@ -77,7 +77,7 @@ pub(crate) fn inline_fragment(p: &mut Parser) {
}

match p.peek() {
Some(T!['{']) => selection::top_selection_set(p),
Some(T!['{']) => selection::selection_set(p),
_ => p.err("expected Selection Set"),
}
}
Expand Down
9 changes: 8 additions & 1 deletion crates/apollo-parser/src/parser/grammar/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,14 @@ pub(crate) fn input_value_definition(p: &mut Parser, is_input: bool) {

if p.peek().is_some() {
guard.finish_node();
return input_value_definition(p, true);
// TODO: use a loop instead of recursion
if p.recursion_limit.check_and_increment() {
p.limit_err("parser recursion limit reached");
return;
}
input_value_definition(p, true);
p.recursion_limit.decrement();
return;
}
}
_ => p.err("expected a Type"),
Expand Down
6 changes: 6 additions & 0 deletions crates/apollo-parser/src/parser/grammar/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,13 @@ fn implements_interface(p: &mut Parser, is_interfaces: bool) {
ty::named_type(p);
if let Some(node) = p.peek_data() {
if !is_definition(node) {
// TODO: use a loop instead of recursion
if p.recursion_limit.check_and_increment() {
p.limit_err("parser recursion limit reached");
return;
}
implements_interface(p, true);
p.recursion_limit.decrement()
}

return;
Expand Down
Loading

0 comments on commit 865be96

Please sign in to comment.