]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-145197: Fix JIT trace crash when recording function from cleared generator frame...
authorHai Zhu <haiizhu@outlook.com>
Wed, 25 Feb 2026 16:52:53 +0000 (00:52 +0800)
committerGitHub <noreply@github.com>
Wed, 25 Feb 2026 16:52:53 +0000 (16:52 +0000)
Include/internal/pycore_opcode_metadata.h
Include/internal/pycore_uop_metadata.h
Lib/test/test_capi/test_opt.py
Misc/NEWS.d/next/Core_and_Builtins/2026-02-25-15-02-08.gh-issue-145197.G6hAUk.rst [new file with mode: 0644]
Python/bytecodes.c
Python/record_functions.c.h

index fa8337bd08d4701cf9a7a8e63410fd6d2a4e6fc8..126bc7d7102925a7776a16c53dec5a5b5a266d05 100644 (file)
@@ -1179,7 +1179,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
     [FORMAT_SIMPLE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [FORMAT_WITH_SPEC] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_UNPREDICTABLE_JUMP_FLAG },
-    [FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG | HAS_RECORDS_VALUE_FLAG },
+    [FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG | HAS_RECORDS_VALUE_FLAG },
     [FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG | HAS_UNPREDICTABLE_JUMP_FLAG },
     [FOR_ITER_RANGE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_UNPREDICTABLE_JUMP_FLAG },
     [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_UNPREDICTABLE_JUMP_FLAG },
@@ -1283,7 +1283,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
     [RETURN_GENERATOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_NEEDS_GUARD_IP_FLAG },
     [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_NEEDS_GUARD_IP_FLAG },
     [SEND] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_SYNC_SP_FLAG | HAS_UNPREDICTABLE_JUMP_FLAG | HAS_NEEDS_GUARD_IP_FLAG },
-    [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG | HAS_RECORDS_VALUE_FLAG },
+    [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG | HAS_RECORDS_VALUE_FLAG },
     [SETUP_ANNOTATIONS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [SET_ADD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
     [SET_FUNCTION_ATTRIBUTE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
index be06738b54ca670cfe50a1926e622c3fef65d6df..33a4d17d766eb26e2587fef789078b22c9130c5d 100644 (file)
@@ -379,7 +379,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = {
     [_RECORD_TOS] = HAS_RECORDS_VALUE_FLAG,
     [_RECORD_TOS_TYPE] = HAS_RECORDS_VALUE_FLAG,
     [_RECORD_NOS] = HAS_RECORDS_VALUE_FLAG,
-    [_RECORD_NOS_GEN_FUNC] = HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG,
+    [_RECORD_NOS_GEN_FUNC] = HAS_RECORDS_VALUE_FLAG,
     [_RECORD_4OS] = HAS_RECORDS_VALUE_FLAG,
     [_RECORD_CALLABLE] = HAS_ARG_FLAG | HAS_RECORDS_VALUE_FLAG,
     [_RECORD_BOUND_METHOD] = HAS_ARG_FLAG | HAS_RECORDS_VALUE_FLAG,
index fe1b45608841a294df8b5db5b863f872d94cf3bb..90e2ed20d1f6b3a34b6bffec5fad111b052f731e 100644 (file)
@@ -4114,6 +4114,28 @@ class TestUopsOptimization(unittest.TestCase):
              PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE="1")
         self.assertEqual(result[0].rc, 0, result)
 
+    def test_for_iter_gen_cleared_frame_does_not_crash(self):
+        # See: https://github.com/python/cpython/issues/145197
+        result = script_helper.run_python_until_end('-c', textwrap.dedent("""
+        def g():
+            yield 1
+            yield 2
+
+        for _ in range(4002):
+            for _ in g():
+                pass
+
+        for i in range(4002):
+            it = g()
+            if (i & 7) == 0:
+                next(it)
+                it.close()
+            for _ in it:
+                pass
+        """),
+        PYTHON_JIT="1", PYTHON_JIT_STRESS="1")
+        self.assertEqual(result[0].rc, 0, result)
+
 def global_identity(x):
     return x
 
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-25-15-02-08.gh-issue-145197.G6hAUk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-25-15-02-08.gh-issue-145197.G6hAUk.rst
new file mode 100644 (file)
index 0000000..6d6afe5
--- /dev/null
@@ -0,0 +1 @@
+Fix JIT trace crash when recording function from cleared generator frame.
index 36af61412b74170ec567f9ea6909965f82fdc3dc..41323c4f54d9edd993a0c4b7665677594d36e48f 100644 (file)
@@ -5730,8 +5730,11 @@ dummy_func(
         tier2 op(_RECORD_NOS_GEN_FUNC, (nos, tos -- nos, tos)) {
             PyObject *obj = PyStackRef_AsPyObjectBorrow(nos);
             if (PyGen_Check(obj)) {
-                PyObject *func = (PyObject *)_PyFrame_GetFunction(&((PyGenObject *)obj)->gi_iframe);
-                RECORD_VALUE(func);
+                PyGenObject *gen = (PyGenObject *)obj;
+                _PyStackRef func = gen->gi_iframe.f_funcobj;
+                if (!PyStackRef_IsNull(func)) {
+                    RECORD_VALUE(PyStackRef_AsPyObjectBorrow(func));
+                }
             }
         }
 
index c6709f9a9d6becba6290af7c7d2e36d5c139a3d8..64cafcb326e11142626af14115973728c9dda606 100644 (file)
@@ -32,11 +32,12 @@ void _PyOpcode_RecordFunction_NOS_GEN_FUNC(_PyInterpreterFrame *frame, _PyStackR
     nos = stack_pointer[-2];
     PyObject *obj = PyStackRef_AsPyObjectBorrow(nos);
     if (PyGen_Check(obj)) {
-        _PyFrame_SetStackPointer(frame, stack_pointer);
-        PyObject *func = (PyObject *)_PyFrame_GetFunction(&((PyGenObject *)obj)->gi_iframe);
-        stack_pointer = _PyFrame_GetStackPointer(frame);
-        *recorded_value = (PyObject *)func;
-        Py_INCREF(*recorded_value);
+        PyGenObject *gen = (PyGenObject *)obj;
+        _PyStackRef func = gen->gi_iframe.f_funcobj;
+        if (!PyStackRef_IsNull(func)) {
+            *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(func);
+            Py_INCREF(*recorded_value);
+        }
     }
 }