summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-10-09 00:45:02 +0300
committerDmitry Stogov <dmitry@zend.com>2015-10-09 00:45:02 +0300
commit91fb3a7b2791f97363b085747e98d14efb23a485 (patch)
tree562d73efe1eb867954dcd373f53a92ac13519f57
parentfa23bebe94d7149d7bb55d3482f4802546198c24 (diff)
downloadphp-git-91fb3a7b2791f97363b085747e98d14efb23a485.tar.gz
Fixed bug #70674 (ReflectionFunction::getClosure() leaks memory when used for internal functions)
-rw-r--r--NEWS2
-rw-r--r--Zend/zend_closures.c25
-rw-r--r--ext/reflection/bug70674.phpt8
3 files changed, 30 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 44adba2511..3410a6d5f1 100644
--- a/NEWS
+++ b/NEWS
@@ -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)