]> granicus.if.org Git - php/commitdiff
Fixed bug #74949 (null pointer dereference in _function_string)
authorXinchen Hui <laruence@gmail.com>
Thu, 27 Jul 2017 03:23:06 +0000 (11:23 +0800)
committerXinchen Hui <laruence@gmail.com>
Thu, 27 Jul 2017 03:23:06 +0000 (11:23 +0800)
NEWS
ext/reflection/php_reflection.c
ext/reflection/tests/bug74949.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 36121bd290516024452136792de99e3bcad0e464..2f1e2bcaa88eda795734705beadd1d5044defc8b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,9 @@ PHP                                                                        NEWS
   . Fixed bug #74991 (include_path has a 4096 char limit in some cases).
     (bwbroersma)
 
+- Reflection:
+  . Fixed bug #74949 (null pointer dereference in _function_string). (Laruence)
+
 - Session:
   . Fixed bug #74833 (SID constant created with wrong module number). (Anatol)
 
index 5b162f759ed700573e0cb3095c8f89a70970748a..e5303663dcb329e17818853ff223e5ee01481f2c 100644 (file)
@@ -291,6 +291,15 @@ static zend_function *_copy_function(zend_function *fptr) /* {{{ */
 }
 /* }}} */
 
+static void _fix_closure_prototype(zend_function *fptr) /* {{{ */
+{
+       /* Actually we are setting proxy function's prototype to null
+        * as for it, the prototype is an object not a function
+        * which could cause serias problems, see #74949 */
+       fptr->common.prototype = NULL;
+}
+/* }}} */
+
 static void _free_function(zend_function *fptr) /* {{{ */
 {
        if (fptr
@@ -598,6 +607,7 @@ static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *in
                                                && memcmp(ZSTR_VAL(mptr->common.function_name), ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
                                                && (closure = zend_get_closure_invoke_method(Z_OBJ_P(obj))) != NULL)
                                        {
+                                               _fix_closure_prototype(closure);
                                                mptr = closure;
                                        } else {
                                                closure = NULL;
@@ -2322,7 +2332,6 @@ ZEND_METHOD(reflection_generator, getExecutingGenerator)
 }
 /* }}} */
 
-
 /* {{{ proto public static mixed ReflectionParameter::export(mixed function, mixed parameter [, bool return]) throws ReflectionException
    Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */
 ZEND_METHOD(reflection_parameter, export)
@@ -2407,6 +2416,7 @@ ZEND_METHOD(reflection_parameter, __construct)
                                {
                                        /* nothing to do. don't set is_closure since is the invoke handler,
                                           not the closure itself */
+                                       _fix_closure_prototype(fptr);
                                } else if ((fptr = zend_hash_str_find_ptr(&ce->function_table, lcname, lcname_len)) == NULL) {
                                        efree(lcname);
                                        zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -3092,6 +3102,7 @@ ZEND_METHOD(reflection_method, __construct)
                && (mptr = zend_get_closure_invoke_method(Z_OBJ_P(orig_obj))) != NULL)
        {
                /* do nothing, mptr already set */
+               _fix_closure_prototype(mptr);
        } else if ((mptr = zend_hash_str_find_ptr(&ce->function_table, lcname, name_len)) == NULL) {
                efree(lcname);
                zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -4096,6 +4107,7 @@ ZEND_METHOD(reflection_class, getMethod)
        {
                /* don't assign closure_object since we only reflect the invoke handler
                   method and not the closure definition itself */
+               _fix_closure_prototype(mptr);
                reflection_method_factory(ce, mptr, NULL, return_value);
                efree(lc_name);
        } else if (ce == zend_ce_closure && Z_ISUNDEF(intern->obj) && (name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1)
@@ -4103,6 +4115,7 @@ ZEND_METHOD(reflection_class, getMethod)
                && object_init_ex(&obj_tmp, ce) == SUCCESS && (mptr = zend_get_closure_invoke_method(Z_OBJ(obj_tmp))) != NULL) {
                /* don't assign closure_object since we only reflect the invoke handler
                   method and not the closure definition itself */
+               _fix_closure_prototype(mptr);
                reflection_method_factory(ce, mptr, NULL, return_value);
                zval_dtor(&obj_tmp);
                efree(lc_name);
@@ -4129,6 +4142,7 @@ static void _addmethod(zend_function *mptr, zend_class_entry *ce, zval *retval,
                        && memcmp(ZSTR_VAL(mptr->common.function_name), ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
                        && (closure = zend_get_closure_invoke_method(Z_OBJ_P(obj))) != NULL)
                {
+                       _fix_closure_prototype(closure);
                        mptr = closure;
                }
                /* don't assign closure_object since we only reflect the invoke handler
@@ -4180,6 +4194,7 @@ ZEND_METHOD(reflection_class, getMethods)
        if (Z_TYPE(intern->obj) != IS_UNDEF && instanceof_function(ce, zend_ce_closure)) {
                zend_function *closure = zend_get_closure_invoke_method(Z_OBJ(intern->obj));
                if (closure) {
+                       _fix_closure_prototype(closure);
                        _addmethod(closure, ce, return_value, filter, &intern->obj);
                        _free_function(closure);
                }
diff --git a/ext/reflection/tests/bug74949.phpt b/ext/reflection/tests/bug74949.phpt
new file mode 100644 (file)
index 0000000..d69597e
--- /dev/null
@@ -0,0 +1,24 @@
+--TEST--
+Bug #74949 (null pointer dereference in _function_string)
+--FILE--
+<?php
+
+$f = function () {};
+
+$r = new ReflectionMethod($f, "__invoke");
+
+unset($f);
+
+echo $r, "\n";
+
+try  {
+       echo $r->getPrototype();
+} catch (Exception $e) {
+       echo $e->getMessage(), "\n";    
+}
+?>
+--EXPECT--
+Method [ <internal> public method __invoke ] {
+}
+
+Method Closure::__invoke does not have a prototype