https://github.com/python/cpython/commit/1546d44f6ee2a629a27748c8aae97f98b0e4ae93
commit: 1546d44f6ee2a629a27748c8aae97f98b0e4ae93
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-08-31T14:19:57+01:00
summary:

[3.14] gh-156689: Fix out-of-bounds read in `PyAst_CheckMode()` for 
`mode='func_type'` (GH-156697) (#156704)

(cherry picked from commit d7f9c6400f49633221983d0c454cf27ecb0302ba)

Co-authored-by: Stan Ulbrych <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-08-31-10-45-11.gh-issue-156689.Jq3xNv.rst
M Doc/library/ast.rst
M Lib/test/test_ast/test_ast.py
M Parser/asdl_c.py
M Python/Python-ast.c

diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 5338d407ca22596..85fc2b9b0232a88 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -2257,7 +2257,7 @@ and classes for traversing abstract syntax trees:
 
    In addition, if ``mode`` is ``'func_type'``, the input syntax is
    modified to correspond to :pep:`484` "signature type comments",
-   e.g. ``(str, int) -> List[str]``.
+   for example ``(str, int) -> List[str]``.
 
    Setting ``feature_version`` to a tuple ``(major, minor)`` will result in
    a "best-effort" attempt to parse using that Python version's grammar.
diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py
index 5d5967cd9148e65..94ad8f85b17a24c 100644
--- a/Lib/test/test_ast/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -152,6 +152,15 @@ def test_parse_invalid_ast(self):
             self.assertRaises(TypeError, ast.parse, ast.Constant(42),
                               optimize=optval)
 
+    def test_parse_ast_func_type(self):
+        # see gh-156689
+        tree = ast.parse('(int, str) -> bool', mode='func_type')
+        self.assertEqual(ast.dump(ast.parse(tree, mode='func_type')),
+                         ast.dump(tree))
+        self.assertRaises(TypeError, ast.parse, ast.Constant(42),
+                          mode='func_type')
+        self.assertRaises(TypeError, ast.parse, tree, mode='exec')
+
     def test_optimization_levels__debug__(self):
         cases = [(-1, '__debug__'), (0, '__debug__'), (1, False), (2, False)]
         for (optval, expected) in cases:
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-31-10-45-11.gh-issue-156689.Jq3xNv.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-31-10-45-11.gh-issue-156689.Jq3xNv.rst
new file mode 100644
index 000000000000000..868997320b2f7cb
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-31-10-45-11.gh-issue-156689.Jq3xNv.rst
@@ -0,0 +1,2 @@
+Fix an out-of-bounds read in :func:`compile` and :func:`ast.parse` when an AST
+object is passed with ``mode='func_type'``.
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py
index 5ad20d49fa4b310..de202aaec8cbbb2 100755
--- a/Parser/asdl_c.py
+++ b/Parser/asdl_c.py
@@ -2169,22 +2169,25 @@ class PartingShots(StaticVisitor):
     return result;
 }
 
-/* mode is 0 for "exec", 1 for "eval" and 2 for "single" input */
+/* mode is 0 for "exec", 1 for "eval", 2 for "single" and 3 for "func_type"
+   input */
 int PyAst_CheckMode(PyObject *ast, int mode)
 {
-    const char * const req_name[] = {"Module", "Expression", "Interactive"};
+    const char * const req_name[] = {"Module", "Expression", "Interactive",
+                                     "FunctionType"};
 
     struct ast_state *state = get_ast_state();
     if (state == NULL) {
         return -1;
     }
 
-    PyObject *req_type[3];
+    PyObject *req_type[4];
     req_type[0] = state->Module_type;
     req_type[1] = state->Expression_type;
     req_type[2] = state->Interactive_type;
+    req_type[3] = state->FunctionType_type;
 
-    assert(0 <= mode && mode <= 2);
+    assert(0 <= mode && mode <= 3);
     int isinstance = PyObject_IsInstance(ast, req_type[mode]);
     if (isinstance == -1) {
         return -1;
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index 1cc88dc179e120d..2dd2f6778d14749 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -18423,22 +18423,25 @@ PyObject* PyAST_mod2obj(mod_ty t)
     return result;
 }
 
-/* mode is 0 for "exec", 1 for "eval" and 2 for "single" input */
+/* mode is 0 for "exec", 1 for "eval", 2 for "single" and 3 for "func_type"
+   input */
 int PyAst_CheckMode(PyObject *ast, int mode)
 {
-    const char * const req_name[] = {"Module", "Expression", "Interactive"};
+    const char * const req_name[] = {"Module", "Expression", "Interactive",
+                                     "FunctionType"};
 
     struct ast_state *state = get_ast_state();
     if (state == NULL) {
         return -1;
     }
 
-    PyObject *req_type[3];
+    PyObject *req_type[4];
     req_type[0] = state->Module_type;
     req_type[1] = state->Expression_type;
     req_type[2] = state->Interactive_type;
+    req_type[3] = state->FunctionType_type;
 
-    assert(0 <= mode && mode <= 2);
+    assert(0 <= mode && mode <= 3);
     int isinstance = PyObject_IsInstance(ast, req_type[mode]);
     if (isinstance == -1) {
         return -1;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to