From e36c04ef483d85fe145ee07280d8c31e7e295644 Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 27 Jul 2017 11:23:06 +0800 Subject: [PATCH 1/2] Fixed bug #74949 (null pointer dereference in _function_string) --- NEWS | 3 +++ ext/reflection/php_reflection.c | 17 ++++++++++++++++- ext/reflection/tests/bug74949.phpt | 24 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/bug74949.phpt diff --git a/NEWS b/NEWS index 36121bd2905..2f1e2bcaa88 100644 --- 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) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 5b162f759ed..e5303663dcb 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -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 index 00000000000..d69597efb86 --- /dev/null +++ b/ext/reflection/tests/bug74949.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #74949 (null pointer dereference in _function_string) +--FILE-- +getPrototype(); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Method [ public method __invoke ] { +} + +Method Closure::__invoke does not have a prototype From efb1be547c1f7f37dbb08f980e15e01490aff6ca Mon Sep 17 00:00:00 2001 From: Xinchen Hui Date: Thu, 27 Jul 2017 11:23:52 +0800 Subject: [PATCH 2/2] Update NEWS --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 807395c5db7..08d2032c965 100644 --- a/NEWS +++ b/NEWS @@ -31,6 +31,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 #74892 (Url Rewriting (trans_sid) not working on urls that start with "#"). (Andrew Nester)