From 06b7a70837d831b8628ae2adde9318371c111d82 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 23 Sep 2025 16:20:04 -0400 Subject: Fix thread_profile_frames crashing due to uninitialized PC ZJIT never sets `cfp->jit_return`, so to avoid crashing while profiling, we need to explicitly validate the PC of the top most frame. Particularly pertinent for profilers that call rb_profile_frames() from within a signal handler such as Vernier and Stackprof since they can sample at any time and observe an invalid PC. --- vm_backtrace.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/vm_backtrace.c b/vm_backtrace.c index aaa0b5051c..e81c568dda 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -1761,15 +1761,22 @@ thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *b } if (lines) { - // The topmost frame may not have an updated PC because the JIT - // may not have set one. The JIT compiler will update the PC - // before entering a new function (so that `caller` will work), - // so only the topmost frame could possibly have an out of date PC - if (cfp == top && cfp->jit_return) { + const VALUE *pc = cfp->pc; + VALUE *iseq_encoded = ISEQ_BODY(cfp->iseq)->iseq_encoded; + VALUE *pc_end = iseq_encoded + ISEQ_BODY(cfp->iseq)->iseq_size; + + // The topmost frame may have an invalid PC because the JIT + // may leave it uninitialized for speed. JIT code must update the PC + // before entering a non-leaf method (so that `caller` will work), + // so only the topmost frame could possibly have an out-of-date PC. + // ZJIT doesn't set `cfp->jit_return`, so it's not a reliable signal. + // + // Avoid passing invalid PC to calc_lineno() to avoid crashing. + if (cfp == top && (pc < iseq_encoded || pc > pc_end)) { lines[i] = 0; } else { - lines[i] = calc_lineno(cfp->iseq, cfp->pc); + lines[i] = calc_lineno(cfp->iseq, pc); } } -- cgit v1.2.3