From aa7465a435206555dfe61e415db8292bcfec2dd7 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 1 Nov 2023 14:26:01 +0300 Subject: [PATCH] Fixed missed type store Fixes oss-fuzz #63601 --- ext/opcache/jit/zend_jit.c | 68 ++++++++++++++++++------ ext/opcache/jit/zend_jit_trace.c | 50 +++++++++++++++++ ext/opcache/tests/jit/reg_alloc_021.phpt | 19 +++++++ 3 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 ext/opcache/tests/jit/reg_alloc_021.phpt diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 2380e49f3b8..36b639ac650 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1095,24 +1095,62 @@ static void zend_jit_allocate_registers(zend_jit_ctx *ctx, const zend_op_array * /* Remove intervals used once */ for (i = 0; i < ssa->vars_count; i++) { - if (ra[i].ref && - (ra[i].flags & ZREG_LOAD) && - (ra[i].flags & ZREG_STORE) && - (ssa->vars[i].use_chain < 0 || - zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) { - bool may_remove = 1; - zend_ssa_phi *phi = ssa->vars[i].phi_use_chain; + if (ra[i].ref) { + if (!(ra[i].flags & (ZREG_LOAD|ZREG_STORE))) { + uint32_t var_num = ssa->vars[i].var; + uint32_t op_num = ssa->vars[i].definition; - while (phi) { - if (ra[phi->ssa_var].ref && - !(ra[phi->ssa_var].flags & ZREG_LOAD)) { - may_remove = 0; - break; + /* Check if a tempoary variable may be freed by exception handler */ + if (op_array->last_live_range + && var_num >= op_array->last_var + && ssa->vars[i].definition >= 0 + && ssa->ops[op_num].result_def == i) { + const zend_live_range *range = op_array->live_range; + int j; + + op_num++; + if (op_array->opcodes[op_num].opcode == ZEND_OP_DATA) { + op_num++; + } + for (j = 0; j < op_array->last_live_range; range++, j++) { + if (range->start > op_num) { + /* further blocks will not be relevant... */ + break; + } else if (op_num < range->end && var_num == (range->var & ~ZEND_LIVE_MASK)) { + /* check if opcodes in range may throw */ + do { + if (zend_may_throw(op_array->opcodes + op_num, ssa->ops + op_num, op_array, ssa)) { + ra[i].flags |= ZREG_STORE; + break; + } + op_num++; + if (op_array->opcodes[op_num].opcode == ZEND_OP_DATA) { + op_num++; + } + } while (op_num < range->end); + break; + } + } } - phi = zend_ssa_next_use_phi(ssa, i, phi); } - if (may_remove) { - ra[i].ref = IR_UNUSED; + if ((ra[i].flags & ZREG_LOAD) + && (ra[i].flags & ZREG_STORE) + && (ssa->vars[i].use_chain < 0 + || zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) { + bool may_remove = 1; + zend_ssa_phi *phi = ssa->vars[i].phi_use_chain; + + while (phi) { + if (ra[phi->ssa_var].ref && + !(ra[phi->ssa_var].flags & ZREG_LOAD)) { + may_remove = 0; + break; + } + phi = zend_ssa_next_use_phi(ssa, i, phi); + } + if (may_remove) { + ra[i].ref = IR_UNUSED; + } } } } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 4e97c452e2b..ee5170467c5 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6174,6 +6174,56 @@ done: } else { SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type, (gen_handler || type == IS_UNKNOWN || !ra || !RA_HAS_REG(ssa_op->result_def))); + + if (op_array->last_live_range + && opline->result.var > op_array->last_var + && STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)) != type) { + if (!gen_handler && type != IS_UNKNOWN && ra && RA_HAS_REG(ssa_op->result_def)) { + uint32_t var_num = opline->result.var; + uint32_t op_num = opline - op_array->opcodes; + const zend_live_range *range = op_array->live_range; + int j; + + op_num += zend_jit_trace_op_len(opline); + for (j = 0; j < op_array->last_live_range; range++, j++) { + if (range->start > op_num) { + /* further blocks will not be relevant... */ + break; + } else if (op_num < range->end && var_num == (range->var & ~ZEND_LIVE_MASK)) { + /* check if opcodes in range may throw */ + bool store_type = 0; + const zend_ssa_op *next_ssa_op = ssa_op + zend_jit_trace_op_len(opline); + const zend_jit_trace_rec *q = p + 1; + + while (1) { + if (q->op != ZEND_JIT_TRACE_VM) { + store_type = 1; + break; + } + op_num = q->opline - op_array->opcodes; + if (op_num >= range->end || op_num < range->start) { + break; + } + if (zend_may_throw(q->opline, next_ssa_op, op_array, ssa)) { + store_type = 1; + break; + } + next_ssa_op += zend_jit_trace_op_len(q->opline); + q++; + } + if (store_type) { + var_num = EX_VAR_TO_NUM(var_num); + + if (!zend_jit_store_type(&ctx, var_num, type)) { + return 0; + } + SET_STACK_TYPE(stack, var_num, type, 1); + } + break; + } + } + } + } if (ssa->var_info[ssa_op->result_def].type & MAY_BE_INDIRECT) { RESET_STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)); } diff --git a/ext/opcache/tests/jit/reg_alloc_021.phpt b/ext/opcache/tests/jit/reg_alloc_021.phpt new file mode 100644 index 00000000000..7774a2252de --- /dev/null +++ b/ext/opcache/tests/jit/reg_alloc_021.phpt @@ -0,0 +1,19 @@ +--TEST-- +Register Alloction 021: TMP variables captured by live_ranges have to be stored +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- += 0; $i--) { + $a = 1 + ++$a - 5 % $i; +} +?> +--EXPECTF-- +Fatal error: Uncaught DivisionByZeroError: Modulo by zero in %sreg_alloc_021.php:4 +Stack trace: +#0 {main} + thrown in %sreg_alloc_021.php on line 4