Skip to content

Commit 06b7a70

Browse files
authored
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.
1 parent 39d764e commit 06b7a70

File tree

1 file changed

+13
-6
lines changed

1 file changed

+13
-6
lines changed

vm_backtrace.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,15 +1761,22 @@ thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *b
17611761
}
17621762

17631763
if (lines) {
1764-
// The topmost frame may not have an updated PC because the JIT
1765-
// may not have set one. The JIT compiler will update the PC
1766-
// before entering a new function (so that `caller` will work),
1767-
// so only the topmost frame could possibly have an out of date PC
1768-
if (cfp == top && cfp->jit_return) {
1764+
const VALUE *pc = cfp->pc;
1765+
VALUE *iseq_encoded = ISEQ_BODY(cfp->iseq)->iseq_encoded;
1766+
VALUE *pc_end = iseq_encoded + ISEQ_BODY(cfp->iseq)->iseq_size;
1767+
1768+
// The topmost frame may have an invalid PC because the JIT
1769+
// may leave it uninitialized for speed. JIT code must update the PC
1770+
// before entering a non-leaf method (so that `caller` will work),
1771+
// so only the topmost frame could possibly have an out-of-date PC.
1772+
// ZJIT doesn't set `cfp->jit_return`, so it's not a reliable signal.
1773+
//
1774+
// Avoid passing invalid PC to calc_lineno() to avoid crashing.
1775+
if (cfp == top && (pc < iseq_encoded || pc > pc_end)) {
17691776
lines[i] = 0;
17701777
}
17711778
else {
1772-
lines[i] = calc_lineno(cfp->iseq, cfp->pc);
1779+
lines[i] = calc_lineno(cfp->iseq, pc);
17731780
}
17741781
}
17751782

0 commit comments

Comments
 (0)