diff options
author | Pablo Galindo Salgado <Pablogsal@gmail.com> | 2022-06-10 18:34:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-10 18:34:15 (GMT) |
commit | 8f36c735b2aa694b140a19d9425bbdf54de5f212 (patch) | |
tree | a9b4e898c1f4e20cc5f68ee9b25a480112efcfbd | |
parent | 9041b00283737f77acbb392871886913d82144f8 (diff) | |
download | cpython-8f36c735b2aa694b140a19d9425bbdf54de5f212.zip cpython-8f36c735b2aa694b140a19d9425bbdf54de5f212.tar.gz cpython-8f36c735b2aa694b140a19d9425bbdf54de5f212.tar.bz2 |
[3.10] gh-93671: Avoid exponential backtracking in deeply nested sequence patterns in match statements (GH-93680) (#93690)
Co-authored-by: Ćukasz Langa <lukasz@langa.pl>.
(cherry picked from commit 53a8b17895e91d08f76a2fb59a555d012cd85ab4)
Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
-rw-r--r-- | Grammar/python.gram | 6 | ||||
-rw-r--r-- | Lib/test/test_patma.py | 21 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-06-10-12-03-17.gh-issue-93671.idkQqG.rst | 2 | ||||
-rw-r--r-- | Parser/parser.c | 10 |
4 files changed, 37 insertions, 2 deletions
diff --git a/Grammar/python.gram b/Grammar/python.gram index 84b9c9b..9f1c826 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -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 @@ -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 { diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index aa18e29..b844482 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -3138,6 +3138,27 @@ class TestTracing(unittest.TestCase): 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__": """ diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-10-12-03-17.gh-issue-93671.idkQqG.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-10-12-03-17.gh-issue-93671.idkQqG.rst new file mode 100644 index 0000000..a775715 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-10-12-03-17.gh-issue-93671.idkQqG.rst @@ -0,0 +1,2 @@ +Fix some exponential backtrace case happening with deeply nested sequence +patterns in match statements. Patch by Pablo Galindo diff --git a/Parser/parser.c b/Parser/parser.c index 8629500..59638da 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -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) { @@ -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; } @@ -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; @@ -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; } |