From efcdf68e6443ab70fbff1703b9dabbfc5090df31 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Jan 2021 13:35:11 -0800 Subject: Guard callinfo Callinfo was being written in to an array and the GC would not see the reference on the stack. `new_insn_send` creates a new callinfo object, then it calls `new_insn_core`. `new_insn_core` allocates a new INSN linked list item, which can end up calling `xmalloc` which will trigger a GC: https://github.com/ruby/ruby/blob/70cd351c7c71c48ee18d7c01e851a89614086f8f/compile.c#L968-L969 Since the callinfo object isn't on the stack, the GC won't see it, and it can get collected. This patch just refactors `new_insn_send` to keep the object on the stack Co-authored-by: John Hawthorn --- compile.c | 7 +++++-- test/ruby/test_gc.rb | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compile.c b/compile.c index d1586acf31..388d476c68 100644 --- a/compile.c +++ b/compile.c @@ -1302,12 +1302,15 @@ static INSN * new_insn_send(rb_iseq_t *iseq, int line_no, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords) { VALUE *operands = compile_data_calloc2(iseq, sizeof(VALUE), 2); - operands[0] = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL); + VALUE ci = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL); + operands[0] = ci; operands[1] = (VALUE)blockiseq; if (blockiseq) { RB_OBJ_WRITTEN(iseq, Qundef, blockiseq); } - return new_insn_core(iseq, line_no, BIN(send), 2, operands); + INSN *insn = new_insn_core(iseq, line_no, BIN(send), 2, operands); + RB_GC_GUARD(ci); + return insn; } static rb_iseq_t * diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index daa8b5c37a..1f75a34cac 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -175,6 +175,16 @@ class TestGc < Test::Unit::TestCase assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.latest_gc_info(:"\u{30eb 30d3 30fc}")} end + def test_stress_compile_send + assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "") + GC.stress = true + begin + eval("A::B.c(1, 1, d: 234)") + rescue + end + EOS + end + def test_singleton_method assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]") GC.stress = true -- cgit v1.2.3