summaryrefslogtreecommitdiff
path: root/compile.c
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2021-07-19 20:25:18 -0400
committerAlan Wu <XrXr@users.noreply.github.com>2021-10-20 18:19:38 -0400
commit736eb29a3c8f5ee18b76c98428350c8ab8b361a0 (patch)
treecf57e1627c25626bcd85fef8a6bb8c7c7a181d80 /compile.c
parenta0bb731f4f71ad3e57be57fb0a1a7347107dbf0d (diff)
downloadruby-736eb29a3c8f5ee18b76c98428350c8ab8b361a0.tar.gz
Fix use-after-free on USE_EMBED_CI=0
The old code didn't keep old_operands[0] reachable while allocating. You can crash it by requiring erb under GC stress mode.
Diffstat (limited to 'compile.c')
-rw-r--r--compile.c18
1 files changed, 11 insertions, 7 deletions
diff --git a/compile.c b/compile.c
index b11650c885..823d6b5718 100644
--- a/compile.c
+++ b/compile.c
@@ -3452,16 +3452,20 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
static int
insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
{
- iobj->insn_id = insn_id;
- iobj->operand_size = insn_len(insn_id) - 1;
- iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN;
-
if (insn_id == BIN(opt_neq)) {
+ // Be careful to not write to iobj before allocating so the old operand stays alive.
VALUE original_ci = iobj->operands[0];
+ VALUE *new_operands = compile_data_calloc2(iseq, 2, sizeof(VALUE));
+ new_operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
+ new_operands[1] = original_ci;
+
+ iobj->insn_id = insn_id;
iobj->operand_size = 2;
- iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE));
- iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
- iobj->operands[1] = original_ci;
+ iobj->operands = new_operands;
+ }
+ else {
+ iobj->insn_id = insn_id;
+ iobj->operand_size = insn_len(insn_id) - 1;
}
return COMPILE_OK;