From 6c8ae44a388e5c03b7db90376af3652007b574e8 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 31 May 2024 17:37:21 -0400 Subject: [PATCH] YJIT: Fix out of bounds access when splatting empty array 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] --- bootstraptest/test_yjit.rb | 12 ++++++++++++ yjit/src/codegen.rs | 26 +++++++++++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 8b8f7d1e39..76406ab601 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -4980,3 +4980,15 @@ assert_equal '[[:c_call, :x=], [:c_call, :x]]', %q{ events } + +# regression test for splatting empty array +assert_equal '1', %q{ + def callee(foo) = foo + + def test_body(args) = callee(1, *args) + + test_body([]) + array = Array.new(100) + array.clear + test_body(array) +} diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index f426dd87ca..d86f0d1955 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -6953,20 +6953,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"); + // Check last element of array if present + if required_args > 0 { + 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))); - - guard_object_is_not_ruby2_keyword_hash( - asm, - last_array_value, - Counter::guard_send_splatarray_last_ruby2_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_ruby2_keywords, + ); + } asm_comment!(asm, "Push arguments from array"); let array_opnd = asm.stack_pop(1);