summaryrefslogtreecommitdiff
path: root/compile.c
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-09-06 10:27:57 -0400
committerPeter Zhu <peter@peterzhu.ca>2023-09-06 11:18:50 -0400
commitb3b57f70cc1ee6f40ff10b2abaa51889abce2331 (patch)
tree7e5fe9d9c062564a3ab912e923707bb36e5fce97 /compile.c
parenta52ac350ccb8eb2ab394548844ffe681f9f02d0f (diff)
Fix missing write barrier in iseq instruction list
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" ```
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/8385
Diffstat (limited to 'compile.c')
-rw-r--r--compile.c53
1 files changed, 36 insertions, 17 deletions
diff --git a/compile.c b/compile.c
index 4d344fff9c..a165690597 100644
--- a/compile.c
+++ b/compile.c
@@ -1282,6 +1282,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)
@@ -1299,6 +1325,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_and_move(VALUE *obj_ptr, VALUE _)
+{
+ rb_gc_mark_and_move(obj_ptr);
+}
+
void
rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage)
{
@@ -10761,23 +10796,7 @@ rb_iseq_mark_and_move_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.
- rb_gc_mark_and_move(&OPERAND_AT(iobj, j));
- break;
- default:
- break;
- }
- }
+ iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_move, (VALUE)0);
}
pos += (int)size;
}