]> granicus.if.org Git - php/commitdiff
Fix VERIFY_RETURN separation
authorNikita Popov <nikic@php.net>
Tue, 24 Mar 2015 09:48:16 +0000 (10:48 +0100)
committerNikita Popov <nikic@php.net>
Tue, 24 Mar 2015 09:51:28 +0000 (10:51 +0100)
a) The SAME_FAKE_TYPE check should always occur on the dereferenced
value, otherwise we'll end up separating large referenced arrays.
b) For by-val return with an rc=1 reference, the SEPARATE_ZVAL
wouldn't do anything. Add a ZVAL_UNREF for this case.

I don't like this fix - there must be some better way to express
this.

Zend/tests/typehints/return_separation.phpt [new file with mode: 0644]
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/typehints/return_separation.phpt b/Zend/tests/typehints/return_separation.phpt
new file mode 100644 (file)
index 0000000..7e9d095
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+Return values are separated for references with rc=1
+--FILE--
+<?php
+
+function test1() : array {
+    $array = [];
+    $ref =& $array;
+    unset($ref);
+    return $array;
+}
+
+function test2() : string {
+    $int = 42;
+    $ref =& $int;
+    unset($ref);
+    return $int;
+}
+
+var_dump(test1());
+var_dump(test2());
+
+?>
+--EXPECT--
+array(0) {
+}
+string(2) "42"
index ed4c7191a7ae1f67d8209f6c08132d9d2f89a0d4..e3eb94e5912618514f4e1b2862b8732703d4e6ae 100644 (file)
@@ -3602,15 +3602,17 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
        } else {
 /* prevents "undefined variable opline" errors */
 #if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED)
-               zval *retval_ptr;
+               zval *retval_ref, *retval_ptr;
                zend_free_op free_op1;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
 
-               retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
+               retval_ref = retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
 
                if (OP1_TYPE == IS_CONST) {
                        ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
-                       retval_ptr = EX_VAR(opline->result.var);
+                       retval_ref = retval_ptr = EX_VAR(opline->result.var);
+               } else if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
+                       ZVAL_DEREF(retval_ptr);
                }
 
                if (UNEXPECTED(!ret_info->class_name
@@ -3620,9 +3622,13 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
 
                        if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) {
                                /* Does not return by reference */
-                               SEPARATE_ZVAL(retval_ptr);
+                               if (retval_ref != retval_ptr && Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       SEPARATE_ZVAL(retval_ref);
+                               }
+                               retval_ptr = retval_ref;
                        } else {
-                               ZVAL_DEREF(retval_ptr);
                                SEPARATE_ZVAL_NOREF(retval_ptr);
                        }
                }
index ebec22ed84a8126e8770ee33d1eda773d4b65bd9..3d09a2f52fd383e02e592aaa611556fa12d619cb 100644 (file)
@@ -6970,15 +6970,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_CONST != IS_UNUSED)
-               zval *retval_ptr;
+               zval *retval_ref, *retval_ptr;
 
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
 
-               retval_ptr = EX_CONSTANT(opline->op1);
+               retval_ref = retval_ptr = EX_CONSTANT(opline->op1);
 
                if (IS_CONST == IS_CONST) {
                        ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
-                       retval_ptr = EX_VAR(opline->result.var);
+                       retval_ref = retval_ptr = EX_VAR(opline->result.var);
+               } else if (IS_CONST == IS_VAR || IS_CONST == IS_CV) {
+                       ZVAL_DEREF(retval_ptr);
                }
 
                if (UNEXPECTED(!ret_info->class_name
@@ -6988,9 +6990,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
 
                        if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) {
                                /* Does not return by reference */
-                               SEPARATE_ZVAL(retval_ptr);
+                               if (retval_ref != retval_ptr && Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       SEPARATE_ZVAL(retval_ref);
+                               }
+                               retval_ptr = retval_ref;
                        } else {
-                               ZVAL_DEREF(retval_ptr);
                                SEPARATE_ZVAL_NOREF(retval_ptr);
                        }
                }
@@ -11936,15 +11942,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_TMP_VAR != IS_UNUSED)
-               zval *retval_ptr;
+               zval *retval_ref, *retval_ptr;
                zend_free_op free_op1;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
 
