diff options
| author | Alan Wu <XrXr@users.noreply.github.com> | 2024-06-04 16:17:41 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-04 13:17:41 -0700 |
| commit | b74f669e2fbe5c63409878e7a9f9d39c8554ff77 (patch) | |
| tree | 2659d02be0944a1c5080aecc8251a5a61befa77d | |
| parent | 1df1538be4a494bbc5ef6e3504312a0284948709 (diff) | |
YJIT: Fix out of bounds access when splatting empty array (#10905)
This is a backport of 6c8ae44a388e5c03b7db90376af3652007b574e8 with a
test tailored to crash the 3.3.x branch (from GH-10904).
Previously, we read the last element array even when the array was
empty, doing an out-of-bounds access. This sometimes caused a SEGV.
[Bug #20496]
| -rw-r--r-- | bootstraptest/test_yjit.rb | 11 | ||||
| -rw-r--r-- | yjit/src/codegen.rs | 26 |
2 files changed, 24 insertions, 13 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 258eef48dc..2865c0ec09 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -4454,3 +4454,14 @@ assert_equal '[[{:a=>1}], {}]', %q{ body } + +# regression test for splatting empty array to cfunc +assert_normal_exit %q{ + def test_body(args) = Array(1, *args) + test_body([]) + 0x100.times do + array = Array.new(100) + array.clear + test_body(array) + end +} diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index ea00889c0c..ca987eaf78 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -5814,20 +5814,20 @@ fn push_splat_args(required_args: u32, asm: &mut Assembler) { asm.cmp(array_len_opnd, required_args.into()); asm.jne(Target::side_exit(Counter::guard_send_splatarray_length_not_equal)); - asm_comment!(asm, "Check last argument is not ruby2keyword hash"); - - // Need to repeat this here to deal with register allocation - let array_reg = asm.load(asm.stack_opnd(0)); - - let ary_opnd = get_array_ptr(asm, array_reg); - - let last_array_value = asm.load(Opnd::mem(64, ary_opnd, (required_args as i32 - 1) * (SIZEOF_VALUE as i32))); + // Check last element of array if present + if required_args > 0 { + asm_comment!(asm, "Check last argument is not ruby2keyword hash"); - guard_object_is_not_ruby2_keyword_hash( - asm, - last_array_value, - Counter::guard_send_splatarray_last_ruby_2_keywords, - ); + // Need to repeat this here to deal with register allocation + let array_reg = asm.load(asm.stack_opnd(0)); + let ary_opnd = get_array_ptr(asm, array_reg); + let last_array_value = asm.load(Opnd::mem(64, ary_opnd, (required_args as i32 - 1) * (SIZEOF_VALUE as i32))); + guard_object_is_not_ruby2_keyword_hash( + asm, + last_array_value, + Counter::guard_send_splatarray_last_ruby_2_keywords, + ); + } asm_comment!(asm, "Push arguments from array"); let array_opnd = asm.stack_pop(1); |
