From: Nikita Popov Date: Fri, 6 Sep 2019 09:30:01 +0000 (+0200) Subject: Fixed bug #78502 X-Git-Tag: php-7.4.0RC2~82 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e81751ceacf79e2e21b48a12dcbe38c16f98b7da;p=php Fixed bug #78502 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. --- diff --git a/Zend/tests/bug78502.phpt b/Zend/tests/bug78502.phpt new file mode 100644 index 0000000000..1f83e27644 --- /dev/null +++ b/Zend/tests/bug78502.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug #78502: Incorrect stack size calculation for indirectly recursive function call +--FILE-- + '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=== diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 33c40296e0..9099c48193 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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)