diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-10-09 00:45:02 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-10-09 00:45:02 +0300 |
commit | 91fb3a7b2791f97363b085747e98d14efb23a485 (patch) | |
tree | 562d73efe1eb867954dcd373f53a92ac13519f57 | |
parent | fa23bebe94d7149d7bb55d3482f4802546198c24 (diff) | |
download | php-git-91fb3a7b2791f97363b085747e98d14efb23a485.tar.gz |
Fixed bug #70674 (ReflectionFunction::getClosure() leaks memory when used for internal functions)
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | Zend/zend_closures.c | 25 | ||||
-rw-r--r-- | ext/reflection/bug70674.phpt | 8 |
3 files changed, 30 insertions, 5 deletions
@@ -15,6 +15,8 @@ PHP NEWS - Reflection: . Fixed bug #70650 (Wrong docblock assignment). (Marcio) + . Fixed bug #70674 (ReflectionFunction::getClosure() leaks memory when used + for internal functions). (Dmitry, Bob) - Standard: . Fixed bug #70667 (strtr() causes invalid writes and a crashes). (Dmitry) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 50aa7d4188..9f2292b878 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -39,6 +39,7 @@ typedef struct _zend_closure { zend_function func; zval this_ptr; zend_class_entry *called_scope; + void (*orig_internal_handler)(INTERNAL_FUNCTION_PARAMETERS); } zend_closure; /* non-static since it needs to be referenced */ @@ -517,6 +518,15 @@ void zend_register_closure_ce(void) /* {{{ */ } /* }}} */ +static void zend_closure_internal_handler(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ +{ + zend_closure *closure = (zend_closure*)EX(func)->common.prototype; + closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + OBJ_RELEASE((zend_object*)closure); + EX(func)->common.prototype = NULL; +} +/* }}} */ + ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ { zend_closure *closure; @@ -525,17 +535,16 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent closure = (zend_closure *)Z_OBJ_P(res); - memcpy(&closure->func, func, func->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function)); - closure->func.common.prototype = (zend_function*)closure; - closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; - if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) { /* use dummy scope if we're binding an object without specifying a scope */ /* maybe it would be better to create one for this purpose */ scope = zend_ce_closure; } - if (closure->func.type == ZEND_USER_FUNCTION) { + if (func->type == ZEND_USER_FUNCTION) { + memcpy(&closure->func, func, sizeof(zend_op_array)); + closure->func.common.prototype = (zend_function*)closure; + closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; if (closure->func.op_array.static_variables) { HashTable *static_variables = closure->func.op_array.static_variables; @@ -551,6 +560,12 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent (*closure->func.op_array.refcount)++; } } else { + memcpy(&closure->func, func, sizeof(zend_internal_function)); + closure->func.common.prototype = (zend_function*)closure; + closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; + /* wrap internal function handler to avoid memory leak */ + closure->orig_internal_handler = closure->func.internal_function.handler; + closure->func.internal_function.handler = zend_closure_internal_handler; /* verify that we aren't binding internal function to a wrong scope */ if(func->common.scope != NULL) { if(scope && !instanceof_function(scope, func->common.scope)) { diff --git a/ext/reflection/bug70674.phpt b/ext/reflection/bug70674.phpt new file mode 100644 index 0000000000..82cfc53f1e --- /dev/null +++ b/ext/reflection/bug70674.phpt @@ -0,0 +1,8 @@ +--TEST-- +Bug #70674 (ReflectionFunction::getClosure() leaks memory when used for internal functions) +--FILE-- +<?php +var_dump(((new ReflectionFunction("strlen"))->getClosure())("hello")); +?> +--EXPECT-- +int(5) |