From 0546055b055d05b037e219ebf7099d104b3a0ec9 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 9 Jun 2023 11:00:27 -0700 Subject: [PATCH 1/4] Add failing regression tests --- Lib/test/test_ast.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index ffd082ec11806a..40075c5f217350 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -3,6 +3,7 @@ import dis import enum import os +import re import sys import textwrap import types @@ -1110,6 +1111,32 @@ def test_null_bytes(self): msg="source code string cannot contain null bytes"): ast.parse("a\0b") + def assert_none_check(self, node: type[ast.AST], attr: str, source: str) -> None: + with self.subTest(f"{node.__name__}.{attr}"): + tree = ast.parse(source) + found = 0 + for child in ast.walk(tree): + if isinstance(child, node): + setattr(child, attr, None) + found += 1 + self.assertEqual(found, 1) + e = re.escape(f"field '{attr}' is required for {node.__name__}") + with self.assertRaisesRegex(ValueError, f"^{e}$"): + compile(tree, "", "exec") + + def test_none_checks(self) -> None: + tests = [ + (ast.alias, "name", "import spam as SPAM"), + (ast.arg, "arg", "def spam(SPAM): spam"), + (ast.comprehension, "target", "[spam for SPAM in spam]"), + (ast.comprehension, "iter", "[spam for spam in SPAM]"), + (ast.keyword, "value", "spam(**SPAM)"), + (ast.match_case, "pattern", "match spam:\n case SPAM: pass"), + (ast.withitem, "context_expr", "with SPAM: pass"), + ] + for node, attr, source in tests: + self.assert_none_check(node, attr, source) + class ASTHelpers_Test(unittest.TestCase): maxDiff = None From 937af55e75533685b0a2e88a85976ad5cf1fd0c6 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 9 Jun 2023 11:10:21 -0700 Subject: [PATCH 2/4] fixup --- Lib/test/test_ast.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 40075c5f217350..a03fa4c7187b05 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -1131,8 +1131,8 @@ def test_none_checks(self) -> None: (ast.comprehension, "target", "[spam for SPAM in spam]"), (ast.comprehension, "iter", "[spam for spam in SPAM]"), (ast.keyword, "value", "spam(**SPAM)"), - (ast.match_case, "pattern", "match spam:\n case SPAM: pass"), - (ast.withitem, "context_expr", "with SPAM: pass"), + (ast.match_case, "pattern", "match spam:\n case SPAM: spam"), + (ast.withitem, "context_expr", "with SPAM: spam"), ] for node, attr, source in tests: self.assert_none_check(node, attr, source) From 45e92feacee2d1a8567ed7b5434134e219f4f105 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 9 Jun 2023 11:11:11 -0700 Subject: [PATCH 3/4] Add error checks after "product" node construction --- Parser/asdl_c.py | 1 + Python/Python-ast.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index cb312796f89e04..d4763ea260a5a2 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -601,6 +601,7 @@ def visitProduct(self, prod, name): args = [f.name for f in prod.fields] args.extend([a.name for a in prod.attributes]) self.emit("*out = %s(%s);" % (ast_func_name(name), self.buildArgs(args)), 1) + self.emit("if (*out == NULL) goto failed;", 1) self.emit("return 0;", 1) self.emit("failed:", 0) self.emit("Py_XDECREF(tmp);", 1) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 84bce59e271471..1ffb8354e3a1b1 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -10834,6 +10834,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* Py_CLEAR(tmp); } *out = _PyAST_comprehension(target, iter, ifs, is_async, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); @@ -11258,6 +11259,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, } *out = _PyAST_arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); @@ -11397,6 +11399,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) } *out = _PyAST_arg(arg, annotation, type_comment, lineno, col_offset, end_lineno, end_col_offset, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); @@ -11519,6 +11522,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, } *out = _PyAST_keyword(arg, value, lineno, col_offset, end_lineno, end_col_offset, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); @@ -11641,6 +11645,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* } *out = _PyAST_alias(name, asname, lineno, col_offset, end_lineno, end_col_offset, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); @@ -11690,6 +11695,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, Py_CLEAR(tmp); } *out = _PyAST_withitem(context_expr, optional_vars, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); @@ -11778,6 +11784,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, Py_CLEAR(tmp); } *out = _PyAST_match_case(pattern, guard, body, arena); + if (*out == NULL) goto failed; return 0; failed: Py_XDECREF(tmp); From f8036bd31ca3fc240229df9d2b1b481fb35f7b68 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 9 Jun 2023 11:20:24 -0700 Subject: [PATCH 4/4] blurb add --- .../2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst new file mode 100644 index 00000000000000..3981dad7a49dfb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst @@ -0,0 +1,2 @@ +Fix an issue that could result in crashes when compiling malformed +:mod:`ast` nodes. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy