Skip to content

Completely remove disabled functions from function table #5473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 38 additions & 26 deletions Zend/tests/bug69315.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,44 @@ disable_functions=strlen,defined,call_user_func,constant,is_string

var_dump(function_exists("strlen"));
var_dump(is_callable("strlen"));
var_dump(strlen("xxx"));
var_dump(defined("PHP_VERSION"));
var_dump(constant("PHP_VERSION"));
var_dump(call_user_func("strlen"));
var_dump(is_string("xxx"));
var_dump(is_string());
try {
var_dump(strlen("xxx"));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(defined("PHP_VERSION"));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(constant("PHP_VERSION"));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(call_user_func("strlen"));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(is_string("xxx"));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(is_string());
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
--EXPECT--
bool(false)
bool(true)

Warning: strlen() has been disabled for security reasons in %sbug69315.php on line %d
NULL

Warning: defined() has been disabled for security reasons in %sbug69315.php on line %d
NULL

Warning: constant() has been disabled for security reasons in %sbug69315.php on line %d
NULL

Warning: call_user_func() has been disabled for security reasons in %sbug69315.php on line %d
NULL

Warning: is_string() has been disabled for security reasons in %sbug69315.php on line %d
NULL

Warning: is_string() has been disabled for security reasons in %s on line %d
NULL
bool(false)
Call to undefined function strlen()
Call to undefined function defined()
Call to undefined function constant()
Call to undefined function call_user_func()
Call to undefined function is_string()
Call to undefined function is_string()
18 changes: 18 additions & 0 deletions Zend/tests/bug79382.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Bug #79382: Cannot redeclare disabled function
--INI--
disable_functions=strlen
--FILE--
<?php

function strlen(string $x): int {
$len = 0;
while (isset($x[$len])) $len++;
return $len;
}

var_dump(strlen("foobar"));

?>
--EXPECT--
int(6)
12 changes: 7 additions & 5 deletions Zend/tests/errmsg_020.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ disable_functions=phpinfo
--FILE--
<?php

phpinfo();
try {
phpinfo();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

echo "Done\n";
?>
--EXPECTF--
Warning: phpinfo() has been disabled for security reasons in %s on line %d
Done
--EXPECT--
Call to undefined function phpinfo()
20 changes: 1 addition & 19 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2686,27 +2686,9 @@ ZEND_API int zend_set_hash_symbol(zval *symbol, const char *name, int name_lengt

/* Disabled functions support */

/* {{{ proto void display_disabled_function(void)
Dummy function which displays an error when a disabled function is called. */
ZEND_API ZEND_COLD ZEND_FUNCTION(display_disabled_function)
{
zend_error(E_WARNING, "%s() has been disabled for security reasons", get_active_function_name());
}
/* }}} */

ZEND_API int zend_disable_function(char *function_name, size_t function_name_length) /* {{{ */
{
zend_internal_function *func;
if ((func = zend_hash_str_find_ptr(CG(function_table), function_name, function_name_length))) {
zend_free_internal_arg_info(func);
func->fn_flags &= ~(ZEND_ACC_VARIADIC | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_RETURN_TYPE);
func->num_args = 0;
func->required_num_args = 0;
func->arg_info = NULL;
func->handler = ZEND_FN(display_disabled_function);
return SUCCESS;
}
return FAILURE;
return zend_hash_str_del(CG(function_table), function_name, function_name_length);
}
/* }}} */

Expand Down
2 changes: 0 additions & 2 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -593,8 +593,6 @@ ZEND_API zend_bool zend_is_iterable(zval *iterable);

ZEND_API zend_bool zend_is_countable(zval *countable);

ZEND_API ZEND_FUNCTION(display_disabled_function);

ZEND_API int zend_get_default_from_internal_arg_info(
zval *default_value_zval, zend_internal_arg_info *arg_info);

Expand Down
30 changes: 10 additions & 20 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@ ZEND_FUNCTION(trait_exists)
ZEND_FUNCTION(function_exists)
{
zend_string *name;
zend_function *func;
zend_bool exists;
zend_string *lcname;

ZEND_PARSE_PARAMETERS_START(1, 1)
Expand All @@ -1142,15 +1142,10 @@ ZEND_FUNCTION(function_exists)
lcname = zend_string_tolower(name);
}

func = zend_hash_find_ptr(EG(function_table), lcname);
exists = zend_hash_exists(EG(function_table), lcname);
zend_string_release_ex(lcname, 0);

/*
* A bit of a hack, but not a bad one: we see if the handler of the function
* is actually one that displays "function is disabled" message.
*/
RETURN_BOOL(func && (func->type != ZEND_INTERNAL_FUNCTION ||
func->internal_function.handler != zif_display_disabled_function));
RETURN_BOOL(exists);
}
/* }}} */

Expand Down Expand Up @@ -1420,30 +1415,25 @@ ZEND_FUNCTION(get_defined_functions)
zval internal, user;
zend_string *key;
zend_function *func;
zend_bool exclude_disabled = 0;
char *disable_functions = NULL;
zend_bool exclude_disabled = 1;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &exclude_disabled) == FAILURE) {
RETURN_THROWS();
}

