Skip to content

Commit b053717

Browse files
committed
core: Remove disable_classes INI setting
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
1 parent 8a24e31 commit b053717

File tree

11 files changed

+43
-203
lines changed

11 files changed

+43
-203
lines changed

Zend/tests/bug77494.phpt

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Check that warning is emitted when disabling classes
3+
--INI--
4+
disable_classes=Exception
5+
--FILE--
6+
<?php
7+
$o = new Exception();
8+
var_dump($o);
9+
?>
10+
--EXPECTF--
11+
object(Exception)#1 (7) {
12+
["message":protected]=>
13+
string(0) ""
14+
["string":"Exception":private]=>
15+
string(0) ""
16+
["code":protected]=>
17+
int(0)
18+
["file":protected]=>
19+
string(%d) "%s"
20+
["line":protected]=>
21+
int(2)
22+
["trace":"Exception":private]=>
23+
array(0) {
24+
}
25+
["previous":"Exception":private]=>
26+
NULL
27+
}

Zend/tests/errmsg_021.phpt

Lines changed: 0 additions & 17 deletions
This file was deleted.

Zend/zend_API.c

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3579,77 +3579,6 @@ ZEND_API void zend_disable_functions(const char *function_list) /* {{{ */
35793579
}
35803580
/* }}} */
35813581

3582-
#ifdef ZEND_WIN32
3583-
#pragma optimize("", off)
3584-
#endif
3585-
static ZEND_COLD zend_object *display_disabled_class(zend_class_entry *class_type) /* {{{ */
3586-
{
3587-
zend_object *intern;
3588-
3589-
intern = zend_objects_new(class_type);
3590-
3591-
/* Initialize default properties */
3592-
if (EXPECTED(class_type->default_properties_count != 0)) {
3593-
zval *p = intern->properties_table;
3594-
zval *end = p + class_type->default_properties_count;
3595-
do {
3596-
ZVAL_UNDEF(p);
3597-
p++;
3598-
} while (p != end);
3599-
}
3600-
3601-
zend_error(E_WARNING, "%s() has been disabled for security reasons", ZSTR_VAL(class_type->name));
3602-
return intern;
3603-
}
3604-
#ifdef ZEND_WIN32
3605-
#pragma optimize("", on)
3606-
#endif
3607-
/* }}} */
3608-
3609-
static const zend_function_entry disabled_class_new[] = {
3610-
ZEND_FE_END
3611-
};
3612-
3613-
ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length) /* {{{ */
3614-
{
3615-
zend_class_entry *disabled_class;
3616-
zend_string *key;
3617-
zend_function *fn;
3618-
zend_property_info *prop;
3619-
3620-
key = zend_string_alloc(class_name_length, 0);
3621-
zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length);
3622-
disabled_class = zend_hash_find_ptr(CG(class_table), key);
3623-
zend_string_release_ex(key, 0);
3624-
if (!disabled_class) {
3625-
return FAILURE;
3626-
}
3627-
3628-
/* Will be reset by INIT_CLASS_ENTRY. */
3629-
free(disabled_class->interfaces);
3630-
3631-
INIT_CLASS_ENTRY_INIT_METHODS((*disabled_class), disabled_class_new);
3632-
disabled_class->create_object = display_disabled_class;
3633-
3634-
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->function_table, fn) {
3635-
if ((fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
3636-
fn->common.scope == disabled_class) {
3637-
zend_free_internal_arg_info(&fn->internal_function);
3638-
}
3639-
} ZEND_HASH_FOREACH_END();
3640-
zend_hash_clean(&disabled_class->function_table);
3641-
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->properties_info, prop) {
3642-
if (prop->ce == disabled_class) {
3643-
zend_string_release(prop->name);
3644-
zend_type_release(prop->type, /* persistent */ 1);
3645-
free(prop);
3646-
}
3647-
} ZEND_HASH_FOREACH_END();
3648-
zend_hash_clean(&disabled_class->properties_info);
3649-
return SUCCESS;
3650-
}
3651-
/* }}} */
3652-
36533582
static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame)
36543583
{
36553584
return frame && frame->func ? frame->func->common.scope : NULL;

Zend/zend_API.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,6 @@ static zend_always_inline zend_result zend_register_class_alias(const char *name
402402
zend_register_class_alias_ex(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, ce, 1)
403403

404404
ZEND_API void zend_disable_functions(const char *function_list);
405-
ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length);
406405

407406
ZEND_API ZEND_COLD void zend_wrong_param_count(void);
408407
ZEND_API ZEND_COLD void zend_wrong_property_read(zval *object, zval *property);

main/main.c

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -319,41 +319,6 @@ static PHP_INI_MH(OnSetLogFilter)
319319
}
320320
/* }}} */
321321

