From 6b0f14fe3b08be05340c6a4ae03f4131f04d882a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 17 Feb 2021 14:45:35 +0100 Subject: Fixed bug #75474 For fake closures, we need to share static variables with the original function, not work on a separate copy. Calling a function through Closure::fromCallable() should have the same behavior as calling it directly. --- Zend/zend_closures.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'Zend/zend_closures.c') diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index c06f90fabc..38105a67f6 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -486,6 +486,11 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */ zend_object_std_dtor(&closure->std); if (closure->func.type == ZEND_USER_FUNCTION) { + /* We shared static_variables with the original function. + * Unshare now so we don't try to destroy them. */ + if (closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE) { + ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, NULL); + } destroy_op_array(&closure->func.op_array); } @@ -660,7 +665,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ } /* }}} */ -ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */ { zend_closure *closure; @@ -679,12 +684,15 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE; - if (closure->func.op_array.static_variables) { - closure->func.op_array.static_variables = - zend_array_dup(closure->func.op_array.static_variables); + /* For fake closures, we want to reuse the static variables of the original function. */ + if (!is_fake) { + if (closure->func.op_array.static_variables) { + closure->func.op_array.static_variables = + zend_array_dup(closure->func.op_array.static_variables); + } + ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, + &closure->func.op_array.static_variables); } - ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, - &closure->func.op_array.static_variables); /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */ if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache) @@ -754,11 +762,16 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent } /* }}} */ +ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) +{ + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ false); +} + ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ { zend_closure *closure; - zend_create_closure(res, func, scope, called_scope, this_ptr); + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true); closure = (zend_closure *)Z_OBJ_P(res); closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; -- cgit v1.2.1