Skip to content

Commit

Permalink
YJIT: Fix String#setbyte crashing for converted arguments
Browse files Browse the repository at this point in the history
Previously, passing objects that respond to #to_int to `String#setbyte`
resulted in a crash when compiled by YJIT. This was due to the lazily
pushed frame from rb_yjit_lazy_push_frame() lingering and not being
popped by an exception as expected.

Since the lazy frame push infrastructure doesn't work when the C
function calls a method, we need to restore fixnum guards to avoid
crashing.

Found running `ruby/spec`.
  • Loading branch information
XrXr committed Apr 19, 2024
1 parent b7c4c88 commit 4f9d531
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
17 changes: 17 additions & 0 deletions bootstraptest/test_yjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4786,3 +4786,20 @@ def tests
tests
}

# test String#stebyte with arguments that need conversion
assert_equal "abc", %q{
str = +"a00"
def change_bytes(str, one, two)
str.setbyte(one, "b".ord)
str.setbyte(2, two)
end
to_int_1 = Object.new
to_int_99 = Object.new
def to_int_1.to_int = 1
def to_int_99.to_int = 99
change_bytes(str, to_int_1, to_int_99)
str
}
12 changes: 11 additions & 1 deletion yjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5718,19 +5718,29 @@ fn jit_rb_str_getbyte(
fn jit_rb_str_setbyte(
jit: &mut JITState,
asm: &mut Assembler,
_ocb: &mut OutlinedCb,
ocb: &mut OutlinedCb,
_ci: *const rb_callinfo,
cme: *const rb_callable_method_entry_t,
_block: Option<BlockHandler>,
_argc: i32,
_known_recv_class: Option<VALUE>,
) -> bool {
// Check that arguments are fixnums since jit_prepare_lazy_frame_call()
// requires rb_str_setbyte() to be leaf or raise, but it tries to
// convert non-integer arguments by calling #to_int.
if !matches!(asm.ctx.two_fixnums_on_stack(jit), Some(true)) {
return false;
}

// Raises when index is out of range. Lazily push a frame in that case.
if !jit_prepare_lazy_frame_call(jit, asm, cme, StackOpnd(2)) {
return false;
}
asm_comment!(asm, "String#setbyte");

// Check that arguments are fixnums
guard_two_fixnums(jit, asm, ocb);

let value = asm.stack_opnd(0);
let index = asm.stack_opnd(1);
let recv = asm.stack_opnd(2);
Expand Down

0 comments on commit 4f9d531

Please sign in to comment.