if (exclude_disabled == 0) {
zend_error(E_DEPRECATED,
"get_defined_functions(): Setting $exclude_disabled to false has no effect");
}

array_init(&internal);
array_init(&user);
array_init(return_value);

if (exclude_disabled) {
disable_functions = INI_STR("disable_functions");
}
ZEND_HASH_FOREACH_STR_KEY_PTR(EG(function_table), key, func) {
if (key && ZSTR_VAL(key)[0] != 0) {
if (func->type == ZEND_INTERNAL_FUNCTION) {
if (disable_functions != NULL) {
if (strstr(disable_functions, func->common.function_name->val) == NULL) {
add_next_index_str(&internal, zend_string_copy(key));
}
} else {
add_next_index_str(&internal, zend_string_copy(key));
}
add_next_index_str(&internal, zend_string_copy(key));
} else if (func->type == ZEND_USER_FUNCTION) {
add_next_index_str(&user, zend_string_copy(key));
}
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_builtin_functions.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function get_declared_traits(): array {}

function get_declared_interfaces(): array {}

function get_defined_functions(bool $exclude_disabled = false): array {}
function get_defined_functions(bool $exclude_disabled = true): array {}

function get_defined_vars(): array {}

Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_builtin_functions_arginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ ZEND_END_ARG_INFO()
#define arginfo_get_declared_interfaces arginfo_func_get_args

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_defined_functions, 0, 0, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, exclude_disabled, _IS_BOOL, 0, "false")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, exclude_disabled, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO()

#define arginfo_get_defined_vars arginfo_func_get_args
Expand Down
4 changes: 0 additions & 4 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -3934,10 +3934,6 @@ int zend_compile_func_array_slice(znode *result, zend_ast_list *args) /* {{{ */

int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
{
if (fbc->internal_function.handler == ZEND_FN(display_disabled_function)) {
return FAILURE;
}

if (CG(compiler_options) & ZEND_COMPILE_NO_BUILTINS) {
return FAILURE;
}
Expand Down
20 changes: 5 additions & 15 deletions ext/opcache/Optimizer/pass1.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) {
if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 &&
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
"function_exists", sizeof("function_exists")-1) &&
!zend_optimizer_is_disabled_func("function_exists", sizeof("function_exists") - 1)) ||
"function_exists", sizeof("function_exists")-1)) ||
(Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 &&
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
"is_callable", sizeof("is_callable")) &&
!zend_optimizer_is_disabled_func("is_callable", sizeof("is_callable") - 1))) {
"is_callable", sizeof("is_callable")))) {
zend_internal_function *func;
zend_string *lc_name = zend_string_tolower(
Z_STR(ZEND_OP1_LITERAL(send1_opline)));
Expand All @@ -400,12 +398,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
#endif
) {
zval t;
if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable") - 1 ||
func->handler != ZEND_FN(display_disabled_function)) {
ZVAL_TRUE(&t);
} else {
ZVAL_FALSE(&t);
}
ZVAL_TRUE(&t);
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
MAKE_NOP(init_opline);
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
Expand All @@ -423,8 +416,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 &&
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
"extension_loaded", sizeof("extension_loaded")-1) &&
!zend_optimizer_is_disabled_func("extension_loaded", sizeof("extension_loaded") - 1)) {
"extension_loaded", sizeof("extension_loaded")-1)) {
zval t;
zend_string *lc_name = zend_string_tolower(
Z_STR(ZEND_OP1_LITERAL(send1_opline)));
Expand Down Expand Up @@ -465,8 +457,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
break;
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 &&
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
"constant", sizeof("constant")-1) &&
!zend_optimizer_is_disabled_func("constant", sizeof("constant") - 1)) {
"constant", sizeof("constant")-1)) {
zval t;

if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) {
Expand All @@ -488,7 +479,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 &&
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
"dirname", sizeof("dirname") - 1) &&
!zend_optimizer_is_disabled_func("dirname", sizeof("dirname") - 1) &&
IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0);
ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
Expand Down
3 changes: 1 addition & 2 deletions ext/opcache/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1019,8 +1019,7 @@ static inline int ct_eval_func_call(
}

