Skip to content
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

Avoid stack overflows via configurable with_recursion_limit #764

Merged
merged 1 commit into from Dec 28, 2022

Conversation

alamb
Copy link
Collaborator

@alamb alamb commented Dec 14, 2022

Closes #305. Alternative to #751

This PR is based off the approach from @46bit in #501 and includes some code and tests from there. While it may look like a large change, it is mostly docstring changes and tests. The code changes are very small

Rationale

Given a deeply nested input, sqlparser will overflow the stack, resulting in a panic/segfault.

Changes

  1. Add a configurable maximum recursion limit and then check that limit during each call to parse_expr, parse_statement and parse_query
  2. Changes how the Parser is configured to a builder style (and adds docstrings to make it easier to figure out)
  3. Improve docstrings (with several examples)
  4. Add tests for recursion limit

API Changes

The Parser::new() signature is change, but it is now documented. All other changes are to unreleased code added in #710 by @ankrgyl

Notes

The only recursive nested structures are Expr, Statement, and Query (as they are the places that are Boxed in the AST). Thus it is sufficient to check recursion depth at each of these points

@@ -55,6 +56,92 @@ macro_rules! return_ok_if_some {
}};
}

#[cfg(feature = "std")]
/// Implemenation [`RecursionCounter`] if std is available
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the key change for actually limiting recursion depth

/// each call to `try_decrease()`, when it reaches 0 an error will
/// be returned.
///
/// Note: Uses an Rc and AtomicUsize in order to satisfy the Rust
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The Rc approach is from @46bit -- if anyone has ideas about how to avoid it, I would love a PR to help.

}
}

/// Guard that increass the remaining depth by 1 on drop
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The DepthGuard is used to automatically ensure the recursion depth is restored upon function return

/// # Ok(())
/// # }
/// ```
pub fn new(dialect: &'a dyn Dialect) -> Self {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the signature here has changed - I tried to illustrate the intended usage with doc comments

}

/// Parse a SQL statement and produce an Abstract Syntax Tree (AST)
pub fn parse_sql(dialect: &dyn Dialect, sql: &str) -> Result<Vec<Statement>, ParserError> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

github mangles the diff -- this function still exists with the same signature. It now also has docstrings

/// # Ok(())
/// # }
/// ```
pub fn parse_statements(&mut self) -> Result<Vec<Statement>, ParserError> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This code was factored out of parse_sql so that people directly using Parser::new() could also have access to that logic

@coveralls
Copy link

Pull Request Test Coverage Report for Build 3698164587

  • 78 of 88 (88.64%) changed or added relevant lines in 4 files are covered.
  • 297 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.03%) to 86.385%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/parser.rs 37 41 90.24%
src/lib.rs 0 6 0.0%
Files with Coverage Reduction New Missed Lines %
src/lib.rs 297 10.0%
Totals Coverage Status
Change from base Build 3689832714: 0.03%
Covered Lines: 12747
Relevant Lines: 14756

💛 - Coveralls

@alamb
Copy link
Collaborator Author

alamb commented Dec 14, 2022

cc @AugustoFKL @andygrove and @Dandandan

@alamb alamb changed the title Avoid stack overflows with_recursion_limit configurable recursion limit to parser Avoid stack overflows via configurable with_recursion_limit Dec 14, 2022
@alamb alamb merged commit 79d0baa into sqlparser-rs:main Dec 28, 2022
@alamb alamb deleted the alamb/limit_stack_size branch December 28, 2022 13:29
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.

Stack overflow when parsing SQL consisting of many imbalanced open parenthesis
3 participants