diff --git a/compile.c b/compile.c index d2de521ec8..44fb53cefd 100644 --- a/compile.c +++ b/compile.c @@ -10761,7 +10761,6 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod /* operands */ for (op_index=0; types[op_index]; op_index++, code_index++) { switch (types[op_index]) { - case TS_CDHASH: case TS_VALUE: { VALUE op = ibf_load_small_value(load, &reading_pos); @@ -10773,6 +10772,20 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod } break; } + case TS_CDHASH: + { + VALUE op = ibf_load_small_value(load, &reading_pos); + VALUE v = ibf_load_object(load, op); + v = rb_hash_dup(v); // hash dumped as frozen + RHASH_TBL_RAW(v)->type = &cdhash_type; + rb_hash_rehash(v); // hash function changed + freeze_hide_obj(v); + + code[code_index] = v; + RB_OBJ_WRITTEN(iseqv, Qundef, v); + FL_SET(iseqv, ISEQ_MARKABLE_ISEQ); + break; + } case TS_ISEQ: { VALUE op = (VALUE)ibf_load_small_value(load, &reading_pos); diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index e3ca1ba926..0cde1fbb7a 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -82,6 +82,19 @@ class TestISeq < Test::Unit::TestCase end; end if defined?(RubyVM::InstructionSequence.load) + def test_cdhash_after_roundtrip + # CDHASH was not built properly when loading from binary and + # was causing opt_case_dispatch to clobber its stack canary + # for its "leaf" instruction attribute. + iseq = compile(<<~EOF) + case Class.new(String).new("foo") + when "foo" + 42 + end + EOF + assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + end + def test_disasm_encoding src = "\u{3042} = 1; \u{3042}; \u{3043}" asm = compile(src).disasm