]> granicus.if.org Git - php/commitdiff
Fixed bug #78502
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 6 Sep 2019 09:30:01 +0000 (11:30 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 6 Sep 2019 09:33:28 +0000 (11:33 +0200)
We need to make sure that the function is fully compiled before we
calculate the stack size. There already was a check for directly
recursive calls, but the same issue exists with indirectly recursive
calls.

I'm using DONE_PASS_TWO as the indication that the function is
fully compiled.

Zend/tests/bug78502.phpt [new file with mode: 0644]
Zend/zend_compile.c

diff --git a/Zend/tests/bug78502.phpt b/Zend/tests/bug78502.phpt
new file mode 100644 (file)
index 0000000..1f83e27
--- /dev/null
@@ -0,0 +1,31 @@
+--TEST--
+Bug #78502: Incorrect stack size calculation for indirectly recursive function call
+--FILE--
+<?php
+
+$tree = [
+    'name' => 'a',
+    'quant' => 1,
+    'children' => [
+        ['name' => 'b', 'quant' => 1],
+        ['name' => 'c', 'quant' => 1, 'children' => [
+            ['name' => 'd', 'quant' => 1],
+        ]],
+    ],
+];
+
+function tree_map($tree, $recursive_attr, closure $callback){
+    if(isset($tree[$recursive_attr])){
+        $tree[$recursive_attr] = array_map(function($c) use($recursive_attr, $callback){
+            return tree_map($c, $recursive_attr, $callback);
+        }, $tree[$recursive_attr]);
+    }
+    return $callback($tree);
+}
+
+tree_map($tree, 'children', function ($node) {});
+
+?>
+===DONE===
+--EXPECT--
+===DONE===
index 33c40296e06dcfe744b45819b6c1f4069c7c970c..9099c48193e6b29795a846fb2a43d84be8ce058e 100644 (file)
@@ -3321,6 +3321,12 @@ int zend_compile_func_ord(znode *result, zend_ast_list *args) /* {{{ */
 }
 /* }}} */
 
+/* We can only calculate the stack size for functions that have been fully compiled, otherwise
+ * additional CV or TMP slots may still be added. This prevents the use of INIT_FCALL for
+ * directly or indirectly recursive function calls. */
+static zend_bool fbc_is_finalized(zend_function *fbc) {
+       return !ZEND_USER_CODE(fbc->type) || (fbc->common.fn_flags & ZEND_ACC_DONE_PASS_TWO);
+}
 
 static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t num_args) /* {{{ */
 {
@@ -3336,9 +3342,7 @@ static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t
        lcname = zend_string_tolower(name);
 
        fbc = zend_hash_find_ptr(CG(function_table), lcname);
-       if (!fbc
-               /* Don't use INIT_FCALL for recursive calls */
-        || (fbc == (zend_function*)CG(active_op_array))
+       if (!fbc || !fbc_is_finalized(fbc)
         || (fbc->type == ZEND_INTERNAL_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS))
         || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS))
         || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) && fbc->op_array.filename != CG(active_op_array)->filename)
@@ -3461,8 +3465,7 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string
 
                zend_emit_op(NULL, ZEND_ASSERT_CHECK, NULL, NULL);
 
-               /* Don't use INIT_FCALL for recursive calls */
-               if (fbc && fbc != (zend_function*)CG(active_op_array)) {
+               if (fbc && fbc_is_finalized(fbc)) {
                        name_node.op_type = IS_CONST;
                        ZVAL_STR_COPY(&name_node.u.constant, name);
 
@@ -3838,9 +3841,7 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
                        return;
                }
 
-               if (!fbc
-                       /* Don't use INIT_FCALL for recursive calls */
-                || (fbc == (zend_function*)CG(active_op_array))
+               if (!fbc || !fbc_is_finalized(fbc)
                 || (fbc->type == ZEND_INTERNAL_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS))
                 || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS))
                 || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) && fbc->op_array.filename != CG(active_op_array)->filename)