322-
/* {{{ php_disable_classes */
323-
static void php_disable_classes(void)
324-
{
325-
char *s = NULL, *e;
326-
327-
if (!*(INI_STR("disable_classes"))) {
328-
return;
329-
}
330-
331-
e = PG(disable_classes) = strdup(INI_STR("disable_classes"));
332-
333-
while (*e) {
334-
switch (*e) {
335-
case ' ':
336-
case ',':
337-
if (s) {
338-
*e = '\0';
339-
zend_disable_class(s, e-s);
340-
s = NULL;
341-
}
342-
break;
343-
default:
344-
if (!s) {
345-
s = e;
346-
}
347-
break;
348-
}
349-
e++;
350-
}
351-
if (s) {
352-
zend_disable_class(s, e-s);
353-
}
354-
}
355-
/* }}} */
356-
357322
/* {{{ php_binary_init */
358323
static void php_binary_init(void)
359324
{
@@ -660,6 +625,17 @@ static PHP_INI_MH(OnChangeMailForceExtra)
660625
}
661626
/* }}} */
662627

628+
/* Emit warning when using this INI setting as it is removed */
629+
static PHP_INI_MH(OnChangeDisableClasses)
630+
{
631+
if (stage != PHP_INI_SYSTEM) {
632+
return FAILURE;
633+
}
634+
php_error_docref("disable_clases", E_WARNING, "The disable_classes INI setting has been removed and has no effect");
635+
636+
return SUCCESS;
637+
}
638+
663639
/* defined in browscap.c */
664640
PHP_INI_MH(OnChangeBrowscap);
665641

@@ -757,7 +733,8 @@ PHP_INI_BEGIN()
757733
PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL)
758734
PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra)
759735
PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL)
760-
PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL)
736+
// TODO Add warning when disabling classes
737+
//PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL)
761738
PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
762739
PHP_INI_ENTRY("max_multipart_body_parts", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
763740

@@ -1975,9 +1952,6 @@ static void core_globals_dtor(php_core_globals *core_globals)
19751952
ZEND_ASSERT(!core_globals->last_error_message);
19761953
ZEND_ASSERT(!core_globals->last_error_file);
19771954

1978-
if (core_globals->disable_classes) {
1979-
free(core_globals->disable_classes);
1980-
}
19811955
if (core_globals->php_binary) {
19821956
free(core_globals->php_binary);
19831957
}
@@ -2235,9 +2209,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
22352209
}
22362210
}
22372211

2238-
/* disable certain classes and functions as requested by php.ini */
2212+
/* disable certain functions as requested by php.ini */
22392213
zend_disable_functions(INI_STR("disable_functions"));
2240-
php_disable_classes();
22412214

22422215
/* make core report what it should */
22432216
if ((module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core")-1)) != NULL) {

main/php_globals.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ struct _php_core_globals {
142142

143143
char *php_sys_temp_dir;
144144

145-
char *disable_classes;
146145
zend_long max_input_nesting_level;
147146
zend_long max_input_vars;
148147

php.ini-development

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,6 @@ serialize_precision = -1
332332
; https://php.net/disable-functions
333333
disable_functions =
334334

335-
; This directive allows you to disable certain classes.
336-
; It receives a comma-delimited list of class names.
337-
; https://php.net/disable-classes
338-
disable_classes =
339-
340335
; Colors for Syntax Highlighting mode. Anything that's acceptable in
341336
; <span style="color: ???????"> would work.
342337
; https://php.net/syntax-highlighting

php.ini-production

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,6 @@ serialize_precision = -1
332332
; https://php.net/disable-functions
333333
disable_functions =
334334

335-
; This directive allows you to disable certain classes.
336-
; It receives a comma-delimited list of class names.
337-
; https://php.net/disable-classes
338-
disable_classes =
339-
340335
; Colors for Syntax Highlighting mode. Anything that's acceptable in
341336
; <span style="color: ???????"> would work.
342337
; https://php.net/syntax-highlighting

sapi/fpm/fpm/fpm_php.c

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -48,35 +48,6 @@ static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_
4848
}
4949
/* }}} */
5050

51-
static void fpm_php_disable(char *value, int (*zend_disable)(const char *, size_t)) /* {{{ */
52-
{
53-
char *s = 0, *e = value;
54-
55-
while (*e) {
56-
switch (*e) {
57-
case ' ':
58-
case ',':
59-
if (s) {
60-
*e = '\0';
61-
zend_disable(s, e - s);
62-
s = 0;
63-
}
64-
break;
65-
default:
66-
if (!s) {
67-
s = e;
68-
}
69-
break;
70-
}
71-
e++;
72-
}
73-
74-
if (s) {
75-
zend_disable(s, e - s);
76-
}
77-
}
78-
/* }}} */
79-
8051
#define FPM_PHP_INI_ALTERING_ERROR -1
8152
#define FPM_PHP_INI_APPLIED 1
8253
#define FPM_PHP_INI_EXTENSION_FAILED 0
@@ -107,13 +78,6 @@ int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
10778
return FPM_PHP_INI_APPLIED;
10879
}
10980

110-
if (!strcmp(name, "disable_classes") && *value) {
111-
char *v = strdup(value);
112-
PG(disable_classes) = v;
113-
fpm_php_disable(v, zend_disable_class);
114-
return FPM_PHP_INI_APPLIED;
115-
}
116-
11781
return FPM_PHP_INI_APPLIED;
11882
}
11983
/* }}} */

0 commit comments

Comments
 (0)