-               retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
+               retval_ref = retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
 
                if (IS_TMP_VAR == IS_CONST) {
                        ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
-                       retval_ptr = EX_VAR(opline->result.var);
+                       retval_ref = retval_ptr = EX_VAR(opline->result.var);
+               } else if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) {
+                       ZVAL_DEREF(retval_ptr);
                }
 
                if (UNEXPECTED(!ret_info->class_name
@@ -11954,9 +11962,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
 
                        if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) {
                                /* Does not return by reference */
-                               SEPARATE_ZVAL(retval_ptr);
+                               if (retval_ref != retval_ptr && Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       SEPARATE_ZVAL(retval_ref);
+                               }
+                               retval_ptr = retval_ref;
                        } else {
-                               ZVAL_DEREF(retval_ptr);
                                SEPARATE_ZVAL_NOREF(retval_ptr);
                        }
                }
@@ -17302,15 +17314,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_VAR != IS_UNUSED)
-               zval *retval_ptr;
+               zval *retval_ref, *retval_ptr;
                zend_free_op free_op1;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
 
-               retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
+               retval_ref = retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
 
                if (IS_VAR == IS_CONST) {
                        ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
-                       retval_ptr = EX_VAR(opline->result.var);
+                       retval_ref = retval_ptr = EX_VAR(opline->result.var);
+               } else if (IS_VAR == IS_VAR || IS_VAR == IS_CV) {
+                       ZVAL_DEREF(retval_ptr);
                }
 
                if (UNEXPECTED(!ret_info->class_name
@@ -17320,9 +17334,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
 
                        if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) {
                                /* Does not return by reference */
-                               SEPARATE_ZVAL(retval_ptr);
+                               if (retval_ref != retval_ptr && Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       SEPARATE_ZVAL(retval_ref);
+                               }
+                               retval_ptr = retval_ref;
                        } else {
-                               ZVAL_DEREF(retval_ptr);
                                SEPARATE_ZVAL_NOREF(retval_ptr);
                        }
                }
@@ -22925,15 +22943,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_UNUSED != IS_UNUSED)
-               zval *retval_ptr;
+               zval *retval_ref, *retval_ptr;
 
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
 
-               retval_ptr = NULL;
+               retval_ref = retval_ptr = NULL;
 
                if (IS_UNUSED == IS_CONST) {
                        ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
-                       retval_ptr = EX_VAR(opline->result.var);
+                       retval_ref = retval_ptr = EX_VAR(opline->result.var);
+               } else if (IS_UNUSED == IS_VAR || IS_UNUSED == IS_CV) {
+                       ZVAL_DEREF(retval_ptr);
                }
 
                if (UNEXPECTED(!ret_info->class_name
@@ -22943,9 +22963,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
 
                        if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) {
                                /* Does not return by reference */
-                               SEPARATE_ZVAL(retval_ptr);
+                               if (retval_ref != retval_ptr && Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       SEPARATE_ZVAL(retval_ref);
+                               }
+                               retval_ptr = retval_ref;
                        } else {
-                               ZVAL_DEREF(retval_ptr);
                                SEPARATE_ZVAL_NOREF(retval_ptr);
                        }
                }
@@ -31872,15 +31896,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_CV != IS_UNUSED)
-               zval *retval_ptr;
+               zval *retval_ref, *retval_ptr;
 
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
 
-               retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
+               retval_ref = retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
 
                if (IS_CV == IS_CONST) {
                        ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr);
-                       retval_ptr = EX_VAR(opline->result.var);
+                       retval_ref = retval_ptr = EX_VAR(opline->result.var);
+               } else if (IS_CV == IS_VAR || IS_CV == IS_CV) {
+                       ZVAL_DEREF(retval_ptr);
                }
 
                if (UNEXPECTED(!ret_info->class_name
@@ -31890,9 +31916,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
 
                        if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) {
                                /* Does not return by reference */
-                               SEPARATE_ZVAL(retval_ptr);
+                               if (retval_ref != retval_ptr && Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       SEPARATE_ZVAL(retval_ref);
+                               }
+                               retval_ptr = retval_ref;
                        } else {
-                               ZVAL_DEREF(retval_ptr);
                                SEPARATE_ZVAL_NOREF(retval_ptr);
                        }
                }