diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 518fd94a31ae5e..25f4f6ed7078df 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -54,6 +54,7 @@ typedef struct _PyJitTracerTranslatorState { } _PyJitTracerTranslatorState; typedef struct _PyJitTracerState { + bool is_tracing; _PyJitTracerInitialState initial_state; _PyJitTracerPreviousState prev_state; _PyJitTracerTranslatorState translator_state; diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index f111e9b5f2025b..8b580daa037919 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3606,6 +3606,51 @@ async def async_for_driver(): """), PYTHON_JIT="1") self.assertEqual(result[0].rc, 0, result) + def test_143358(self): + # https://github.com/python/cpython/issues/143358 + + result = script_helper.run_python_until_end('-c', textwrap.dedent(f""" + def f1(): + + class EvilIterator: + + def __init__(self): + self._items = [1, 2] + self._index = 1 + + def __iter__(self): + return self + + def __next__(self): + if not len(self._items) % 13: + self._items.clear() + + for i_loop_9279 in range(10): + self._items.extend([1, "", None]) + + if not len(self._items) % 11: + return 'unexpected_type_from_iterator' + + if self._index >= len(self._items): + raise StopIteration + + item = self._items[self._index] + self._index += 1 + return item + + evil_iter = EvilIterator() + + large_num = 2**31 + for _ in range(400): + try: + _ = [x + y for x in evil_iter for y in evil_iter if evil_iter._items.append(x) or large_num] + except TypeError: + pass + + f1() + """), PYTHON_JIT="1", PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE="64") + self.assertEqual(result[0].rc, 0, result) + def global_identity(x): return x diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-36-41.gh-issue-143123.-51gt_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-36-41.gh-issue-143123.-51gt_.rst new file mode 100644 index 00000000000000..04523bdb615bfe --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-36-41.gh-issue-143123.-51gt_.rst @@ -0,0 +1 @@ +Protect the JIT against recursive tracing. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0156fb3d06d854..b57471fbb4c1f1 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5607,6 +5607,16 @@ dummy_func( #else assert(_PyErr_Occurred(tstate)); #endif +#if _Py_TIER2 + if (IS_JIT_TRACING()) { + LEAVE_TRACING(); + int res = stop_tracing_and_jit(tstate, frame); + (void)res; + // We shouldn't ven have compiled in the first place. + assert(res == 0); + } +#endif + /* Log traceback info. */ assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e5e6d30f9c22f0..cede9024fc48c5 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -12323,6 +12323,16 @@ JUMP_TO_LABEL(error); #else assert(_PyErr_Occurred(tstate)); #endif + #if _Py_TIER2 + if (IS_JIT_TRACING()) { + LEAVE_TRACING(); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = stop_tracing_and_jit(tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + (void)res; + assert(res == 0); + } + #endif assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); if (!_PyFrame_IsIncomplete(frame)) { diff --git a/Python/optimizer.c b/Python/optimizer.c index 79ac179d0b710a..a06294cec1bff0 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1030,11 +1030,11 @@ _PyJit_TryInitializeTracing( // Don't error, just go to next instruction. return 0; } + _tstate->jit_tracer_state->is_tracing = false; } _PyJitTracerState *tracer = _tstate->jit_tracer_state; // A recursive trace. - // Don't trace into the inner call because it will stomp on the previous trace, causing endless retraces. - if (tracer->prev_state.code_curr_size > CODE_SIZE_EMPTY) { + if (tracer->is_tracing) { return 0; } if (oparg > 0xFFFF) { @@ -1086,6 +1086,7 @@ _PyJit_TryInitializeTracing( close_loop_instr[1].counter = trigger_backoff_counter(); } _Py_BloomFilter_Init(&tracer->prev_state.dependencies); + tracer->is_tracing = true; return 1; } @@ -1100,6 +1101,7 @@ _PyJit_FinalizeTracing(PyThreadState *tstate) Py_CLEAR(tracer->prev_state.instr_code); tracer->prev_state.code_curr_size = CODE_SIZE_EMPTY; tracer->prev_state.code_max_size = UOP_MAX_TRACE_LENGTH/2 - 1; + tracer->is_tracing = false; } void