mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
core: Remove disable_classes INI setting
RFC: https://wiki.php.net/rfc/deprecations_php_8_5#remove_disable_classes_ini_setting This took longer to merge than expected but the initial motivation from 2 years ago still applied: As described in the email to the PHP internals list [1] this feature is fundamentally broken and pointless. Only internal classes can be disable which brings the following observation. On a minimal build of PHP, with only the mandatory extensions enabled, there are 148 classes/interfaces/traits defined. [2] Other than the SPL ones (and even then), disabling any of these classes will cause issues within the engine. Moreover, the SPL ones are not a security concern. Therefore, any other class that can be disabled must come from an extension that can be disabled altogether. And "disabling" a class from an extension without disabling said extension will render it useless anyway. If a hosting provided is concerned about an extension, then it should not enable it in the first place. Not break it ad hoc. Considering the above, I cannot see how this functionality was ever useful. This is in stark contrast to the disable_functions INI setting, which can be used to selectively remove functionality of an extension without breaking it overall. What makes this setting particularly broken is that it does not unregister the class, it only overwrites the create CE handler to emit a warning and purge the properties and function hashtables. This leads to various use after free, segfaults, and broken expectations for the engine and extensions which define said classes. On top of that, it is possible to actually instantiate such a class (and even classes which actually disallow this like ext/imap) in userland, and pass it to function that are typed against said class without raising a TypeError. However, when trying to do anything with said object stuff is going to explode in countless ways. [1] https://news-web.php.net/php.internals/120896 [2] https://gist.github.com/Girgias/63d55ba1e50b580412b004046daed02b
This commit is contained in:
committed by
GitHub
parent
5d5305d99d
commit
f4e2e91d4b
@@ -1,23 +0,0 @@
|
|||||||
--TEST--
|
|
||||||
Bug #77494 (Disabling class causes segfault on member access)
|
|
||||||
--EXTENSIONS--
|
|
||||||
curl
|
|
||||||
--INI--
|
|
||||||
disable_classes=CURLFile,ErrorException
|
|
||||||
--FILE--
|
|
||||||
<?php
|
|
||||||
$a = new CURLFile();
|
|
||||||
var_dump($a->name);
|
|
||||||
$b = new ErrorException();
|
|
||||||
var_dump($b->message);
|
|
||||||
?>
|
|
||||||
--EXPECTF--
|
|
||||||
Warning: CURLFile() has been disabled for security reasons in %sbug77494.php on line 2
|
|
||||||
|
|
||||||
Warning: Undefined property: CURLFile::$name in %s on line %d
|
|
||||||
NULL
|
|
||||||
|
|
||||||
Warning: ErrorException() has been disabled for security reasons in %s on line %d
|
|
||||||
|
|
||||||
Warning: Undefined property: ErrorException::$message in %s on line %d
|
|
||||||
NULL
|
|
||||||
27
Zend/tests/disable_classes_warning.phpt
Normal file
27
Zend/tests/disable_classes_warning.phpt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
--TEST--
|
||||||
|
Check that warning is emitted when disabling classes
|
||||||
|
--INI--
|
||||||
|
disable_classes=Exception
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
$o = new Exception();
|
||||||
|
var_dump($o);
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
object(Exception)#1 (7) {
|
||||||
|
["message":protected]=>
|
||||||
|
string(0) ""
|
||||||
|
["string":"Exception":private]=>
|
||||||
|
string(0) ""
|
||||||
|
["code":protected]=>
|
||||||
|
int(0)
|
||||||
|
["file":protected]=>
|
||||||
|
string(%d) "%s"
|
||||||
|
["line":protected]=>
|
||||||
|
int(2)
|
||||||
|
["trace":"Exception":private]=>
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
["previous":"Exception":private]=>
|
||||||
|
NULL
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
--TEST--
|
|
||||||
errmsg: disabled class
|
|
||||||
--INI--
|
|
||||||
disable_classes=stdclass
|
|
||||||
--FILE--
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class test extends stdclass {
|
|
||||||
}
|
|
||||||
|
|
||||||
$t = new test;
|
|
||||||
|
|
||||||
echo "Done\n";
|
|
||||||
?>
|
|
||||||
--EXPECTF--
|
|
||||||
Warning: test() has been disabled for security reasons in %s on line %d
|
|
||||||
Done
|
|
||||||
@@ -3692,77 +3692,6 @@ ZEND_API void zend_disable_functions(const char *function_list) /* {{{ */
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
#ifdef ZEND_WIN32
|
|
||||||
#pragma optimize("", off)
|
|
||||||
#endif
|
|
||||||
static ZEND_COLD zend_object *display_disabled_class(zend_class_entry *class_type) /* {{{ */
|
|
||||||
{
|
|
||||||
zend_object *intern;
|
|
||||||
|
|
||||||
intern = zend_objects_new(class_type);
|
|
||||||
|
|
||||||
/* Initialize default properties */
|
|
||||||
if (EXPECTED(class_type->default_properties_count != 0)) {
|
|
||||||
zval *p = intern->properties_table;
|
|
||||||
zval *end = p + class_type->default_properties_count;
|
|
||||||
do {
|
|
||||||
ZVAL_UNDEF(p);
|
|
||||||
p++;
|
|
||||||
} while (p != end);
|
|
||||||
}
|
|
||||||
|
|
||||||
zend_error(E_WARNING, "%s() has been disabled for security reasons", ZSTR_VAL(class_type->name));
|
|
||||||
return intern;
|
|
||||||
}
|
|
||||||
#ifdef ZEND_WIN32
|
|
||||||
#pragma optimize("", on)
|
|
||||||
#endif
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
static const zend_function_entry disabled_class_new[] = {
|
|
||||||
ZEND_FE_END
|
|
||||||
};
|
|
||||||
|
|
||||||
ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length) /* {{{ */
|
|
||||||
{
|
|
||||||
zend_class_entry *disabled_class;
|
|
||||||
zend_string *key;
|
|
||||||
zend_function *fn;
|
|
||||||
zend_property_info *prop;
|
|
||||||
|
|
||||||
key = zend_string_alloc(class_name_length, 0);
|
|
||||||
zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length);
|
|
||||||
disabled_class = zend_hash_find_ptr(CG(class_table), key);
|
|
||||||
zend_string_release_ex(key, 0);
|
|
||||||
if (!disabled_class) {
|
|
||||||
return FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Will be reset by INIT_CLASS_ENTRY. */
|
|
||||||
free(disabled_class->interfaces);
|
|
||||||
|
|
||||||
INIT_CLASS_ENTRY_INIT_METHODS((*disabled_class), disabled_class_new);
|
|
||||||
disabled_class->create_object = display_disabled_class;
|
|
||||||
|
|
||||||
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->function_table, fn) {
|
|
||||||
if ((fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
|
|
||||||
fn->common.scope == disabled_class) {
|
|
||||||
zend_free_internal_arg_info(&fn->internal_function);
|
|
||||||
}
|
|
||||||
} ZEND_HASH_FOREACH_END();
|
|
||||||
zend_hash_clean(&disabled_class->function_table);
|
|
||||||
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->properties_info, prop) {
|
|
||||||
if (prop->ce == disabled_class) {
|
|
||||||
zend_string_release(prop->name);
|
|
||||||
zend_type_release(prop->type, /* persistent */ 1);
|
|
||||||
free(prop);
|
|
||||||
}
|
|
||||||
} ZEND_HASH_FOREACH_END();
|
|
||||||
zend_hash_clean(&disabled_class->properties_info);
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame)
|
static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame)
|
||||||
{
|
{
|
||||||
return frame && frame->func ? frame->func->common.scope : NULL;
|
return frame && frame->func ? frame->func->common.scope : NULL;
|
||||||
|
|||||||
@@ -405,7 +405,6 @@ static zend_always_inline zend_result zend_register_class_alias(const char *name
|
|||||||
zend_register_class_alias_ex(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, ce, 1)
|
zend_register_class_alias_ex(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, ce, 1)
|
||||||
|
|
||||||
ZEND_API void zend_disable_functions(const char *function_list);
|
ZEND_API void zend_disable_functions(const char *function_list);
|
||||||
ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length);
|
|
||||||
|
|
||||||
ZEND_API ZEND_COLD void zend_wrong_param_count(void);
|
ZEND_API ZEND_COLD void zend_wrong_param_count(void);
|
||||||
ZEND_API ZEND_COLD void zend_wrong_property_read(zval *object, zval *property);
|
ZEND_API ZEND_COLD void zend_wrong_property_read(zval *object, zval *property);
|
||||||
|
|||||||
45
main/main.c
45
main/main.c
@@ -413,41 +413,6 @@ static PHP_INI_MH(OnSetLogFilter)
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ php_disable_classes */
|
|
||||||
static void php_disable_classes(void)
|
|
||||||
{
|
|
||||||
char *s = NULL, *e;
|
|
||||||
|
|
||||||
if (!*(INI_STR("disable_classes"))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e = PG(disable_classes) = strdup(INI_STR("disable_classes"));
|
|
||||||
|
|
||||||
while (*e) {
|
|
||||||
switch (*e) {
|
|
||||||
case ' ':
|
|
||||||
case ',':
|
|
||||||
if (s) {
|
|
||||||
*e = '\0';
|
|
||||||
zend_disable_class(s, e-s);
|
|
||||||
s = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!s) {
|
|
||||||
s = e;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
e++;
|
|
||||||
}
|
|
||||||
if (s) {
|
|
||||||
zend_disable_class(s, e-s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/* {{{ php_binary_init */
|
/* {{{ php_binary_init */
|
||||||
static void php_binary_init(void)
|
static void php_binary_init(void)
|
||||||
{
|
{
|
||||||
@@ -872,7 +837,6 @@ PHP_INI_BEGIN()
|
|||||||
PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL)
|
PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL)
|
||||||
PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra)
|
PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra)
|
||||||
PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL)
|
PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL)
|
||||||
PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL)
|
|
||||||
PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
|
PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
|
||||||
PHP_INI_ENTRY("max_multipart_body_parts", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
|
PHP_INI_ENTRY("max_multipart_body_parts", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
|
||||||
|
|
||||||
@@ -2105,9 +2069,6 @@ static void core_globals_dtor(php_core_globals *core_globals)
|
|||||||
ZEND_ASSERT(!core_globals->last_error_message);
|
ZEND_ASSERT(!core_globals->last_error_message);
|
||||||
ZEND_ASSERT(!core_globals->last_error_file);
|
ZEND_ASSERT(!core_globals->last_error_file);
|
||||||
|
|
||||||
if (core_globals->disable_classes) {
|
|
||||||
free(core_globals->disable_classes);
|
|
||||||
}
|
|
||||||
if (core_globals->php_binary) {
|
if (core_globals->php_binary) {
|
||||||
free(core_globals->php_binary);
|
free(core_globals->php_binary);
|
||||||
}
|
}
|
||||||
@@ -2372,9 +2333,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disable certain classes and functions as requested by php.ini */
|
/* disable certain functions as requested by php.ini */
|
||||||
zend_disable_functions(INI_STR("disable_functions"));
|
zend_disable_functions(INI_STR("disable_functions"));
|
||||||
php_disable_classes();
|
|
||||||
|
|
||||||
/* make core report what it should */
|
/* make core report what it should */
|
||||||
if ((module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core")-1)) != NULL) {
|
if ((module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core")-1)) != NULL) {
|
||||||
@@ -2403,7 +2363,7 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
|
|||||||
struct {
|
struct {
|
||||||
const long error_level;
|
const long error_level;
|
||||||
const char *phrase;
|
const char *phrase;
|
||||||
const char *directives[18]; /* Remember to change this if the number of directives change */
|
const char *directives[19]; /* Remember to change this if the number of directives change */
|
||||||
} directives[2] = {
|
} directives[2] = {
|
||||||
{
|
{
|
||||||
E_DEPRECATED,
|
E_DEPRECATED,
|
||||||
@@ -2434,6 +2394,7 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
|
|||||||
"safe_mode_protected_env_vars",
|
"safe_mode_protected_env_vars",
|
||||||
"zend.ze1_compatibility_mode",
|
"zend.ze1_compatibility_mode",
|
||||||
"track_errors",
|
"track_errors",
|
||||||
|
"disable_classes",
|
||||||
NULL
|
NULL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ struct _php_core_globals {
|
|||||||
|
|
||||||
char *php_sys_temp_dir;
|
char *php_sys_temp_dir;
|
||||||
|
|
||||||
char *disable_classes;
|
|
||||||
zend_long max_input_nesting_level;
|
zend_long max_input_nesting_level;
|
||||||
zend_long max_input_vars;
|
zend_long max_input_vars;
|
||||||
|
|
||||||
|
|||||||
@@ -327,11 +327,6 @@ serialize_precision = -1
|
|||||||
; https://php.net/disable-functions
|
; https://php.net/disable-functions
|
||||||
disable_functions =
|
disable_functions =
|
||||||
|
|
||||||
; This directive allows you to disable certain classes.
|
|
||||||
; It receives a comma-delimited list of class names.
|
|
||||||
; https://php.net/disable-classes
|
|
||||||
disable_classes =
|
|
||||||
|
|
||||||
; Colors for Syntax Highlighting mode. Anything that's acceptable in
|
; Colors for Syntax Highlighting mode. Anything that's acceptable in
|
||||||
; <span style="color: ???????"> would work.
|
; <span style="color: ???????"> would work.
|
||||||
; https://php.net/syntax-highlighting
|
; https://php.net/syntax-highlighting
|
||||||
|
|||||||
@@ -327,11 +327,6 @@ serialize_precision = -1
|
|||||||
; https://php.net/disable-functions
|
; https://php.net/disable-functions
|
||||||
disable_functions =
|
disable_functions =
|
||||||
|
|
||||||
; This directive allows you to disable certain classes.
|
|
||||||
; It receives a comma-delimited list of class names.
|
|
||||||
; https://php.net/disable-classes
|
|
||||||
disable_classes =
|
|
||||||
|
|
||||||
; Colors for Syntax Highlighting mode. Anything that's acceptable in
|
; Colors for Syntax Highlighting mode. Anything that's acceptable in
|
||||||
; <span style="color: ???????"> would work.
|
; <span style="color: ???????"> would work.
|
||||||
; https://php.net/syntax-highlighting
|
; https://php.net/syntax-highlighting
|
||||||
|
|||||||
@@ -48,35 +48,6 @@ static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_
|
|||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
static void fpm_php_disable(char *value, int (*zend_disable)(const char *, size_t)) /* {{{ */
|
|
||||||
{
|
|
||||||
char *s = 0, *e = value;
|
|
||||||
|
|
||||||
while (*e) {
|
|
||||||
switch (*e) {
|
|
||||||
case ' ':
|
|
||||||
case ',':
|
|
||||||
if (s) {
|
|
||||||
*e = '\0';
|
|
||||||
zend_disable(s, e - s);
|
|
||||||
s = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!s) {
|
|
||||||
s = e;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
e++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s) {
|
|
||||||
zend_disable(s, e - s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
#define FPM_PHP_INI_ALTERING_ERROR -1
|
#define FPM_PHP_INI_ALTERING_ERROR -1
|
||||||
#define FPM_PHP_INI_APPLIED 1
|
#define FPM_PHP_INI_APPLIED 1
|
||||||
#define FPM_PHP_INI_EXTENSION_FAILED 0
|
#define FPM_PHP_INI_EXTENSION_FAILED 0
|
||||||
@@ -121,13 +92,6 @@ int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
|
|||||||
return FPM_PHP_INI_APPLIED;
|
return FPM_PHP_INI_APPLIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(name, "disable_classes") && *value) {
|
|
||||||
char *v = strdup(value);
|
|
||||||
PG(disable_classes) = v;
|
|
||||||
fpm_php_disable(v, zend_disable_class);
|
|
||||||
return FPM_PHP_INI_APPLIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FPM_PHP_INI_APPLIED;
|
return FPM_PHP_INI_APPLIED;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|||||||
@@ -474,9 +474,8 @@ pm.max_spare_servers = 3
|
|||||||
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
|
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
|
||||||
|
|
||||||
; Defining 'extension' will load the corresponding shared extension from
|
; Defining 'extension' will load the corresponding shared extension from
|
||||||
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
|
; extension_dir. Defining 'disable_functions' will not overwrite previously
|
||||||
; overwrite previously defined php.ini values, but will append the new value
|
; defined php.ini values, but will append the new value instead.
|
||||||
; instead.
|
|
||||||
|
|
||||||
; Note: path INI options can be relative and will be expanded with the prefix
|
; Note: path INI options can be relative and will be expanded with the prefix
|
||||||
; (pool, global or @prefix@)
|
; (pool, global or @prefix@)
|
||||||
|
|||||||
@@ -56,8 +56,6 @@ static const char HARDCODED_INI[] =
|
|||||||
",crypt"
|
",crypt"
|
||||||
/* openlog() has a known memory-management issue. */
|
/* openlog() has a known memory-management issue. */
|
||||||
",openlog"
|
",openlog"
|
||||||
/* Can cause long loops that bypass the executor step limit. */
|
|
||||||
"\ndisable_classes=InfiniteIterator"
|
|
||||||
;
|
;
|
||||||
|
|
||||||
static int startup(sapi_module_struct *sapi_module)
|
static int startup(sapi_module_struct *sapi_module)
|
||||||
@@ -128,6 +126,21 @@ static sapi_module_struct fuzzer_module = {
|
|||||||
STANDARD_SAPI_MODULE_PROPERTIES
|
STANDARD_SAPI_MODULE_PROPERTIES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static ZEND_COLD zend_object *disable_class_create_handler(zend_class_entry *class_type) /* {{{ */
|
||||||
|
{
|
||||||
|
zend_throw_error(NULL, "Cannot construct class %s, as it is disabled", ZSTR_VAL(class_type->name));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fuzzer_disable_classes(void)
|
||||||
|
{
|
||||||
|
/* Overwrite built-in constructor for InfiniteIterator as it
|
||||||
|
* can cause long loops that bypass the executor step limit. */
|
||||||
|
/* Lowercase as this is how the CE as stored */
|
||||||
|
zend_class_entry *InfiniteIterator_class = zend_hash_str_find(CG(class_table), "infiniteiterator", strlen("infiniteiterator"));
|
||||||
|
InfiniteIterator_class->create_object = disable_class_create_handler;
|
||||||
|
}
|
||||||
|
|
||||||
int fuzzer_init_php(const char *extra_ini)
|
int fuzzer_init_php(const char *extra_ini)
|
||||||
{
|
{
|
||||||
#ifdef __SANITIZE_ADDRESS__
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
@@ -183,6 +196,8 @@ int fuzzer_request_startup(void)
|
|||||||
SIGG(check) = 0;
|
SIGG(check) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
fuzzer_disable_classes();
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user