Skip to content

Commit

Permalink
[3.10] pythongh-93671: Avoid exponential backtracking in deeply neste…
Browse files Browse the repository at this point in the history
…d sequence patterns in match statements (pythonGH-93680)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>.
(cherry picked from commit 53a8b17)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
  • Loading branch information
pablogsal committed Jun 10, 2022
1 parent 9041b00 commit fd56269
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 2 deletions.
6 changes: 4 additions & 2 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ as_pattern[pattern_ty]:
or_pattern[pattern_ty]:
| patterns[asdl_pattern_seq*]='|'.closed_pattern+ {
asdl_seq_LEN(patterns) == 1 ? asdl_seq_GET(patterns, 0) : _PyAST_MatchOr(patterns, EXTRA) }
closed_pattern[pattern_ty]:

closed_pattern[pattern_ty] (memo):
| literal_pattern
| capture_pattern
| wildcard_pattern
Expand Down Expand Up @@ -329,7 +330,8 @@ maybe_sequence_pattern[asdl_seq*]:
maybe_star_pattern[pattern_ty]:
| star_pattern
| pattern
star_pattern[pattern_ty]:

star_pattern[pattern_ty] (memo):
| '*' target=pattern_capture_target {
_PyAST_MatchStar(target->v.Name.id, EXTRA) }
| '*' wildcard_pattern {
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_patma.py
Original file line number Diff line number Diff line change
Expand Up @@ -3138,6 +3138,27 @@ def f(command): # 0
self.assertListEqual(self._trace(f, "go x"), [1, 2, 3])
self.assertListEqual(self._trace(f, "spam"), [1, 2, 3])

def test_parser_deeply_nested_patterns(self):
# Deeply nested patterns can cause exponential backtracking when parsing.
# See gh-93671 for more information.

levels = 100

patterns = [
"A" + "(" * levels + ")" * levels,
"{1:" * levels + "1" + "}" * levels,
"[" * levels + "1" + "]" * levels,
]

for pattern in patterns:
with self.subTest(pattern):
code = inspect.cleandoc("""
match None:
case {}:
pass
""".format(pattern))
compile(code, "<string>", "exec")


if __name__ == "__main__":
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix some exponential backtrace case happening with deeply nested sequence
patterns in match statements. Patch by Pablo Galindo
10 changes: 10 additions & 0 deletions Parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -5920,6 +5920,10 @@ closed_pattern_rule(Parser *p)
return NULL;
}
pattern_ty _res = NULL;
if (_PyPegen_is_memoized(p, closed_pattern_type, &_res)) {
p->level--;
return _res;
}
int _mark = p->mark;
{ // literal_pattern
if (p->error_indicator) {
Expand Down Expand Up @@ -6075,6 +6079,7 @@ closed_pattern_rule(Parser *p)
}
_res = NULL;
done:
_PyPegen_insert_memo(p, _mark, closed_pattern_type, _res);
p->level--;
return _res;
}
Expand Down Expand Up @@ -7598,6 +7603,10 @@ star_pattern_rule(Parser *p)
return NULL;
}
pattern_ty _res = NULL;
if (_PyPegen_is_memoized(p, star_pattern_type, &_res)) {
p->level--;
return _res;
}
int _mark = p->mark;
if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
p->error_indicator = 1;
Expand Down Expand Up @@ -7682,6 +7691,7 @@ star_pattern_rule(Parser *p)
}
_res = NULL;
done:
_PyPegen_insert_memo(p, _mark, star_pattern_type, _res);
p->level--;
return _res;
}
Expand Down

0 comments on commit fd56269

Please sign in to comment.