summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/bug29210.phpt104
-rw-r--r--Zend/zend_API.c25
-rw-r--r--Zend/zend_API.h6
-rw-r--r--Zend/zend_object_handlers.c12
-rw-r--r--Zend/zend_object_handlers.h2
-rw-r--r--ext/standard/basic_functions.c4
6 files changed, 141 insertions, 12 deletions
diff --git a/Zend/tests/bug29210.phpt b/Zend/tests/bug29210.phpt
new file mode 100644
index 0000000000..294685499c
--- /dev/null
+++ b/Zend/tests/bug29210.phpt
@@ -0,0 +1,104 @@
+--TEST--
+Bug #29210 Function: is_callable - no support for private and protected classes
+--FILE--
+<?php
+class test_class {
+ private function test_func1() {
+ echo "test_func1\n";
+ }
+ protected function test_func2() {
+ echo "test_func2\n";
+ }
+ static private function test_func3() {
+ echo "test_func3\n";
+ }
+ static protected function test_func4() {
+ echo "test_func4\n";
+ }
+ function test() {
+ if (is_callable(array($this,'test_func1'))) {
+ $this->test_func1();
+ } else {
+ echo "test_func1 isn't callable from inside\n";
+ }
+ if (is_callable(array($this,'test_func2'))) {
+ $this->test_func2();
+ } else {
+ echo "test_func2 isn't callable from inside\n";
+ }
+ if (is_callable(array('test_class','test_func3'))) {
+ test_class::test_func3();
+ } else {
+ echo "test_func3 isn't callable from inside\n";
+ }
+ if (is_callable(array('test_class','test_func4'))) {
+ test_class::test_func4();
+ } else {
+ echo "test_func4 isn't callable from inside\n";
+ }
+ }
+}
+
+class foo extends test_class {
+ function test() {
+ if (is_callable(array($this,'test_func1'))) {
+ $this->test_func1();
+ } else {
+ echo "test_func1 isn't callable from child\n";
+ }
+ if (is_callable(array($this,'test_func2'))) {
+ $this->test_func2();
+ } else {
+ echo "test_func2 isn't callable from child\n";
+ }
+ if (is_callable(array('test_class','test_func3'))) {
+ test_class::test_func3();
+ } else {
+ echo "test_func3 isn't callable from child\n";
+ }
+ if (is_callable(array('test_class','test_func4'))) {
+ test_class::test_func4();
+ } else {
+ echo "test_func4 isn't callable from child\n";
+ }
+ }
+}
+
+$object = new test_class;
+$object->test();
+if (is_callable(array($object,'test_func1'))) {
+ $object->test_func1();
+} else {
+ echo "test_func1 isn't callable from outside\n";
+}
+if (is_callable(array($object,'test_func2'))) {
+ $object->test_func2();
+} else {
+ echo "test_func2 isn't callable from outside\n";
+}
+if (is_callable(array('test_class','test_func3'))) {
+ test_class::test_func3();
+} else {
+ echo "test_func3 isn't callable from outside\n";
+}
+if (is_callable(array('test_class','test_func4'))) {
+ test_class::test_func4();
+} else {
+ echo "test_func4 isn't callable from outside\n";
+}
+$object = new foo();
+$object->test();
+?>
+--EXPECT--
+test_func1
+test_func2
+test_func3
+test_func4
+test_func1 isn't callable from outside
+test_func2 isn't callable from outside
+test_func3 isn't callable from outside
+test_func4 isn't callable from outside
+test_func1 isn't callable from child
+test_func2
+test_func3 isn't callable from child
+test_func4
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 6e66cfd288..16a9648af1 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -1741,7 +1741,7 @@ ZEND_API int zend_disable_class(char *class_name, uint class_name_length TSRMLS_
return 1;
}
-ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char **callable_name)
+ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, char **callable_name)
{
char *lcname;
zend_bool retval = 0;
@@ -1752,7 +1752,7 @@ ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char
if (callable_name) {
*callable_name = estrndup(Z_STRVAL_P(callable), Z_STRLEN_P(callable));
}
- if (syntax_only) {
+ if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) {
return 1;
}
@@ -1789,7 +1789,7 @@ ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char
memcpy(ptr, Z_STRVAL_PP(method), Z_STRLEN_PP(method) + 1);
}
- if (syntax_only)
+ if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY)
return 1;
lcname = zend_str_tolower_dup(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj));
@@ -1818,14 +1818,27 @@ ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char
memcpy(ptr, Z_STRVAL_PP(method), Z_STRLEN_PP(method) + 1);
}
- if (syntax_only)
+ if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY)
return 1;
}
if (ce) {
+ zend_function *fbc;
+
lcname = zend_str_tolower_dup(Z_STRVAL_PP(method), Z_STRLEN_PP(method));
- if (zend_hash_exists(&ce->function_table, lcname, Z_STRLEN_PP(method)+1)) {
- retval = 1;
+ if (zend_hash_find(&ce->function_table, lcname, Z_STRLEN_PP(method)+1, (void **)&fbc) == SUCCESS) {
+ retval = 1;
+ if ((check_flags & IS_CALLABLE_CHECK_NO_ACCESS) == 0) {
+ if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
+ if (!zend_check_private(fbc, (Z_TYPE_PP(obj) == IS_STRING)?EG(scope):(*obj)->value.obj.handlers->get_class_entry(*obj TSRMLS_CC), lcname, Z_STRLEN_PP(method) TSRMLS_CC)) {
+ retval = 0;
+ }
+ } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
+ if (!zend_check_protected(fbc->common.scope, EG(scope))) {
+ retval = 0;
+ }
+ }
+ }
}
/* check for __call too */
if (retval == 0 && ce->__call != 0) {
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 131dc9826a..99c7aa4edc 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -184,7 +184,11 @@ ZEND_API int zend_disable_function(char *function_name, uint function_name_lengt
ZEND_API int zend_disable_class(char *class_name, uint class_name_length TSRMLS_DC);
ZEND_API void zend_wrong_param_count(TSRMLS_D);
-ZEND_API zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char **callable_name);
+
+#define IS_CALLABLE_CHECK_SYNTAX_ONLY (1<<0)
+#define IS_CALLABLE_CHECK_NO_ACCESS (1<<1)
+
+ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, char **callable_name);
ZEND_API zend_bool zend_make_callable(zval *callable, char **callable_name TSRMLS_DC);
ZEND_API char *zend_get_module_version(char *module_name);
ZEND_API int zend_get_module_started(char *module_name);
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 8f2b1e9502..e7ac0a3e78 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -531,7 +531,7 @@ ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS)
* Returns the function address that should be called, or NULL
* if no such function exists.
*/
-static inline zend_function *zend_check_private(zend_function *fbc, zend_class_entry *ce, int fn_flags, char *function_name_strval, int function_name_strlen TSRMLS_DC)
+static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC)
{
if (!ce) {
return 0;
@@ -566,6 +566,12 @@ static inline zend_function *zend_check_private(zend_function *fbc, zend_class_e
}
+ZEND_API int zend_check_private(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC)
+{
+ return zend_check_private_int(fbc, ce, function_name_strval, function_name_strlen TSRMLS_CC) != NULL;
+}
+
+
/* Ensures that we're allowed to call a protected method.
*/
ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope)
@@ -643,7 +649,7 @@ static union _zend_function *zend_std_get_method(zval **object_ptr, char *method
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- updated_fbc = zend_check_private(fbc, object->value.obj.handlers->get_class_entry(object TSRMLS_CC), fbc->common.fn_flags, lc_method_name, method_len TSRMLS_CC);
+ updated_fbc = zend_check_private_int(fbc, object->value.obj.handlers->get_class_entry(object TSRMLS_CC), lc_method_name, method_len TSRMLS_CC);
if (!updated_fbc) {
zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : "");
}
@@ -681,7 +687,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *f
/* Ensure that if we're calling a private function, we're allowed to do so.
*/
- updated_fbc = zend_check_private(fbc, EG(scope), fbc->common.fn_flags, function_name_strval, function_name_strlen TSRMLS_CC);
+ updated_fbc = zend_check_private_int(fbc, EG(scope), function_name_strval, function_name_strlen TSRMLS_CC);
if (!updated_fbc) {
zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), function_name_strval, EG(scope) ? EG(scope)->name : "");
}
diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h
index 1cd6c0a8ad..5284ba2efd 100644
--- a/Zend/zend_object_handlers.h
+++ b/Zend/zend_object_handlers.h
@@ -139,6 +139,8 @@ ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int ty
#define IS_ZEND_STD_OBJECT(z) ((z).type == IS_OBJECT && (Z_OBJ_HT((z))->get_class_entry != NULL))
#define HAS_CLASS_ENTRY(z) (Z_OBJ_HT(z)->get_class_entry != NULL)
+ZEND_API int zend_check_private(union _zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC);
+
ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope);
ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name TSRMLS_DC);
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 2e4fb54372..fe9f781e84 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -2051,7 +2051,7 @@ PHP_FUNCTION(call_user_func)
convert_to_string_ex(params[0]);
}
- if (!zend_is_callable(*params[0], 0, &name)) {
+ if (!zend_is_callable(*params[0], IS_CALLABLE_CHECK_NO_ACCESS, &name)) {
php_error_docref1(NULL TSRMLS_CC, name, E_WARNING, "First argument is expected to be a valid callback");
efree(name);
efree(params);
@@ -2106,7 +2106,7 @@ PHP_FUNCTION(call_user_func_array)
convert_to_string_ex(func);
}
- if (!zend_is_callable(*func, 0, &name)) {
+ if (!zend_is_callable(*func, IS_CALLABLE_CHECK_NO_ACCESS, &name)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "First argument is expected to be a valid callback, '%s' was given", name);
efree(name);
RETURN_NULL();