Fix self-assign evaluation order for ASSIGN_DIM_OP

For $ary[idx] op= $ary we should evaluate the RHS operand first,
otherwise we may create a reference-free recursive array. Use the
same handling we do for the normal $ary[idx] = $ary case.

Fixes oss-fuzz #40287.
This commit is contained in:
Nikita Popov 2021-11-01 09:36:22 +01:00
parent a4ed17167c
commit 55aadc647b
3 changed files with 41 additions and 30 deletions

View File

@ -0,0 +1,19 @@
--TEST--
Compound array assignment with same variable
--FILE--
<?php
function test() {
$ary = [[]];
$ary[0] += $ary;
foreach ($ary as $v) {
var_dump($v);
}
}
test();
?>
--EXPECT--
array(1) {
[0]=>
array(0) {
}
}

View File

@ -3152,6 +3152,22 @@ static bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{
}
/* }}} */
static void zend_compile_expr_with_potential_assign_to_self(
znode *expr_node, zend_ast *expr_ast, zend_ast *var_ast) {
if (zend_is_assign_to_self(var_ast, expr_ast) && !is_this_fetch(expr_ast)) {
/* $a[0] = $a should evaluate the right $a first */
znode cv_node;
if (zend_try_compile_cv(&cv_node, expr_ast) == FAILURE) {
zend_compile_simple_var_no_cv(expr_node, expr_ast, BP_VAR_R, 0);
} else {
zend_emit_op_tmp(expr_node, ZEND_QM_ASSIGN, &cv_node, NULL);
}
} else {
zend_compile_expr(expr_node, expr_ast);
}
}
static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
@ -3192,20 +3208,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_DIM:
offset = zend_delayed_compile_begin();
zend_delayed_compile_dim(result, var_ast, BP_VAR_W);
if (zend_is_assign_to_self(var_ast, expr_ast)
&& !is_this_fetch(expr_ast)) {
/* $a[0] = $a should evaluate the right $a first */
znode cv_node;
if (zend_try_compile_cv(&cv_node, expr_ast) == FAILURE) {
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
} else {
zend_emit_op_tmp(&expr_node, ZEND_QM_ASSIGN, &cv_node, NULL);
}
} else {
zend_compile_expr(&expr_node, expr_ast);
}
zend_compile_expr_with_potential_assign_to_self(&expr_node, expr_ast, var_ast);
opline = zend_delayed_compile_end(offset);
opline->opcode = ZEND_ASSIGN_DIM;
@ -3375,7 +3378,7 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_DIM:
offset = zend_delayed_compile_begin();
zend_delayed_compile_dim(result, var_ast, BP_VAR_RW);
zend_compile_expr(&expr_node, expr_ast);
zend_compile_expr_with_potential_assign_to_self(&expr_node, expr_ast, var_ast);
opline = zend_delayed_compile_end(offset);
opline->opcode = ZEND_ASSIGN_DIM_OP;
@ -8962,17 +8965,8 @@ static void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
zend_emit_op_tmp(result, ZEND_COALESCE, &var_node_is, NULL);
CG(memoize_mode) = ZEND_MEMOIZE_NONE;
if (var_ast->kind == ZEND_AST_DIM
&& zend_is_assign_to_self(var_ast, default_ast)
&& !is_this_fetch(default_ast)) {
/* $a[0] = $a should evaluate the right $a first */
znode cv_node;
if (zend_try_compile_cv(&cv_node, default_ast) == FAILURE) {
zend_compile_simple_var_no_cv(&default_node, default_ast, BP_VAR_R, 0);
} else {
zend_emit_op_tmp(&default_node, ZEND_QM_ASSIGN, &cv_node, NULL);
}
if (var_ast->kind == ZEND_AST_DIM) {
zend_compile_expr_with_potential_assign_to_self(&default_node, default_ast, var_ast);
} else {
zend_compile_expr(&default_node, default_ast);
}

View File

@ -11,10 +11,8 @@ $a = null;
$a[] .= $a;
var_dump($a);
?>
--EXPECTF--
Warning: Array to string conversion in %sassign_dim_op_004.php on line 3
--EXPECT--
array(1) {
[0]=>
string(5) "Array"
string(0) ""
}