YJIT: Fix missing argc check in known cfuncs

Previously we were missing a compile-time check that the known cfuncs
receive the correct number of arguments.

We noticied this because in particular when using ARGS_SPLAT, which also
wasn't checked, YJIT would crash on code which was otherwise correct
(didn't raise exceptions in the VM).

This still supports vararg (argc == -1) cfuncs. I added an additional
assertion that when we use the specialized codegen for one of these
known functions that the argc are popped off the stack correctly, which
should help ensure they're implemented correctly (previously the crash
was usually observed on a future `leave` insn).

[Bug #19595]
This commit is contained in:
John Hawthorn 2023-04-12 16:22:22 -07:00
parent c8d8207aea
commit 0ce2bdc76d
Notes: git 2023-04-13 00:48:59 +00:00
2 changed files with 35 additions and 1 deletions

View File

@ -1227,6 +1227,38 @@ assert_equal '42', %q{
run
}
# splatting an empty array on a specialized method
assert_equal 'ok', %q{
def run
"ok".to_s(*[])
end
run
run
}
# splatting an single element array on a specialized method
assert_equal '[1]', %q{
def run
[].<<(*[1])
end
run
run
}
# specialized method with wrong args
assert_equal 'ok', %q{
def run(x)
"bad".to_s(123) if x
rescue
:ok
end
run(false)
run(true)
}
# getinstancevariable on Symbol
assert_equal '[nil, nil]', %q{
# @foo to exercise the getinstancevariable instruction

View File

@ -4987,10 +4987,12 @@ fn gen_send_cfunc(
}
// Delegate to codegen for C methods if we have it.
if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 {
if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 && flags & VM_CALL_ARGS_SPLAT == 0 && (cfunc_argc == -1 || argc == cfunc_argc) {
let codegen_p = lookup_cfunc_codegen(unsafe { (*cme).def });
let expected_stack_after = asm.ctx.get_stack_size() as i32 - argc;
if let Some(known_cfunc_codegen) = codegen_p {
if known_cfunc_codegen(jit, asm, ocb, ci, cme, block, argc, recv_known_klass) {
assert_eq!(expected_stack_after, asm.ctx.get_stack_size() as i32);
// cfunc codegen generated code. Terminate the block so
// there isn't multiple calls in the same block.
jump_to_next_insn(jit, asm, ocb);