gh-141786 Fix missing parent executor during trace

An executor's trace can point to another executor, forming a graph
of traces / executors. Sometimes it is possible while recording a
trace that the parent executor is freed / invalidated halfway. This
leads to the bug described in the issue gh-141786, which if left
unfixed could cause a memory leak.

This patch checks for the validity of the parent executor as well
as allowing JIT from the cold executor. While the cold executor is
not linked, it is the executor responsible for creating side traces
and we still want to JIT from it.
This commit is contained in:
Cajetan Rodrigues 2025-12-05 10:29:15 +01:00
parent 53ec7c8fc0
commit 63662b9178
2 changed files with 11 additions and 2 deletions

View file

@ -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();

View file

@ -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;
}