]> granicus.if.org Git - php/commitdiff
Allow internal functions to return values by reference (this was disabled in implemen...
authorDmitry Stogov <dmitry@zend.com>
Tue, 17 Oct 2017 08:50:34 +0000 (11:50 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 17 Oct 2017 08:50:34 +0000 (11:50 +0300)
However, don't require internal functions returning by reference to return a reference.
Mark unserialize() as returning by reference and remove unwrap_reference hack, to allow proper returning of self referenced arrays using a reference.
Currently unserialize() is the only internal function that may return a reference.

Zend/zend_vm_def.h
Zend/zend_vm_execute.h
ext/gmp/tests/unserialize_with_reference.phpt
ext/opcache/Optimizer/zend_func_info.c
ext/standard/basic_functions.c
ext/standard/var.c

index b8f0aa1f343d25782d580aa29496aff758ec1659..c8194e8f5ba746c9837130cfe3a8245defa05904 100644 (file)
@@ -3480,11 +3480,12 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
        fbc->internal_function.handler(call, ret);
 
 #if ZEND_DEBUG
-       ZEND_ASSERT(
-               EG(exception) || !call->func ||
-               !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
-               zend_verify_internal_return_type(call->func, ret));
-       ZEND_ASSERT(!Z_ISREF_P(ret));
+       if (!EG(exception) && call->func) {
+               ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+                       zend_verify_internal_return_type(call->func, ret));
+               ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                       !Z_ISREF_P(ret));
+       }
 #endif
 
        EG(current_execute_data) = execute_data;
@@ -3582,8 +3583,8 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
                if (!EG(exception) && call->func) {
                        ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
                                zend_verify_internal_return_type(call->func, ret));
-                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                               ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
                }
 #endif
 
@@ -3676,8 +3677,8 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
                if (!EG(exception) && call->func) {
                        ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
                                zend_verify_internal_return_type(call->func, ret));
-                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                               ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
                }
 #endif
 
@@ -7837,10 +7838,12 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
                }
 
 #if ZEND_DEBUG
-               ZEND_ASSERT(
-                       EG(exception) || !call->func ||
-                       !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
-                       zend_verify_internal_return_type(call->func, ret));
+               if (!EG(exception) && call->func) {
+                       ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+                               zend_verify_internal_return_type(call->func, ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
+               }
 #endif
 
                EG(current_execute_data) = call->prev_execute_data;
index 9d99daf98684a731e82eb4df39d8b9b5bc33120a..b290c5036e84af65fa1261f33e27791de27cf656 100644 (file)
@@ -573,11 +573,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
        fbc->internal_function.handler(call, ret);
 
 #if ZEND_DEBUG
-       ZEND_ASSERT(
-               EG(exception) || !call->func ||
-               !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
-               zend_verify_internal_return_type(call->func, ret));
-       ZEND_ASSERT(!Z_ISREF_P(ret));
+       if (!EG(exception) && call->func) {
+               ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+                       zend_verify_internal_return_type(call->func, ret));
+               ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                       !Z_ISREF_P(ret));
+       }
 #endif
 
        EG(current_execute_data) = execute_data;
@@ -617,11 +618,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
        fbc->internal_function.handler(call, ret);
 
 #if ZEND_DEBUG
-       ZEND_ASSERT(
-               EG(exception) || !call->func ||
-               !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
-               zend_verify_internal_return_type(call->func, ret));
-       ZEND_ASSERT(!Z_ISREF_P(ret));
+       if (!EG(exception) && call->func) {
+               ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+                       zend_verify_internal_return_type(call->func, ret));
+               ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                       !Z_ISREF_P(ret));
+       }
 #endif
 
        EG(current_execute_data) = execute_data;
@@ -741,8 +743,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
                if (!EG(exception) && call->func) {
                        ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
                                zend_verify_internal_return_type(call->func, ret));
-                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                               ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
                }
 #endif
 
@@ -819,8 +821,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
                if (!EG(exception) && call->func) {
                        ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
                                zend_verify_internal_return_type(call->func, ret));
-                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                               ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
                }
 #endif
 
@@ -913,8 +915,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
                if (!EG(exception) && call->func) {
                        ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
                                zend_verify_internal_return_type(call->func, ret));
-                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                               ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
                }
 #endif
 
@@ -1038,8 +1040,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
                if (!EG(exception) && call->func) {
                        ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
                                zend_verify_internal_return_type(call->func, ret));
-                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                               ? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
                }
 #endif
 
@@ -2032,10 +2034,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z
                }
 
 #if ZEND_DEBUG
-               ZEND_ASSERT(
-                       EG(exception) || !call->func ||
-                       !(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
-                       zend_verify_internal_return_type(call->func, ret));
+               if (!EG(exception) && call->func) {
+                       ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
+                               zend_verify_internal_return_type(call->func, ret));
+                       ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ||
+                               !Z_ISREF_P(ret));
+               }
 #endif
 
                EG(current_execute_data) = call->prev_execute_data;
index b733430a3870b07a59be2cbaf13064cea7965de1..e1f158070375d7a907bcc207503ec00524835bf4 100644 (file)
@@ -2,7 +2,7 @@
 Unserialize GMP instance with internal reference to itself
 --FILE--
 <?php
-$s = 'C:3:"GMP":23:{s:1:"2";a:1:{i:46;R:1;}}';
+$s = 'C:3:"GMP":23:{s:1:"2";a:1:{i:46;r:1;}}';
 var_dump(unserialize($s));
 ?>
 --EXPECT--
index 957d53864ec2543ac3b57d0e020ed66410221b7a..5090363f829083ae44d3536f02440a2b87e4b049 100644 (file)
@@ -531,7 +531,7 @@ static const func_info_t func_infos[] = {
        FN("forward_static_call",          UNKNOWN_INFO),
        FN("forward_static_call_array",    UNKNOWN_INFO),
        F1("serialize",                    MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
-       FN("unserialize",                  UNKNOWN_INFO),
+       FN("unserialize",                  UNKNOWN_INFO | MAY_BE_REF),
        F0("var_dump",                     MAY_BE_NULL),
        F1("var_export",                   MAY_BE_NULL | MAY_BE_STRING),
        F0("debug_zval_dump",              MAY_BE_NULL),
index 93d469168b70596e9a839f3298fc9c3794ea0938..7bb579c692e28d9588e430876710fef5cf73d10b 100644 (file)
@@ -2669,7 +2669,7 @@ ZEND_BEGIN_ARG_INFO(arginfo_serialize, 0)
        ZEND_ARG_INFO(0, var)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_unserialize, 0, 0, 1)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_unserialize, 0, 1, 1)
        ZEND_ARG_INFO(0, variable_representation)
        ZEND_ARG_INFO(0, allowed_classes)
 ZEND_END_ARG_INFO()
index e28afe91776fab86ce1c4b65d5329b9169623a79..f8406d8988b8b7181fca85b4002c3db6a0fe7cfc 100644 (file)
@@ -1124,13 +1124,6 @@ PHP_FUNCTION(unserialize)
        /* Reset to previous allowed_classes in case this is a nested call */
        php_var_unserialize_set_allowed_classes(var_hash, prev_class_hash);
        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
-
-       /* Per calling convention we must not return a reference here, so unwrap. We're doing this at
-        * the very end, because __wakeup() calls performed during UNSERIALIZE_DESTROY might affect
-        * the value we unwrap here. This is compatible with behavior in PHP <=7.0. */
-       if (Z_ISREF_P(return_value)) {
-               zend_unwrap_reference(return_value);
-       }
 }
 /* }}} */