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.
This commit is contained in:
Alan Wu 2021-07-19 20:25:18 -04:00
Родитель f82e3599c4
Коммит 1e0f2e4b09
1 изменённых файлов: 12 добавлений и 7 удалений

Просмотреть файл

@ -3452,15 +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;
if (insn_id == BIN(opt_neq)) {
VALUE *old_operands = iobj->operands;
// 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] = old_operands[0];
iobj->operands = new_operands;
}
else {
iobj->insn_id = insn_id;
iobj->operand_size = insn_len(insn_id) - 1;
}
return COMPILE_OK;