summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Galindo Salgado <Pablogsal@gmail.com>2022-06-10 18:34:15 (GMT)
committerGitHub <noreply@github.com>2022-06-10 18:34:15 (GMT)
commit8f36c735b2aa694b140a19d9425bbdf54de5f212 (patch)
treea9b4e898c1f4e20cc5f68ee9b25a480112efcfbd
parent9041b00283737f77acbb392871886913d82144f8 (diff)
downloadcpython-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.gram6
-rw-r--r--Lib/test/test_patma.py21
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-06-10-12-03-17.gh-issue-93671.idkQqG.rst2
-rw-r--r--Parser/parser.c10
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;
}