]> granicus.if.org Git - php/commitdiff
Don't inline if function has ref arguments
authorNikita Popov <nikic@php.net>
Sat, 12 Nov 2016 17:48:07 +0000 (18:48 +0100)
committerNikita Popov <nikic@php.net>
Sat, 12 Nov 2016 17:49:41 +0000 (18:49 +0100)
Otherwise we end up leaving opcodes like FETCH_DIM_W behind. The
test case demonstrates a leak in particular.

ext/opcache/Optimizer/optimize_func_calls.c
ext/opcache/tests/wrong_inlining_005.phpt [new file with mode: 0644]

index f9a42db79e845d82223aec60e3a6470efa0f8464..3480a9a6ee33faeb25757292f8f75773ee49b6d5 100644 (file)
@@ -71,8 +71,6 @@ static void zend_delete_call_instructions(zend_op *opline)
                                break;
                        case ZEND_SEND_VAL:
                        case ZEND_SEND_VAR:
-                       case ZEND_SEND_VAR_NO_REF:
-                       case ZEND_SEND_REF:
                                if (call == 0) {
                                        if (opline->op1_type == IS_CONST) {
                                                MAKE_NOP(opline);
@@ -102,6 +100,8 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o
                zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
 
                if (ret_opline->op1_type == IS_CONST) {
+                       uint32_t i, num_args = func->op_array.num_args;
+                       num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
 
                        if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
                                /* TODO: we can't inlne methods, because $this may be used
@@ -109,17 +109,27 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o
                                 */
                                return;
                        }
+
+                       for (i = 0; i < num_args; i++) {
+                               /* Don't inline functions with by-reference arguments. This would require
+                                * correct handling of INDIRECT arguments. */
+                               if (func->op_array.arg_info[i].pass_by_reference) {
+                                       return;
+                               }
+                       }
+
                        if (fcall->extended_value < func->op_array.num_args) {
                                /* don't inline funcions with named constants in default arguments */
-                               uint32_t n = fcall->extended_value;
+                               i = fcall->extended_value;
 
                                do {
-                                       if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[n].op2))) {
+                                       if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) {
                                                return;
                                        }
-                                       n++;
-                               } while (n < func->op_array.num_args);
+                                       i++;
+                               } while (i < func->op_array.num_args);
                        }
+
                        if (RETURN_VALUE_USED(opline)) {
                                zval zv;
 
diff --git a/ext/opcache/tests/wrong_inlining_005.phpt b/ext/opcache/tests/wrong_inlining_005.phpt
new file mode 100644 (file)
index 0000000..b34cd1b
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Inlining of functions with ref arguments
+--FILE--
+<?php
+
+function by_ref(&$var)
+{
+}
+function &get_array() {
+    $array = [new stdClass];
+    return $array;
+}
+function test()
+{
+    by_ref(get_array()[0]);
+    print "ok!\n";
+}
+test();
+
+?>
+--EXPECT--
+ok!