diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c index 02c28ead33e1..3775f8165c3e 100644 --- a/Zend/Optimizer/block_pass.c +++ b/Zend/Optimizer/block_pass.c @@ -648,7 +648,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } else if (opline->op1_type == IS_TMP_VAR && !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { src = VAR_SOURCE(opline->op1); - if (src) { + if (src && (src->op1_type != IS_VAR)) { if (src->opcode == ZEND_BOOL_NOT) { VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); @@ -692,7 +692,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) || opline->result.var == opline->op1.var)) { src = VAR_SOURCE(opline->op1); - if (src) { + if (src && (src->op1_type != IS_VAR)) { if (src->opcode == ZEND_BOOL || src->opcode == ZEND_QM_ASSIGN) { VAR_SOURCE(opline->op1) = NULL; diff --git a/ext/opcache/tests/gh21691.phpt b/ext/opcache/tests/gh21691.phpt new file mode 100644 index 000000000000..3926b76c7a12 --- /dev/null +++ b/ext/opcache/tests/gh21691.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-21691 (OPcache CFG optimizer breaks reference returns with JMPZ/JMPZ_EX) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +NULL diff --git a/ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt b/ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt new file mode 100644 index 000000000000..490fa4f6c3b0 --- /dev/null +++ b/ext/standard/tests/serialize/serialize_array_ref_reentrancy.phpt @@ -0,0 +1,21 @@ +--TEST-- +serialize(): a by-reference __serialize() that grows the array being walked must not free it +--FILE-- +ref[] = 'x' . $i; + } + return ['d' => 1]; + } +} +$g = new G(); +$inner = [$g, 'tail']; +$g->ref = &$inner; +$top = [&$inner]; +var_dump(serialize($top)); +?> +--EXPECT-- +string(59) "a:1:{i:0;a:2:{i:0;O:1:"G":1:{s:1:"d";i:1;}i:1;s:4:"tail";}}" diff --git a/ext/standard/var.c b/ext/standard/var.c index 3137a270f661..d79b8941634d 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -1303,13 +1303,17 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ zend_release_properties(myht); return; } - case IS_ARRAY: + case IS_ARRAY: { smart_str_appendl(buf, "a:", 2); myht = Z_ARRVAL_P(struc); + bool rcn = !is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1); + GC_TRY_ADDREF(myht); php_var_serialize_nested_data( buf, struc, myht, zend_array_count(myht), /* incomplete_class */ false, var_hash, - !is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1)); + rcn); + GC_TRY_DTOR_NO_REF(myht); return; + } case IS_REFERENCE: struc = Z_REFVAL_P(struc); goto again;