@@ -283,14 +283,19 @@ static void ruby_runtime_stack_callback(
283283 iseq -> body -> iseq_size > 0 ) {
284284 ptrdiff_t pc_offset = cfp -> pc - iseq -> body -> iseq_encoded ;
285285
286- // bounds checking like private_vm_api_access.c PROF-11475 fix
286+ // Enhanced bounds checking like private_vm_api_access.c PROF-11475 fix
287+ // to prevent crashes when calling rb_iseq_line_no
287288 if (pc_offset >= 0 && pc_offset <= iseq -> body -> iseq_size ) {
288289 size_t pos = (size_t )pc_offset ;
289290 if (pos > 0 ) {
290291 // Use pos-1 because PC points to next instruction
291292 pos -- ;
292293 }
293- line_no = rb_iseq_line_no (iseq , pos );
294+
295+ // Additional safety check before calling rb_iseq_line_no (PROF-11475 fix)
296+ if (pos < iseq -> body -> iseq_size ) {
297+ line_no = rb_iseq_line_no (iseq , pos );
298+ }
294299 }
295300 }
296301 }
@@ -310,7 +315,9 @@ static void ruby_runtime_stack_callback(
310315 const char * function_name = "<C method>" ;
311316 const char * file_name = "<C extension>" ;
312317
313- // Try to get method entry information
318+ #ifdef RUBY_MJIT_HEADER
319+ // Only attempt method entry resolution on Ruby versions with MJIT header (2.6-3.2)
320+ // where rb_vm_frame_method_entry is guaranteed to be available
314321 const rb_callable_method_entry_t * me = rb_vm_frame_method_entry (cfp );
315322 if (me && is_pointer_readable (me , sizeof (rb_callable_method_entry_t ))) {
316323 if (me -> def && is_pointer_readable (me -> def , sizeof (* me -> def ))) {
@@ -358,6 +365,11 @@ static void ruby_runtime_stack_callback(
358365 }
359366 }
360367 }
368+ #else
369+ // For Ruby versions without MJIT header (2.5, 3.3+), skip complex method resolution
370+ // to avoid compatibility issues with rb_vm_frame_method_entry
371+ // This provides basic C frame info which is still useful
372+ #endif
361373
362374 ddog_crasht_RuntimeStackFrame frame = {
363375 .type_name = char_slice_from_cstr (NULL ),
0 commit comments