func = zend_hash_find_ptr(CG(function_table), name);
if (!func || func->type != ZEND_INTERNAL_FUNCTION
|| func->internal_function.handler == ZEND_FN(display_disabled_function)) {
if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
return FAILURE;
}

Expand Down
4 changes: 1 addition & 3 deletions ext/opcache/Optimizer/zend_func_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -899,9 +899,7 @@ static uint32_t get_internal_func_info(
}

func_info_t *info = Z_PTR_P(zv);
if (UNEXPECTED(zend_optimizer_is_disabled_func(info->name, info->name_len))) {
return MAY_BE_NULL;
} else if (info->info_func) {
if (info->info_func) {
return info->info_func(call_info, ssa);
} else {
return info->info;
Expand Down
7 changes: 0 additions & 7 deletions ext/opcache/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,6 @@ static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zen
return zend_optimizer_add_literal(op_array, &zv);
}

int zend_optimizer_is_disabled_func(const char *name, size_t len) {
zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);

return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
fbc->internal_function.handler == ZEND_FN(display_disabled_function));
}

static inline void drop_leading_backslash(zval *val) {
if (Z_STRVAL_P(val)[0] == '\\') {
zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
Expand Down
1 change: 0 additions & 1 deletion ext/opcache/Optimizer/zend_optimizer_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
void zend_optimizer_compact_vars(zend_op_array *op_array);
int zend_optimizer_is_disabled_func(const char *name, size_t len);
zend_function *zend_optimizer_get_called_func(
zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool *is_prototype);
uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
Expand Down
13 changes: 8 additions & 5 deletions ext/opcache/tests/bug68104.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ disable_functions=dl
--FILE--
<?php
var_dump(is_callable("dl"));
dl("a.so");
try {
dl("a.so");
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECTF--
bool(true)

Warning: dl() has been disabled for security reasons in %sbug68104.php on line %d
--EXPECT--
bool(false)
Call to undefined function dl()
11 changes: 7 additions & 4 deletions ext/opcache/tests/bug76796.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ disable_functions=strpos
--FILE--
<?php

var_dump(strpos('foo', 'bar'));
try {
var_dump(strpos('foo', 'bar'));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
Warning: strpos() has been disabled for security reasons in %s on line %d
NULL
--EXPECT--
Call to undefined function strpos()
8 changes: 2 additions & 6 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1549,16 +1549,12 @@ ZEND_METHOD(ReflectionFunctionAbstract, isUserDefined)
Returns whether this function has been disabled or not */
ZEND_METHOD(ReflectionFunction, isDisabled)
{
reflection_object *intern;
zend_function *fptr;

GET_REFLECTION_OBJECT_PTR(fptr);

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}

RETURN_BOOL(fptr->type == ZEND_INTERNAL_FUNCTION && fptr->internal_function.handler == zif_display_disabled_function);
/* A disabled function cannot be queried using Reflection. */
RETURN_FALSE;
}
/* }}} */

Expand Down
Loading