diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 51234a2e40f54f..0f67f19a115d83 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2695,6 +2695,144 @@ def recursive_wrapper_4569(): pass """)) + def test_executor_invalidation_does_not_crash(self): + script_helper.assert_python_ok("-c", textwrap.dedent(""" + import random + + rand = random.Random(38720) + import sys + + def b(): + for c in range(23): + rand.random() + rand.random() + if rand.random() > 0.1: + rand.random() + rand.random() + rand.random() + for c in range(4): + rand.random() + if rand.random(): + 0 + if rand.random() > 0.1: + rand.random() + rand.random() + if rand.random(): + rand.random() + if rand.random(): + rand.random() + rand.random() + if rand.random(): + rand.random() + rand.random() + if rand.random(): + if rand.random() > 0.1: + rand.random() + if rand.random() < rand.random(): + ... + if rand.random() < rand.random(): + ... + if rand.random(): + ... + if rand.random() > 0.1: + rand.random() + rand.random() + for c in range(12): + rand.random() + + def random(B): + if rand.random() < 0.1: + random(B) + + def Random(B): + if rand.random(): + random(B) + + def b(B): + if rand.random(): + Random(B) + + def c(B): + if rand.random() < 0.1: + b(B) + + def a(B): + if rand.random() > 0.1: + c(B) + + def A(B): + if rand.random() > 0.1: + a(B) + + def C(B): + if rand.random() > 0.1: + A(B) + + def B(B): + if rand.random() < 0.1: + C(B) + + for D in range(200): + try: + B(D) + except: + ... + + class A: + def __del__(C): + b = sys._getframe(1) + exec("D=0", b.f_globals, b.f_locals) + + B = A() + if rand.random() < 0.1: + if rand.random() < 0.1: + if rand.random() < 0.1: + for D in range(D): + if rand.random() < 0.1: + if rand.random() > 0.1: + if rand.random(): + if rand.random() > rand.random(): + C + if rand.random() < 0.1: + if rand.random() < 0.1: + if rand.random() < 0.1: + del B + if rand.random() < 0.1: + if rand.random() < 0.1: + if rand.random(): + 0 + if rand.random() > 0.1: + if rand.random() > 0.1: + rand.random() + rand.random() + if rand.random() > 0.1: + rand.random() + rand.random() + if rand.random() > 0.1: + if rand.random() > 0.1: + rand.random() + rand.random() + rand.random() + rand.random() + if rand.random() > 0.1: + if rand.random() > 0.1: + rand.random() + if rand.random() > rand.random(): + 0 + if rand.random(): + C + if rand.random() > 0.1: + rand.random() + rand.random() + for c in range(3): + if rand.random(): + 0 + for c in range(2): + rand.random() + + for D in range(5064): + b() + """)) def global_identity(x): return x diff --git a/Python/ceval.c b/Python/ceval.c index 46bf644106ac39..06500ea09fbe87 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1408,7 +1408,9 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) // Likewise, we hold a strong reference to the executor containing this exit, so the exit is guaranteed // to be valid to access. if (err <= 0) { - exit->temperature = restart_backoff_counter(exit->temperature); + if (exit->executor->vm_data.linked && exit->executor->vm_data.valid) { + exit->temperature = restart_backoff_counter(exit->temperature); + } } else { exit->temperature = initial_temperature_backoff_counter(); diff --git a/Python/optimizer.c b/Python/optimizer.c index 9db894f0bf054a..1cbb1ffd16d5d1 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -140,6 +140,14 @@ _PyOptimizer_Optimize( } assert(!interp->compiling); assert(_tstate->jit_tracer_state.initial_state.stack_depth >= 0); + _PyExitData *exit = _tstate->jit_tracer_state.initial_state.exit; + _PyExecutorObject *cold_executor = _PyExecutor_GetColdExecutor(); + if (exit != NULL && + (!exit->executor->vm_data.linked || !exit->executor->vm_data.valid) && + exit->executor != cold_executor) { + // gh-141786 Parent executor is either unlinked or invalid - cannot optimize. + return 0; + } #ifndef Py_GIL_DISABLED assert(_tstate->jit_tracer_state.initial_state.func != NULL); interp->compiling = true; @@ -185,7 +193,6 @@ _PyOptimizer_Optimize( else { executor->vm_data.code = NULL; } - _PyExitData *exit = _tstate->jit_tracer_state.initial_state.exit; if (exit != NULL) { exit->executor = executor; }