summaryrefslogtreecommitdiff
path: root/vm_backtrace.c
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2023-09-11 14:11:46 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2023-09-15 09:01:02 -0700
commita1dc1a3de9683daf5a543d6f618e17aabfcb8708 (patch)
tree4929f01ae37c5d1fd81ae11d5ddcde8a9300bcbc /vm_backtrace.c
parentefe2822708d62bfe3ba858da526e6ef7d4724959 (diff)
Return line 0 for JIT frames
Frames pushed by YJIT have an unreliable PC. The PC could be garbage, and if we try to read the line number with a garbage PC, then the program can crash. This commit returns line 0 for programs where there is a `jit_return` function. If `jit_return` has been set then this frame was pushed by the JIT, and we cannot trust the PC. Here is a debugger session for a program that crashed due to a broken PC: ``` (lldb) p ruby_current_vm_ptr->ractor.main_thread->ec->cfp->iseq->body->iseq_encoded (VALUE *) $0 = 0x0000000118a30e00 (lldb) p/x ruby_current_vm_ptr->ractor.main_thread->ec->cfp->pc (const VALUE *) $1 = 0x0000600000b02d00 (lldb) p/x ruby_current_vm_ptr->ractor.main_thread->ec->cfp->jit_return (void *) $2 = 0x000000010622942c ``` You can see the PC is completely out of range, but there is a `jit_return` pointer so we can avoid this crash.
Diffstat (limited to 'vm_backtrace.c')
-rw-r--r--vm_backtrace.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/vm_backtrace.c b/vm_backtrace.c
index e46eaa5b81..42debfba3d 100644
--- a/vm_backtrace.c
+++ b/vm_backtrace.c
@@ -1583,6 +1583,7 @@ rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
int i;
const rb_execution_context_t *ec = GET_EC();
const rb_control_frame_t *cfp = ec->cfp, *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
+ const rb_control_frame_t *top = cfp;
const rb_callable_method_entry_t *cme;
// If this function is called inside a thread after thread creation, but
@@ -1613,7 +1614,18 @@ rb_profile_frames(int start, int limit, VALUE *buff, int *lines)
buff[i] = (VALUE)cfp->iseq;
}
- if (lines) lines[i] = calc_lineno(cfp->iseq, cfp->pc);
+ 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) {
+ lines[i] = 0;
+ }
+ else {
+ lines[i] = calc_lineno(cfp->iseq, cfp->pc);
+ }
+ }
i++;
}