Fixed missed type store

Fixes oss-fuzz #63601
This commit is contained in:
Dmitry Stogov 2023-11-01 14:26:01 +03:00
parent 7fcbedd7df
commit aa7465a435
3 changed files with 122 additions and 15 deletions

View file

@ -1095,24 +1095,62 @@ static void zend_jit_allocate_registers(zend_jit_ctx *ctx, const zend_op_array *
/* Remove intervals used once */ /* Remove intervals used once */
for (i = 0; i < ssa->vars_count; i++) { for (i = 0; i < ssa->vars_count; i++) {
if (ra[i].ref && if (ra[i].ref) {
(ra[i].flags & ZREG_LOAD) && if (!(ra[i].flags & (ZREG_LOAD|ZREG_STORE))) {
(ra[i].flags & ZREG_STORE) && uint32_t var_num = ssa->vars[i].var;
(ssa->vars[i].use_chain < 0 || uint32_t op_num = ssa->vars[i].definition;
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) { /* Check if a tempoary variable may be freed by exception handler */
if (ra[phi->ssa_var].ref && if (op_array->last_live_range
!(ra[phi->ssa_var].flags & ZREG_LOAD)) { && var_num >= op_array->last_var
may_remove = 0; && ssa->vars[i].definition >= 0
break; && 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) { if ((ra[i].flags & ZREG_LOAD)
ra[i].ref = IR_UNUSED; && (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;
}
} }
} }
} }

View file

@ -6174,6 +6174,56 @@ done:
} else { } else {
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type, 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))); (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) { if (ssa->var_info[ssa_op->result_def].type & MAY_BE_INDIRECT) {
RESET_STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)); RESET_STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var));
} }

View file

@ -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--
<?php
$a = 0;
for($i = 5; $i >= 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