deserialization). (Laruence)
. Fixed bug #72543 (Different references behavior comparing to PHP 5)
(Laruence, Dmitry, Nikita)
+ . Fixed bug #72347 (VERIFY_RETURN type casts visible in finally). (Dmitry)
+ . Fixed bug #72216 (Return by reference with finally is not memory safe).
+ (Dmitry)
+ . Fixed bug #72215 (Wrong return value if var modified in finally). (Dmitry)
. Fixed bug #71539 (Memory error on $arr[$a] =& $arr[$b] if RHS rehashes)
(Dmitry, Nikita)
. Added new constant PHP_FD_SETSIZE. (cmb)
--- /dev/null
+--TEST--
+Bug #72215 (Wrong return value if var modified in finally)
+--FILE--
+<?php
+function test() {
+ $a = 1;
+ try {
+ return $a;
+ } finally {
+ $a = 2;
+ }
+}
+var_dump(test());
+?>
+--EXPECT--
+int(1)
--- /dev/null
+--TEST--
+Bug #72215.1 (Wrong return value if var modified in finally)
+--FILE--
+<?php
+function &test(&$b) {
+ $a =& $b;
+ try {
+ return $a;
+ } finally {
+ $a = 2;
+ }
+}
+$x = 1;
+$y =& test($x);
+var_dump($y);
+$x = 3;
+var_dump($y);
+?>
+--EXPECT--
+int(2)
+int(3)
--- /dev/null
+--TEST--
+Bug #72215.1 (Wrong return value if var modified in finally)
+--FILE--
+<?php
+function &test(&$b) {
+ $a =& $b;
+ try {
+ return $a;
+ } finally {
+ $a =& $c;
+ $a = 2;
+ }
+}
+$x = 1;
+$y =& test($x);
+var_dump($y);
+$x = 3;
+var_dump($y);
+?>
+--EXPECT--
+int(1)
+int(3)
--- /dev/null
+--TEST--
+Bug #72216 (Return by reference with finally is not memory safe)
+--FILE--
+<?php
+function &test() {
+ $a = ["ok"];
+ try {
+ return $a[0];
+ } finally {
+ $a[""] = 42;
+ }
+}
+var_dump(test());
+?>
+--EXPECT--
+string(2) "ok"
--- /dev/null
+--TEST--
+Bug #72347 (VERIFY_RETURN type casts visible in finally)
+--FILE--
+<?php
+function test() : int {
+ $d = 1.5;
+ try {
+ return $d;
+ } finally {
+ var_dump($d);
+ }
+}
+var_dump(test());
+?>
+--EXPECT--
+float(1.5)
+int(1)
}
/* }}} */
+static int zend_has_finally_ex(zend_long depth) /* {{{ */
+{
+ zend_loop_var *base;
+ zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
+
+ if (!loop_var) {
+ return 0;
+ }
+ base = zend_stack_base(&CG(loop_var_stack));
+ for (; loop_var >= base; loop_var--) {
+ if (loop_var->opcode == ZEND_FAST_CALL) {
+ return 1;
+ } else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
+ } else if (loop_var->opcode == ZEND_RETURN) {
+ /* Stack separator */
+ return 0;
+ } else if (depth <= 1) {
+ return 0;
+ } else {
+ depth--;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+static int zend_has_finally(void) /* {{{ */
+{
+ return zend_has_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1);
+}
+/* }}} */
+
void zend_compile_return(zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
zend_compile_expr(&expr_node, expr_ast);
}
+ if ((CG(active_op_array)->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)
+ && (expr_node.op_type == IS_CV || (by_ref && expr_node.op_type == IS_VAR))
+ && zend_has_finally()) {
+ /* Copy return value into temporary VAR to avoid modification in finally code */
+ if (by_ref) {
+ zend_emit_op(&expr_node, ZEND_MAKE_REF, &expr_node, NULL);
+ } else {
+ zend_emit_op_tmp(&expr_node, ZEND_QM_ASSIGN, &expr_node, NULL);
+ }
+ }
+
/* Generator return types are handled separately */
if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_emit_return_type_check(
ZEND_VM_NEXT_OPCODE();
}
-ZEND_VM_HANDLER(51, ZEND_MAKE_REF, VAR, UNUSED)
+ZEND_VM_HANDLER(51, ZEND_MAKE_REF, VAR|CV, UNUSED)
{
USE_OPLINE
zval *op1 = EX_VAR(opline->op1.var);
- if (EXPECTED(Z_TYPE_P(op1) == IS_INDIRECT)) {
+ if (OP1_TYPE == IS_CV) {
+ if (UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ GET_OP1_UNDEF_CV(op1, BP_VAR_R);
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ ZVAL_MAKE_REF(op1);
+ ZVAL_COPY(EX_VAR(opline->result.var), op1);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_INDIRECT)) {
op1 = Z_INDIRECT_P(op1);
if (EXPECTED(!Z_ISREF_P(op1))) {
ZVAL_MAKE_REF(op1);
USE_OPLINE
zval *op1 = EX_VAR(opline->op1.var);
- if (EXPECTED(Z_TYPE_P(op1) == IS_INDIRECT)) {
+ if (IS_VAR == IS_CV) {
+ if (UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ GET_OP1_UNDEF_CV(op1, BP_VAR_R);
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ ZVAL_MAKE_REF(op1);
+ ZVAL_COPY(EX_VAR(opline->result.var), op1);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_INDIRECT)) {
op1 = Z_INDIRECT_P(op1);
if (EXPECTED(!Z_ISREF_P(op1))) {
ZVAL_MAKE_REF(op1);
ZEND_VM_NEXT_OPCODE();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zval *op1 = EX_VAR(opline->op1.var);
+
+ if (IS_CV == IS_CV) {
+ if (UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
+ SAVE_OPLINE();
+ GET_OP1_UNDEF_CV(op1, BP_VAR_R);
+ ZVAL_NULL(EX_VAR(opline->result.var));
+ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
+ }
+ ZVAL_MAKE_REF(op1);
+ ZVAL_COPY(EX_VAR(opline->result.var), op1);
+ } else if (EXPECTED(Z_TYPE_P(op1) == IS_INDIRECT)) {
+ op1 = Z_INDIRECT_P(op1);
+ if (EXPECTED(!Z_ISREF_P(op1))) {
+ ZVAL_MAKE_REF(op1);
+ }
+ GC_REFCOUNT(Z_REF_P(op1))++;
+ ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(op1));
+ } else {
+ ZVAL_COPY_VALUE(EX_VAR(opline->result.var), op1);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
- ZEND_NULL_HANDLER,
+ ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER,
ZEND_NULL_HANDLER,
ZEND_BOOL_SPEC_CONST_HANDLER,
ZEND_BOOL_SPEC_TMPVAR_HANDLER,