From fc49490b07f055942e684952061b0e641de4a45b Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Wed, 13 Sep 2023 10:59:44 -0400 Subject: [PATCH] Fix missing write barrier in iseq instruction list [Bug #19880] There's a missing write barrier for operands in the iseq instruction list, which can cause crashes. It can be reproduced when Ruby is compiled with `-DRUBY_DEBUG_ENV=1`. Using the following command: ``` RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0 RUBY_DEBUG=gc_stress ruby -w --disable=gems -Itool/lib -W0 test.rb ``` The following script crashes: ``` require "test/unit" ``` --- compile.c | 59 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/compile.c b/compile.c index da89d0e0cadd11..a1260feaf1a81d 100644 --- a/compile.c +++ b/compile.c @@ -1243,6 +1243,32 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line) return adjust; } +static void +iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE *, VALUE), VALUE data) +{ + const char *types = insn_op_types(insn->insn_id); + for (int j = 0; types[j]; j++) { + char type = types[j]; + switch (type) { + case TS_CDHASH: + case TS_ISEQ: + case TS_VALUE: + case TS_IC: // constant path array + case TS_CALLDATA: // ci is stored. + func(&OPERAND_AT(insn, j), data); + break; + default: + break; + } + } +} + +static void +iseq_insn_each_object_write_barrier(VALUE *obj_ptr, VALUE iseq) +{ + RB_OBJ_WRITTEN(iseq, Qundef, *obj_ptr); +} + static INSN * new_insn_core(rb_iseq_t *iseq, const NODE *line_node, int insn_id, int argc, VALUE *argv) @@ -1260,6 +1286,9 @@ new_insn_core(rb_iseq_t *iseq, const NODE *line_node, iobj->operands = argv; iobj->operand_size = argc; iobj->sc_state = 0; + + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq); + return iobj; } @@ -10735,6 +10764,12 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords) return keyword; } +static void +iseq_insn_each_object_mark(VALUE *obj_ptr, VALUE _) +{ + rb_gc_mark(*obj_ptr); +} + void rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage) { @@ -10761,29 +10796,7 @@ rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage) iobj = (INSN *)&storage->buff[pos]; if (iobj->operands) { - int j; - const char *types = insn_op_types(iobj->insn_id); - - for (j = 0; types[j]; j++) { - char type = types[j]; - switch (type) { - case TS_CDHASH: - case TS_ISEQ: - case TS_VALUE: - case TS_IC: // constant path array - case TS_CALLDATA: // ci is stored. - { - VALUE op = OPERAND_AT(iobj, j); - - if (!SPECIAL_CONST_P(op)) { - rb_gc_mark(op); - } - } - break; - default: - break; - } - } + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark, (VALUE)0); } pos += (int)size; }