Fix SCDF cleanup of unused basic block, kept only because of FREE of a loop var

Fixes oss-fuzz #41516
This commit is contained in:
Dmitry Stogov 2021-12-01 18:03:08 +03:00
parent b594a95a2f
commit c3766c08f4
4 changed files with 30 additions and 6 deletions

View file

@ -224,10 +224,11 @@ static uint32_t cleanup_loop_var_free_block(scdf_ctx *scdf, zend_basic_block *bl
zend_ssa *ssa = scdf->ssa; zend_ssa *ssa = scdf->ssa;
const zend_op_array *op_array = scdf->op_array; const zend_op_array *op_array = scdf->op_array;
const zend_cfg *cfg = &ssa->cfg; const zend_cfg *cfg = &ssa->cfg;
int block_num = block - cfg->blocks;
uint32_t removed_ops = 0; uint32_t removed_ops = 0;
/* Removes phi nodes */ /* Removes phi nodes */
for (zend_ssa_phi *phi = ssa->blocks[block - cfg->blocks].phis; phi; phi = phi->next) { for (zend_ssa_phi *phi = ssa->blocks[block_num].phis; phi; phi = phi->next) {
zend_ssa_remove_uses_of_var(ssa, phi->ssa_var); zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
zend_ssa_remove_phi(ssa, phi); zend_ssa_remove_phi(ssa, phi);
} }
@ -235,7 +236,8 @@ static uint32_t cleanup_loop_var_free_block(scdf_ctx *scdf, zend_basic_block *bl
for (uint32_t i = block->start; i < block->start + block->len; i++) { for (uint32_t i = block->start; i < block->start + block->len; i++) {
zend_op *opline = &op_array->opcodes[i]; zend_op *opline = &op_array->opcodes[i];
zend_ssa_op *ssa_op = &scdf->ssa->ops[i]; zend_ssa_op *ssa_op = &scdf->ssa->ops[i];
if (is_live_loop_var_free(scdf, opline, ssa_op)) { if (opline->opcode == ZEND_NOP
|| is_live_loop_var_free(scdf, opline, ssa_op)) {
continue; continue;
} }
@ -243,10 +245,11 @@ static uint32_t cleanup_loop_var_free_block(scdf_ctx *scdf, zend_basic_block *bl
* in the block. */ * in the block. */
zend_ssa_remove_defs_of_instr(ssa, ssa_op); zend_ssa_remove_defs_of_instr(ssa, ssa_op);
zend_ssa_remove_instr(ssa, opline, ssa_op); zend_ssa_remove_instr(ssa, opline, ssa_op);
removed_ops++;
} }
/* This block has no predecessors anymore. */ zend_ssa_remove_block_from_cfg(ssa, block_num);
block->predecessors_count = 0;
return removed_ops; return removed_ops;
} }

View file

@ -1448,9 +1448,8 @@ void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{
{ {
zend_basic_block *block = &ssa->cfg.blocks[i]; zend_basic_block *block = &ssa->cfg.blocks[i];
zend_ssa_block *ssa_block = &ssa->blocks[i]; zend_ssa_block *ssa_block = &ssa->blocks[i];
int *predecessors;
zend_ssa_phi *phi; zend_ssa_phi *phi;
int j, s; int j;
block->flags &= ~ZEND_BB_REACHABLE; block->flags &= ~ZEND_BB_REACHABLE;
@ -1470,6 +1469,16 @@ void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{
zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]); zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]);
} }
zend_ssa_remove_block_from_cfg(ssa, i);
}
/* }}} */
void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int i) /* {{{ */
{
zend_basic_block *block = &ssa->cfg.blocks[i];
int *predecessors;
int j, s;
for (s = 0; s < block->successors_count; s++) { for (s = 0; s < block->successors_count; s++) {
zend_ssa_remove_predecessor(ssa, i, block->successors[s]); zend_ssa_remove_predecessor(ssa, i, block->successors[s]);
} }

View file

@ -157,6 +157,7 @@ void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num); void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b); void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b);
void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types); void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types);
void zend_ssa_remove_block_from_cfg(zend_ssa *ssa, int b);
static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var) static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var)
{ {

View file

@ -0,0 +1,11 @@
--TEST--
Cleanup of basic block kept only because of FREE loop var
--FILE--
<?php
var_dump(X||match(X and true or true){false=>X});
?>
--EXPECTF--
Fatal error: Uncaught Error: Undefined constant "X" in %smatch_scdf_cleanup.php:2
Stack trace:
#0 {main}
thrown in %smatch_scdf_cleanup.php on line 2