diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index e02e0afe2bfc..059c636bd9f1 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -736,7 +736,7 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ * RETURN_TRUE; } - const zend_class_entry *ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + const zend_class_entry *ce = zend_lookup_class_with_flags(class_name, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (!ce) { RETURN_FALSE; } @@ -1227,7 +1227,7 @@ ZEND_FUNCTION(class_alias) Z_PARAM_BOOL(autoload) ZEND_PARSE_PARAMETERS_END(); - ce = zend_lookup_class_ex(class_name, NULL, !autoload ? ZEND_FETCH_CLASS_NO_AUTOLOAD : 0); + ce = zend_lookup_class_with_flags(class_name, !autoload ? ZEND_FETCH_CLASS_NO_AUTOLOAD : 0); if (ce) { if (zend_register_class_alias_ex(ZSTR_VAL(alias_name), ZSTR_LEN(alias_name), ce, false) == SUCCESS) { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index dfd7497a6ed0..6a2e782676ba 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -10059,8 +10059,8 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top && !(CG(compiler_options) & ZEND_COMPILE_WITHOUT_EXECUTION)) { if (toplevel) { if (extends_ast) { - zend_class_entry *parent_ce = zend_lookup_class_ex( - ce->parent_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + zend_class_entry *parent_ce = zend_lookup_class_with_flags( + ce->parent_name, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (parent_ce && !zend_compile_ignore_class(parent_ce, ce->info.user.filename)) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e7923d76996c..bd3a7b3e0408 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -992,7 +992,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons } else if (zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_PARENT))) { return self_ce->parent; } else { - return zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + return zend_lookup_class_with_flags(name, ZEND_FETCH_CLASS_NO_AUTOLOAD); } } @@ -1003,7 +1003,7 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type( if (ZSTR_HAS_CE_CACHE(name)) { zend_class_entry *ce = ZSTR_GET_CE_CACHE(name); if (!ce) { - ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + ce = zend_lookup_class_with_flags(name, ZEND_FETCH_CLASS_NO_AUTOLOAD); } return ce; } @@ -1142,7 +1142,7 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_type( if (ZSTR_HAS_CE_CACHE(name)) { ce = ZSTR_GET_CE_CACHE(name); if (!ce) { - ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + ce = zend_lookup_class_with_flags(name, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (UNEXPECTED(!ce)) { /* Cannot resolve */ return NULL; @@ -5146,7 +5146,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s lcname = zend_string_init(ZSTR_VAL(function), cname_length, 0); - called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + called_scope = zend_fetch_class(lcname, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(called_scope == NULL)) { zend_string_release_ex(lcname, 0); return NULL; @@ -5270,7 +5270,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(const z } if (Z_TYPE_P(obj) == IS_STRING) { - zend_class_entry *called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + zend_class_entry *called_scope = zend_fetch_class(Z_STR_P(obj), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(called_scope == NULL)) { return NULL; diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 78f33e2b5bd8..884bc925586f 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -47,9 +47,16 @@ ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_ ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value); ZEND_API void execute_ex(zend_execute_data *execute_data); ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value); + ZEND_API bool zend_is_valid_class_name(const zend_string *name); -ZEND_API zend_class_entry *zend_lookup_class(zend_string *name); -ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags); +ZEND_API zend_class_entry *zend_lookup_class_with_flags(zend_string *name, uint32_t flags); +ZEND_ATTRIBUTE_NONNULL ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lc_name, uint32_t flags); + +static zend_always_inline zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ +{ + return zend_lookup_class_with_flags(name, 0); +} + ZEND_API zend_class_entry *zend_get_called_scope(const zend_execute_data *ex); ZEND_API zend_object *zend_get_this_object(const zend_execute_data *ex); ZEND_API zend_result zend_eval_string(const char *str, zval *retval_ptr, const char *string_name); @@ -529,7 +536,7 @@ ZEND_API void zend_unset_timeout(void); ZEND_NORETURN ZEND_API void ZEND_FASTCALL zend_timeout(void); ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type); ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, uint32_t fetch_type, zend_class_entry *scope); -ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type); +ZEND_ATTRIBUTE_NONNULL ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, size_t len); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index beb3f9f995af..d986a3c47cbf 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1220,19 +1220,22 @@ ZEND_API bool zend_is_valid_class_name(const zend_string *name) { for (size_t i = 0; i < ZSTR_LEN(name); i++) { unsigned char c = ZSTR_VAL(name)[i]; if (!ZEND_BIT_TEST(valid_chars, c)) { - return 0; + return false; } } - return 1; + + /* An empty string is *not* a valid class name */ + return ZSTR_LEN(name); } -ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */ +ZEND_ATTRIBUTE_NONNULL ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lc_name, uint32_t flags) /* {{{ */ { zend_class_entry *ce = NULL; - zend_string *lc_name; zend_string *autoload_name; uint32_t ce_cache = 0; + ZEND_ASSERT(ZSTR_VAL(lc_name)[0] != '\\' && "lowercase name must not start with \\"); + if (ZSTR_HAS_CE_CACHE(name) && ZSTR_VALID_CE_CACHE(name)) { ce_cache = GC_REFCOUNT(name); ce = GET_CE_CACHE(ce_cache); @@ -1241,30 +1244,10 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * } } - if (key) { - lc_name = key; - } else { - if (!ZSTR_LEN(name)) { - return NULL; - } - - if (ZSTR_VAL(name)[0] == '\\') { - if (ZSTR_LEN(name) == 1) { - /* A lone namespace separator names no class, e.g. is_callable('\::method'). */ - return NULL; - } - lc_name = zend_string_alloc(ZSTR_LEN(name) - 1, 0); - zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1); - } else { - lc_name = zend_string_tolower(name); - } - } - + /* Note: we do not validate the class name because of anonymous classes + * having a name that is not compatible by the rules of zend_is_valid_class_name() */ ce = zend_hash_find_ptr(EG(class_table), lc_name); if (ce) { - if (!key) { - zend_string_release_ex(lc_name, 0); - } if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) { if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && @@ -1287,31 +1270,21 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * return ce; } - /* The compiler is not-reentrant. Make sure we autoload only during run-time. */ - if ((flags & ZEND_FETCH_CLASS_NO_AUTOLOAD) || zend_is_compiling()) { - if (!key) { - zend_string_release_ex(lc_name, 0); - } + if (UNEXPECTED(!zend_is_valid_class_name(lc_name))) { return NULL; } + ZEND_ASSERT(zend_is_valid_class_name(name) && "If lowercase name is valid the name must also be so"); - if (!zend_autoload) { - if (!key) { - zend_string_release_ex(lc_name, 0); - } + /* The compiler is not-reentrant. Make sure we autoload only during run-time. */ + if ((flags & ZEND_FETCH_CLASS_NO_AUTOLOAD) || zend_is_compiling()) { return NULL; } - /* Verify class name before passing it to the autoloader. */ - if (!key && !ZSTR_HAS_CE_CACHE(name) && !zend_is_valid_class_name(name)) { - zend_string_release_ex(lc_name, 0); + if (!zend_autoload) { return NULL; } if (zend_hash_add_empty_element(&EG(autoload_current_classnames), lc_name) == NULL) { - if (!key) { - zend_string_release_ex(lc_name, 0); - } return NULL; } @@ -1332,9 +1305,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_string_release_ex(autoload_name, 0); zend_hash_del(&EG(autoload_current_classnames), lc_name); - if (!key) { - zend_string_release_ex(lc_name, 0); - } if (ce) { ZEND_ASSERT(!CG(in_compilation)); if (ce_cache) { @@ -1345,11 +1315,19 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * } /* }}} */ -ZEND_API zend_class_entry *zend_lookup_class(zend_string *name) /* {{{ */ +ZEND_API zend_class_entry *zend_lookup_class_with_flags(zend_string *name, uint32_t flags) { - return zend_lookup_class_ex(name, NULL, 0); + zend_string *lc_name; + if (ZSTR_VAL(name)[0] == '\\') { + lc_name = zend_string_alloc(ZSTR_LEN(name) - 1, false); + zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1); + } else { + lc_name = zend_string_tolower(name); + } + zend_class_entry *ce = zend_lookup_class_ex(name, lc_name, flags); + zend_string_release_ex(lc_name, false); + return ce; } -/* }}} */ ZEND_API zend_class_entry *zend_get_called_scope(const zend_execute_data *ex) /* {{{ */ { @@ -1800,7 +1778,7 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type) break; } - ce = zend_lookup_class_ex(class_name, NULL, fetch_type); + ce = zend_lookup_class_with_flags(class_name, fetch_type); if (!ce) { report_class_fetch_error(class_name, fetch_type); return NULL; @@ -1834,7 +1812,7 @@ zend_class_entry *zend_fetch_class_with_scope( default: ZEND_UNREACHABLE(); } - ce = zend_lookup_class_ex(class_name, NULL, fetch_type); + ce = zend_lookup_class_with_flags(class_name, fetch_type); if (!ce) { report_class_fetch_error(class_name, fetch_type); return NULL; @@ -1842,9 +1820,9 @@ zend_class_entry *zend_fetch_class_with_scope( return ce; } -zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *key, uint32_t fetch_type) /* {{{ */ +ZEND_ATTRIBUTE_NONNULL ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type) /* {{{ */ { - zend_class_entry *ce = zend_lookup_class_ex(class_name, key, fetch_type); + zend_class_entry *ce = zend_lookup_class_ex(class_name, lcname, fetch_type); if (!ce) { report_class_fetch_error(class_name, fetch_type); return NULL; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 375acc45d6a9..e324f0b430b7 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -275,8 +275,8 @@ static zend_class_entry *lookup_class_ex( return ce; } - ce = zend_lookup_class_ex( - name, NULL, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD); + ce = zend_lookup_class_with_flags( + name, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD); if (!CG(in_compilation) || in_preload) { if (ce) { @@ -319,7 +319,7 @@ static bool unlinked_instanceof(const zend_class_entry *ce1, const zend_class_en if (ce1->ce_flags & ZEND_ACC_RESOLVED_PARENT) { parent_ce = ce1->parent; } else { - parent_ce = zend_lookup_class_ex(ce1->parent_name, NULL, + parent_ce = zend_lookup_class_with_flags(ce1->parent_name, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD); } @@ -3555,9 +3555,11 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); if (ce->parent_name) { + zend_string *lc_name = lc_parent_name ? zend_string_copy(lc_parent_name) : zend_string_tolower(ce->parent_name); parent = zend_fetch_class_by_name( - ce->parent_name, lc_parent_name, + ce->parent_name, lc_name, ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION); + zend_string_release(lc_name); if (!parent) { check_unrecoverable_load_failure(ce); return NULL; diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 4e4c815377ac..2d043232d25d 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2512,7 +2512,7 @@ static zend_always_inline zend_inheritance_cache_entry* zend_accel_inheritance_c } if (found && entry->dependencies) { for (i = 0; i < entry->dependencies_count; i++) { - const zend_class_entry *dependency_ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + const zend_class_entry *dependency_ce = zend_lookup_class_with_flags(entry->dependencies[i].name, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (dependency_ce != entry->dependencies[i].ce) { if (!dependency_ce) { @@ -2560,7 +2560,7 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, } for (uint32_t i = 0; i < entry->dependencies_count; i++) { - const zend_class_entry *dependency_ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, 0); + const zend_class_entry *dependency_ce = zend_lookup_class(entry->dependencies[i].name); if (dependency_ce == NULL) { return NULL; diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index d6ce931fb974..5e08fd84e778 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -575,7 +575,7 @@ static zend_class_entry* zend_get_known_class(const zend_op_array *op_array, con ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); class_name = Z_STR_P(zv); - ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + ce = zend_lookup_class_with_flags(class_name, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { ce = NULL; } diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 0c5f1ba99c19..a51782fb928f 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -40,7 +40,7 @@ ZEND_TLS zend_string *spl_autoload_extensions; static zend_class_entry * spl_find_ce_by_name(zend_string *name, bool autoload) { - zend_class_entry *ce = zend_lookup_class_ex(name, NULL, autoload ? 0 : ZEND_FETCH_CLASS_NO_AUTOLOAD); + zend_class_entry *ce = zend_lookup_class_with_flags(name, autoload ? 0 : ZEND_FETCH_CLASS_NO_AUTOLOAD); if (ce == NULL) { php_error_docref(NULL, E_WARNING, "Class %s does not exist%s", ZSTR_VAL(name), autoload ? " and could not be loaded" : ""); return NULL; diff --git a/ext/standard/tests/serialize/unserialize_allowed_classes_option_invalid_class_names.phpt b/ext/standard/tests/serialize/unserialize_allowed_classes_option_invalid_class_names.phpt index 74997939817d..827ce20085ce 100644 --- a/ext/standard/tests/serialize/unserialize_allowed_classes_option_invalid_class_names.phpt +++ b/ext/standard/tests/serialize/unserialize_allowed_classes_option_invalid_class_names.phpt @@ -41,6 +41,7 @@ try { ?> --EXPECT-- +ValueError: unserialize(): Option "allowed_classes" must be an array of class names, "" given ValueError: unserialize(): Option "allowed_classes" must be an array of class names, " whitespace " given ValueError: unserialize(): Option "allowed_classes" must be an array of class names, "name with whitespace" given