]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] GH-105588: Add missing error checks to some obj2ast_* converters (GH-105838)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 15 Jun 2023 23:12:01 +0000 (16:12 -0700)
committerGitHub <noreply@github.com>
Thu, 15 Jun 2023 23:12:01 +0000 (23:12 +0000)
GH-105588: Add missing error checks to some obj2ast_* converters (GH-105589)
(cherry picked from commit a4056c8f9c2d9970d39e3cb6bffb255cd4b8a42c)

Co-authored-by: Brandt Bucher <brandtbucher@microsoft.com>
Lib/test/test_ast.py
Misc/NEWS.d/next/Core and Builtins/2023-06-09-11-19-51.gh-issue-105588.Y5ovpY.rst [new file with mode: 0644]
Parser/asdl_c.py
Python/Python-ast.c

index ffd082ec11806a4ea03fdb61f3b4bcd490621311..a03fa4c7187b05295d6a34423d2fa5cb10691957 100644 (file)
@@ -3,6 +3,7 @@ import builtins
 import dis
 import enum
 import os
+import re
 import sys
 import textwrap
 import types
@@ -1110,6 +1111,32 @@ class AST_Tests(unittest.TestCase):
             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, "<test>", "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: spam"),
+            (ast.withitem, "context_expr", "with SPAM: spam"),
+        ]
+        for node, attr, source in tests:
+            self.assert_none_check(node, attr, source)
+
 class ASTHelpers_Test(unittest.TestCase):
     maxDiff = None
 
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 (file)
index 0000000..3981dad
--- /dev/null
@@ -0,0 +1,2 @@
+Fix an issue that could result in crashes when compiling malformed
+:mod:`ast` nodes.
index cb312796f89e04f39447c7e8faf98db4aad25bc0..d4763ea260a5a21652261b12c363e32f9ca0d924 100755 (executable)
@@ -601,6 +601,7 @@ class Obj2ModVisitor(PickleVisitor):
         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)
index 84bce59e271471bc2774d1feaa7930508f91bcb2..1ffb8354e3a1b11f27c3a361683d92664538648f 100644 (file)
@@ -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);