From 0d0d135bf73bf9acbbfc005dd89f008697889347 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Mon, 21 Sep 2026 15:29:03 +0800 Subject: [PATCH 1/3] gh-157875: Fix a reference leak when rewinding JIT traces --- Lib/test/test_capi/test_opt.py | 22 +++++++++++++++++++ ...-09-21-16-00-00.gh-issue-157875.7Qn4Jp.rst | 2 ++ Python/optimizer.c | 3 +++ 3 files changed, 27 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-16-00-00.gh-issue-157875.7Qn4Jp.rst diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 36efab51878141..c844e30a229d96 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -6016,6 +6016,28 @@ def f1(): """), PYTHON_JIT="1") self.assertEqual(result[0].rc, 0, result) + def test_157875_rewound_trace_reference_leak(self): + # https://github.com/python/cpython/issues/157875 + import weakref + + def run(): + class C: + def __iter__(self): + return self + + def __next__(self): + raise StopIteration + + obj = C() + for _ in range(10_000): + for _ in obj: + pass + return weakref.ref(C) + + ref = run() + gc.collect() + self.assertIsNone(ref()) + def test_144068_daemon_thread_jit_cleanup(self): result = script_helper.run_python_until_end('-c', textwrap.dedent(""" import threading diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-16-00-00.gh-issue-157875.7Qn4Jp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-16-00-00.gh-issue-157875.7Qn4Jp.rst new file mode 100644 index 00000000000000..22b1a17fa7bca4 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-16-00-00.gh-issue-157875.7Qn4Jp.rst @@ -0,0 +1,2 @@ +Fix a reference leak in the JIT when a trace is rewound after encountering +unsupported bytecode. diff --git a/Python/optimizer.c b/Python/optimizer.c index e05adb344c8d06..4ae72304c16206 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -789,6 +789,9 @@ _PyJit_translate_single_bytecode_to_trace( { _PyUOpInstruction *curr = uop_buffer_last(trace); while (curr->opcode != _SET_IP && uop_buffer_length(trace) > 2) { + if (_PyUop_Flags[curr->opcode] & HAS_RECORDS_VALUE_FLAG) { + Py_XDECREF((PyObject *)(uintptr_t)curr->operand0); + } trace->next--; curr = uop_buffer_last(trace); } From 53492190bb522b62f6aedbe47f859adf24cf9eea Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Mon, 21 Sep 2026 20:49:36 +0800 Subject: [PATCH 2/3] Update test_opt.py --- Lib/test/test_capi/test_opt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index c844e30a229d96..f78228b515a87c 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -5981,7 +5981,7 @@ def __next__(self): def test_149335_trace_buffer_guard(self): # https://github.com/python/cpython/issues/149335 - result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + result = script_helper.run_python_until_end('-c', textwrap.dedent(f""" import sys def f1(): @@ -6011,7 +6011,7 @@ def f1(): mv1 = mv_12 = mv3 = mv_14 = mv45 = sys.float_info.epsilon mv46 = sys.float_info.epsilon - for i in range(15000): + for i in range({TIER2_THRESHOLD}): f1() """), PYTHON_JIT="1") self.assertEqual(result[0].rc, 0, result) @@ -6029,7 +6029,7 @@ def __next__(self): raise StopIteration obj = C() - for _ in range(10_000): + for _ in range(TIER2_THRESHOLD): for _ in obj: pass return weakref.ref(C) From 2698b496de6535c696ea18a1767166a9dbf5fe63 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Tue, 22 Sep 2026 08:41:12 +0800 Subject: [PATCH 3/3] Update test_opt.py --- Lib/test/test_capi/test_opt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index f78228b515a87c..d455c0a25120db 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -5981,7 +5981,7 @@ def __next__(self): def test_149335_trace_buffer_guard(self): # https://github.com/python/cpython/issues/149335 - result = script_helper.run_python_until_end('-c', textwrap.dedent(f""" + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" import sys def f1(): @@ -6011,7 +6011,7 @@ def f1(): mv1 = mv_12 = mv3 = mv_14 = mv45 = sys.float_info.epsilon mv46 = sys.float_info.epsilon - for i in range({TIER2_THRESHOLD}): + for i in range(15000): f1() """), PYTHON_JIT="1") self.assertEqual(result[0].rc, 0, result)