Skip to content

Commit d161489

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 9eb032b commit d161489

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
@@ -3465,77 +3465,6 @@ ZEND_API void zend_disable_functions(const char *function_list) /* {{{ */
34653465
}
34663466
/* }}} */
34673467

3468-
#ifdef ZEND_WIN32
3469-
#pragma optimize("", off)
3470-
#endif
3471-
static ZEND_COLD zend_object *display_disabled_class(zend_class_entry *class_type) /* {{{ */
3472-
{
3473-
zend_object *intern;
3474-
3475-
intern = zend_objects_new(class_type);
3476-
3477-
/* Initialize default properties */
3478-
if (EXPECTED(class_type->default_properties_count != 0)) {
3479-
zval *p = intern->properties_table;
3480-
zval *end = p + class_type->default_properties_count;
3481-
do {
3482-
ZVAL_UNDEF(p);
3483-
p++;
3484-
} while (p != end);
3485-
}
3486-
3487-
zend_error(E_WARNING, "%s() has been disabled for security reasons", ZSTR_VAL(class_type->name));
3488-
return intern;
3489-
}
3490-
#ifdef ZEND_WIN32
3491-
#pragma optimize("", on)
3492-
#endif
3493-
/* }}} */
3494-
3495-
static const zend_function_entry disabled_class_new[] = {
3496-
ZEND_FE_END
3497-
};
3498-
3499-
ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length) /* {{{ */
3500-
{
3501-
zend_class_entry *disabled_class;
3502-
zend_string *key;
3503-
zend_function *fn;
3504-
zend_property_info *prop;
3505-
3506-
key = zend_string_alloc(class_name_length, 0);
3507-
zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length);
3508-
disabled_class = zend_hash_find_ptr(CG(class_table), key);
3509-
zend_string_release_ex(key, 0);
3510-
if (!disabled_class) {
3511-
return FAILURE;
3512-
}
3513-
3514-
/* Will be reset by INIT_CLASS_ENTRY. */
3515-
free(disabled_class->interfaces);
3516-
3517-
INIT_CLASS_ENTRY_INIT_METHODS((*disabled_class), disabled_class_new);
3518-
disabled_class->create_object = display_disabled_class;
3519-
3520-
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->function_table, fn) {
3521-
if ((fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
3522-
fn->common.scope == disabled_class) {
3523-
zend_free_internal_arg_info(&fn->internal_function);
3524-
}
3525-
} ZEND_HASH_FOREACH_END();
3526-
zend_hash_clean(&disabled_class->function_table);
3527-
ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->properties_info, prop) {
3528-
if (prop->ce == disabled_class) {
3529-
zend_string_release(prop->name);
3530-
zend_type_release(prop->type, /* persistent */ 1);
3531-
free(prop);
3532-
}
3533-
} ZEND_HASH_FOREACH_END();
3534-
zend_hash_clean(&disabled_class->properties_info);
3535-
return SUCCESS;
3536-
}
3537-
/* }}} */
3538-
35393468
static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame)
35403469
{
35413470
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
@@ -398,7 +398,6 @@ static zend_always_inline zend_result zend_register_class_alias(const char *name
398398
zend_register_class_alias_ex(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, ce, 1)
399399

400400
ZEND_API void zend_disable_functions(const char *function_list);
401-
ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length);
402401

403402
ZEND_API ZEND_COLD void zend_wrong_param_count(void);
404403
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

@@ -1955,9 +1932,6 @@ static void core_globals_dtor(php_core_globals *core_globals)
19551932
ZEND_ASSERT(!core_globals->last_error_message);
19561933
ZEND_ASSERT(!core_globals->last_error_file);
19571934

1958-
if (core_globals->disable_classes) {
1959-
free(core_globals->disable_classes);
1960-
}
19611935
if (core_globals->php_binary) {
19621936
free(core_globals->php_binary);
19631937
}
@@ -2223,9 +2197,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
22232197
}
22242198
}
22252199

2226-
/* disable certain classes and functions as requested by php.ini */
2200+
/* disable certain functions as requested by php.ini */
22272201
zend_disable_functions(INI_STR("disable_functions"));
2228-
php_disable_classes();
22292202

22302203
/* make core report what it should */
22312204
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
int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
8152
{
8253

@@ -100,13 +71,6 @@ int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
10071
return 1;
10172
}
10273

103-
if (!strcmp(name, "disable_classes") && *value) {
104-
char *v = strdup(value);
105-
PG(disable_classes) = v;
106-
fpm_php_disable(v, zend_disable_class);
107-
return 1;
108-
}
109-
11074
return 1;
11175
}
11276
/* }}} */

sapi/fpm/www.conf.in

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,9 +474,8 @@ pm.max_spare_servers = 3
474474
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
475475

476476
; Defining 'extension' will load the corresponding shared extension from
477-
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
478-
; overwrite previously defined php.ini values, but will append the new value
479-
; instead.
477+
; extension_dir. Defining 'disable_functions' will not overwrite previously
478+
; defined php.ini values, but will append the new value instead.
480479

481480
; Note: path INI options can be relative and will be expanded with the prefix
482481
; (pool, global or @prefix@)

0 commit comments

Comments
 (0)