]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-130139: always check ast node type in ast.parse() with ast input (#130140)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Sun, 16 Feb 2025 13:32:39 +0000 (13:32 +0000)
committerGitHub <noreply@github.com>
Sun, 16 Feb 2025 13:32:39 +0000 (13:32 +0000)
Doc/whatsnew/3.14.rst
Include/internal/pycore_ast.h
Lib/test/test_ast/test_ast.py
Lib/test/test_unparse.py
Misc/NEWS.d/next/Library/2025-02-15-01-37-47.gh-issue-130139.gntc7B.rst [new file with mode: 0644]
Parser/asdl_c.py
Python/Python-ast.c
Python/bltinmodule.c

index c42a5a022b7e4399b1940b676f514599b2e6e0f4..ac0ae8cf0133e6d50b7d546b7ba06c2d57f6d8d5 100644 (file)
@@ -346,6 +346,10 @@ ast
 * The ``repr()`` output for AST nodes now includes more information.
   (Contributed by Tomas R in :gh:`116022`.)
 
+* :func:`ast.parse`, when called with an AST as input, now always verifies
+  that the root node type is appropriate.
+  (Contributed by Irit Katriel in :gh:`130139`.)
+
 
 calendar
 --------
index f5bf1205a82be98eb6e8c96c45ff540ebc559c3b..69abc3536e312a3e0460336b29496f4742d01ff0 100644 (file)
@@ -907,6 +907,7 @@ type_param_ty _PyAST_TypeVarTuple(identifier name, expr_ty default_value, int
 
 
 PyObject* PyAST_mod2obj(mod_ty t);
+int PyAst_CheckMode(PyObject *ast, int mode);
 mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode);
 int PyAST_Check(PyObject* obj);
 
index 434f291eebe51b1ddc44cd7a6ddac5f427587da2..42dbb6e79c33b4ad769da62d4fe0ab80c9c65a5f 100644 (file)
@@ -131,6 +131,12 @@ class AST_Tests(unittest.TestCase):
             tree = ast.parse(snippet)
             compile(tree, '<string>', 'exec')
 
+    def test_parse_invalid_ast(self):
+        # see gh-130139
+        for optval in (-1, 0, 1, 2):
+            self.assertRaises(TypeError, ast.parse, ast.Constant(42),
+                              optimize=optval)
+
     def test_optimization_levels__debug__(self):
         cases = [(-1, '__debug__'), (0, '__debug__'), (1, False), (2, False)]
         for (optval, expected) in cases:
index f6c4f1f3f6476aaed8584d80fb8210c6f8a017dc..f45a651c7ccb5d555ad828076ab35de37e724c5d 100644 (file)
@@ -422,9 +422,9 @@ class UnparseTestCase(ASTTestCase):
             self.check_ast_roundtrip(f"'''{docstring}'''")
 
     def test_constant_tuples(self):
-        self.check_src_roundtrip(ast.Constant(value=(1,), kind=None), "(1,)")
+        self.check_src_roundtrip(ast.Module([ast.Constant(value=(1,))]), "(1,)")
         self.check_src_roundtrip(
-            ast.Constant(value=(1, 2, 3), kind=None), "(1, 2, 3)"
+            ast.Module([ast.Constant(value=(1, 2, 3))]), "(1, 2, 3)"
         )
 
     def test_function_type(self):
diff --git a/Misc/NEWS.d/next/Library/2025-02-15-01-37-47.gh-issue-130139.gntc7B.rst b/Misc/NEWS.d/next/Library/2025-02-15-01-37-47.gh-issue-130139.gntc7B.rst
new file mode 100644 (file)
index 0000000..5cb3bf1
--- /dev/null
@@ -0,0 +1,2 @@
+Fix bug where :func:`ast.parse` did not error on AST input which is not of the
+correct type, when called with optimize=False.
index 7b2df738119115bca38fbf6ff3689d2fa90cb965..b2a5dd6792deadc84a21b467c0eaa80b94142e4a 100755 (executable)
@@ -2166,18 +2166,13 @@ PyObject* PyAST_mod2obj(mod_ty t)
 }
 
 /* mode is 0 for "exec", 1 for "eval" and 2 for "single" input */
-mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
+int PyAst_CheckMode(PyObject *ast, int mode)
 {
     const char * const req_name[] = {"Module", "Expression", "Interactive"};
-    int isinstance;
-
-    if (PySys_Audit("compile", "OO", ast, Py_None) < 0) {
-        return NULL;
-    }
 
     struct ast_state *state = get_ast_state();
     if (state == NULL) {
-        return NULL;
+        return -1;
     }
 
     PyObject *req_type[3];
@@ -2186,13 +2181,30 @@ mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
     req_type[2] = state->Interactive_type;
 
     assert(0 <= mode && mode <= 2);
-
-    isinstance = PyObject_IsInstance(ast, req_type[mode]);
-    if (isinstance == -1)
-        return NULL;
+    int isinstance = PyObject_IsInstance(ast, req_type[mode]);
+    if (isinstance == -1) {
+        return -1;
+    }
     if (!isinstance) {
         PyErr_Format(PyExc_TypeError, "expected %s node, got %.400s",
                      req_name[mode], _PyType_Name(Py_TYPE(ast)));
+        return -1;
+    }
+    return 0;
+}
+
+mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
+{
+    if (PySys_Audit("compile", "OO", ast, Py_None) < 0) {
+        return NULL;
+    }
+
+    struct ast_state *state = get_ast_state();
+    if (state == NULL) {
+        return NULL;
+    }
+
+    if (PyAst_CheckMode(ast, mode) < 0) {
         return NULL;
     }
 
@@ -2356,6 +2368,7 @@ def write_header(mod, metadata, f):
     f.write(textwrap.dedent("""
 
         PyObject* PyAST_mod2obj(mod_ty t);
+        int PyAst_CheckMode(PyObject *ast, int mode);
         mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode);
         int PyAST_Check(PyObject* obj);
 
index 7038e3c92ab8f07a866cfb23819869e60db56672..4adf72a8537a51bd6ac4b1c6b8756949276e8ddd 100644 (file)
@@ -18161,18 +18161,13 @@ PyObject* PyAST_mod2obj(mod_ty t)
 }
 
 /* mode is 0 for "exec", 1 for "eval" and 2 for "single" input */
-mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
+int PyAst_CheckMode(PyObject *ast, int mode)
 {
     const char * const req_name[] = {"Module", "Expression", "Interactive"};
-    int isinstance;
-
-    if (PySys_Audit("compile", "OO", ast, Py_None) < 0) {
-        return NULL;
-    }
 
     struct ast_state *state = get_ast_state();
     if (state == NULL) {
-        return NULL;
+        return -1;
     }
 
     PyObject *req_type[3];
@@ -18181,13 +18176,30 @@ mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
     req_type[2] = state->Interactive_type;
 
     assert(0 <= mode && mode <= 2);
-
-    isinstance = PyObject_IsInstance(ast, req_type[mode]);
-    if (isinstance == -1)
-        return NULL;
+    int isinstance = PyObject_IsInstance(ast, req_type[mode]);
+    if (isinstance == -1) {
+        return -1;
+    }
     if (!isinstance) {
         PyErr_Format(PyExc_TypeError, "expected %s node, got %.400s",
                      req_name[mode], _PyType_Name(Py_TYPE(ast)));
+        return -1;
+    }
+    return 0;
+}
+
+mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode)
+{
+    if (PySys_Audit("compile", "OO", ast, Py_None) < 0) {
+        return NULL;
+    }
+
+    struct ast_state *state = get_ast_state();
+    if (state == NULL) {
+        return NULL;
+    }
+
+    if (PyAst_CheckMode(ast, mode) < 0) {
         return NULL;
     }
 
index 46a6fd9a8ef01743b532c2c1045b4f11ed66a851..a7243baa64c2a8cff910b0cf15cc501d77ebfcbc 100644 (file)
@@ -835,6 +835,9 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
         goto error;
     if (is_ast) {
         if ((flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST) {
+            if (PyAst_CheckMode(source, compile_mode) < 0) {
+                goto error;
+            }
             // return an un-optimized AST
             result = Py_NewRef(source);
         }