From de98d82c6dd25eed9375244d75236f02365d64c2 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 12 Oct 2026 13:10:40 +0100 Subject: [PATCH] Zend: refactor zend_is_callable_check_class() Such that the computed lowercase class name can be passed to zend_lookup_class_ex() --- .../call_user_func_002.phpt | 6 +++++ Zend/zend_API.c | 25 ++++++++++++++++--- Zend/zend_execute_API.c | 2 ++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Zend/tests/call_user_functions/call_user_func_002.phpt b/Zend/tests/call_user_functions/call_user_func_002.phpt index 9d8f5ffaf344..3ce5e56ac53a 100644 --- a/Zend/tests/call_user_functions/call_user_func_002.phpt +++ b/Zend/tests/call_user_functions/call_user_func_002.phpt @@ -17,6 +17,11 @@ try { } catch (Throwable $e) { echo $e::class, ': ', $e->getMessage(), "\n"; } +try { + call_user_func(array('\\', 'bar')); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} try { call_user_func(array($foo, 'bar')); } catch (Throwable $e) { @@ -33,6 +38,7 @@ try { string(3) "foo" TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, class "foo" not found TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, class "" not found +TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, class "\" not found Warning: Undefined variable $foo in %s on line %d TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, first array member is not a valid class name or object diff --git a/Zend/zend_API.c b/Zend/zend_API.c index c3a0b4cfbafb..fb9b703abcbe 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3743,8 +3743,25 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc zend_string *lcname; ALLOCA_FLAG(use_heap); - ZSTR_ALLOCA_ALLOC(lcname, name_len, use_heap); - zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(name), name_len); + if (UNEXPECTED(ZSTR_LEN(name) == 0)) { + if (error) { + *error = estrdup("class \"\" not found"); + } + return false; + } + if (ZSTR_VAL(name)[0] == '\\') { + if (UNEXPECTED(ZSTR_LEN(name) == 1)) { + if (error) { + *error = estrdup("class \"\\\" not found"); + } + return false; + } + ZSTR_ALLOCA_ALLOC(lcname, name_len-1, use_heap); + zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(name)+1, name_len-1); + } else { + ZSTR_ALLOCA_ALLOC(lcname, name_len, use_heap); + zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(name), name_len); + } *strict_class = false; if (zend_string_equals(lcname, ZSTR_KNOWN(ZEND_STR_SELF))) { @@ -3801,7 +3818,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc *strict_class = true; ret = true; } - } else if ((ce = zend_lookup_class(name)) != NULL) { + } else if ((ce = zend_lookup_class_ex(name, lcname, 0)) != NULL) { const zend_class_entry *frame_scope = get_scope(frame); fcc->calling_scope = ce; if (frame_scope && !fcc->object) { @@ -3821,7 +3838,7 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc *strict_class = true; ret = true; } else { - if (error) zend_spprintf(error, 0, "class \"%.*s\" not found", (int)name_len, ZSTR_VAL(name)); + if (error) zend_spprintf(error, 0, "class \"%pS\" not found", name); } ZSTR_ALLOCA_FREE(lcname, use_heap); /* User error handlers may throw from deprecations above; do not report callable as valid. */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index beb3f9f995af..420ac5f9d1bb 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1242,6 +1242,8 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * } if (key) { + ZEND_ASSERT(ZSTR_LEN(name) != 0 && "Must not be an empty class name"); + ZEND_ASSERT((ZSTR_VAL(name)[0] != '\\' || ZSTR_LEN(name) > 1) && "Class name must not be \"\\\""); lc_name = key; } else { if (!ZSTR_LEN(name)) {