summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2024-06-04 16:17:41 -0400
committerGitHub <noreply@github.com>2024-06-04 13:17:41 -0700
commitb74f669e2fbe5c63409878e7a9f9d39c8554ff77 (patch)
tree2659d02be0944a1c5080aecc8251a5a61befa77d
parent1df1538be4a494bbc5ef6e3504312a0284948709 (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.rb11
-rw-r--r--yjit/src/codegen.rs26
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);