diff --git a/NEWS b/NEWS index aa6f7f81a89d..95a0ab97ee1f 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ PHP NEWS . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN carries no credentials). (iliaal) +- PHPDBG: + . Fixed fleaked lowercased lookup keys in phpdbg_resolve_opline_break. + (jorgsowa) + - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between PHP 8.3 and PHP 8.5). (jorgsowa) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index d10b4d83fc3e..0173d2ef47a2 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -104,9 +104,7 @@ zend_result zend_optimizer_eval_special_func_call( zval *result, const zend_string *name, zend_string *arg) { if (zend_string_equals_literal(name, "function_exists") || zend_string_equals_literal(name, "is_callable")) { - zend_string *lc_name = zend_string_tolower(arg); - const zend_internal_function *func = zend_hash_find_ptr(EG(function_table), lc_name); - zend_string_release_ex(lc_name, 0); + const zend_internal_function *func = zend_hash_find_ptr_lc(EG(function_table), arg); if (func && func->type == ZEND_INTERNAL_FUNCTION && func->module->type == MODULE_PERSISTENT @@ -120,9 +118,7 @@ zend_result zend_optimizer_eval_special_func_call( return FAILURE; } if (zend_string_equals_literal(name, "extension_loaded")) { - zend_string *lc_name = zend_string_tolower(arg); - zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name); - zend_string_release_ex(lc_name, 0); + zend_module_entry *m = zend_hash_find_ptr_lc(&module_registry, arg); if (!m) { if (PG(enable_dl)) { diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 2dceac2512db..87acf073a2da 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -953,7 +953,6 @@ ZEND_FUNCTION(method_exists) { zval *klass; zend_string *method_name; - zend_string *lcname; zend_class_entry *ce; zend_function *func; @@ -974,9 +973,7 @@ ZEND_FUNCTION(method_exists) RETURN_THROWS(); } - lcname = zend_string_tolower(method_name); - func = zend_hash_find_ptr(&ce->function_table, lcname); - zend_string_release_ex(lcname, 0); + func = zend_hash_find_ptr_lc(&ce->function_table, method_name); if (func) { /* Exclude shadow properties when checking a method on a specific class. Include @@ -2219,7 +2216,6 @@ ZEND_FUNCTION(extension_loaded) ZEND_FUNCTION(get_extension_funcs) { zend_string *extension_name; - zend_string *lcname; bool array; zend_module_entry *module; zend_function *zif; @@ -2228,9 +2224,7 @@ ZEND_FUNCTION(get_extension_funcs) RETURN_THROWS(); } if (strncasecmp(ZSTR_VAL(extension_name), "zend", sizeof("zend"))) { - lcname = zend_string_tolower(extension_name); - module = zend_hash_find_ptr(&module_registry, lcname); - zend_string_release_ex(lcname, 0); + module = zend_hash_find_ptr_lc(&module_registry, extension_name); } else { module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core") - 1); } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 8df6a5599d30..848b9d209b2d 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -264,11 +264,7 @@ static zend_class_entry *lookup_class_ex( bool in_preload = CG(compiler_options) & ZEND_COMPILE_PRELOAD; if (UNEXPECTED(!EG(active) && !in_preload)) { - zend_string *lc_name = zend_string_tolower(name); - - ce = zend_hash_find_ptr(CG(class_table), lc_name); - - zend_string_release(lc_name); + ce = zend_hash_find_ptr_lc(CG(class_table), name); if (register_unresolved && !ce) { zend_error_noreturn( @@ -2530,7 +2526,6 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e size_t i, j = 0; zend_trait_precedence *cur_precedence; zend_trait_method_reference *cur_method_ref; - zend_string *lc_trait_name; zend_string *lcname; HashTable **exclude_tables = NULL; zend_class_entry **aliases = NULL; @@ -2545,9 +2540,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e while ((cur_precedence = precedences[i])) { /** Resolve classes for all precedence operations. */ cur_method_ref = &cur_precedence->trait_method; - lc_trait_name = zend_string_tolower(cur_method_ref->class_name); - trait = zend_hash_find_ptr(EG(class_table), lc_trait_name); - zend_string_release_ex(lc_trait_name, 0); + trait = zend_hash_find_ptr_lc(EG(class_table), cur_method_ref->class_name); if (!trait || !(trait->ce_flags & ZEND_ACC_LINKED)) { zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name)); } @@ -2574,9 +2567,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e zend_class_entry *exclude_ce; uint32_t trait_num; - lc_trait_name = zend_string_tolower(class_name); - exclude_ce = zend_hash_find_ptr(EG(class_table), lc_trait_name); - zend_string_release_ex(lc_trait_name, 0); + exclude_ce = zend_hash_find_ptr_lc(EG(class_table), class_name); if (!exclude_ce || !(exclude_ce->ce_flags & ZEND_ACC_LINKED)) { zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(class_name)); } @@ -2619,9 +2610,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e lcname = zend_string_tolower(cur_method_ref->method_name); if (cur_method_ref->class_name) { /* For all aliases with an explicit class name, resolve the class now. */ - lc_trait_name = zend_string_tolower(cur_method_ref->class_name); - trait = zend_hash_find_ptr(EG(class_table), lc_trait_name); - zend_string_release_ex(lc_trait_name, 0); + trait = zend_hash_find_ptr_lc(EG(class_table), cur_method_ref->class_name); if (!trait || !(trait->ce_flags & ZEND_ACC_LINKED)) { zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name)); } diff --git a/Zend/zend_perf_stat.h b/Zend/zend_perf_stat.h new file mode 100644 index 000000000000..447315de0133 --- /dev/null +++ b/Zend/zend_perf_stat.h @@ -0,0 +1,135 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Authors: Ilija Tovilo | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_PERF_STAT_H +#define ZEND_PERF_STAT_H + +#include "zend_portability.h" + +# if !(HAVE_FCNTL_H && HAVE_SYS_SELECT_H && HAVE_SYS_STAT_H && HAVE_SYS_TIME_H && HAVE_UNISTD_H) +static void zend_perf_stat_enable(void) {} +static void zend_perf_stat_disable(void) {} +# else + +# include +# include +# include +# include + +# include "Zend/zend.h" + +# define ZPS_CTL_FIFO_ENV "PERF_STAT_CTL_FIFO" +# define ZPS_ACK_FIFO_ENV "PERF_STAT_ACK_FIFO" + +static int ctl_fd = -2; +static int ack_fd = -2; + +static int zps_open_fifo(const char *env_name, int flags) +{ + const char *path = getenv(env_name); + + if (path == NULL || path[0] == '\0') { + return -1; + } + +# ifdef O_CLOEXEC + flags |= O_CLOEXEC; +# endif + int fd = open(path, flags | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "Failed to open fifo %s\n", path); + fflush(stderr); + zend_bailout(); + } + + struct stat st; + if (fstat(fd, &st) != 0 || !S_ISFIFO(st.st_mode)) { + close(fd); + fprintf(stderr, "File %s is not a fifo\n", path); + fflush(stderr); + zend_bailout(); + } + return fd; +} + +static void zps_init(void) +{ + if (ctl_fd == -2) { + ctl_fd = zps_open_fifo(ZPS_CTL_FIFO_ENV, O_WRONLY); + } + if (ack_fd == -2) { + ack_fd = zps_open_fifo(ZPS_ACK_FIFO_ENV, O_RDONLY); + } +} + +static void zps_wait_ack(void) +{ + if (ack_fd < 0) { + return; + } + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(ack_fd, &readfds); + if (select(ack_fd + 1, &readfds, NULL, NULL, &timeout) <= 0) { + return; + } + + char ack[sizeof("ack\n") - 1]; + ssize_t bytes_read; + do { + bytes_read = read(ack_fd, ack, sizeof(ack)); + } while (bytes_read > 0); +} + +static void zps_control(const char *command, size_t command_len) +{ + zps_init(); + + if (ctl_fd < 0) { + return; + } + + if (write(ctl_fd, command, command_len) != (ssize_t) command_len) { + close(ctl_fd); + ctl_fd = -1; + return; + } + + zps_wait_ack(); +} + +static void zend_perf_stat_enable(void) +{ + static const char command[] = "enable\n"; + + zps_control(command, sizeof(command) - 1); +} + +static void zend_perf_stat_disable(void) +{ + static const char command[] = "disable\n"; + + zps_control(command, sizeof(command) - 1); +} + +# endif +#endif diff --git a/ext/hash/hash.c b/ext/hash/hash.c index e2af98c81ddf..74abf02acd81 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -102,9 +102,7 @@ static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = { PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(zend_string *algo) /* {{{ */ { - zend_string *lower = zend_string_tolower(algo); - const php_hash_ops *ops = zend_hash_find_ptr(&php_hash_hashtable, lower); - zend_string_release(lower); + const php_hash_ops *ops = zend_hash_find_ptr_lc(&php_hash_hashtable, algo); return ops; } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 83e933b01181..6f606d9d37d5 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3847,9 +3847,7 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e memset(error, 0, sizeof(preload_error)); if (ce->parent_name) { - zend_string *key = zend_string_tolower(ce->parent_name); - const zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key); - zend_string_release(key); + const zend_class_entry *parent = zend_hash_find_ptr_lc(EG(class_table), ce->parent_name); if (!parent) { error->kind = "Unknown parent "; error->name = ZSTR_VAL(ce->parent_name); diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 6a47ec30c862..dcd9f7b126df 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1484,7 +1484,6 @@ static zend_function *dbh_method_get(zend_object **object, zend_string *method_n { zend_function *fbc = NULL; pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object); - zend_string *lc_method_name; if ((fbc = zend_std_get_method(object, method_name, key)) == NULL) { /* not a pre-defined method, nor a user-defined method; check @@ -1497,9 +1496,7 @@ static zend_function *dbh_method_get(zend_object **object, zend_string *method_n } } - lc_method_name = zend_string_tolower(method_name); - fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name); - zend_string_release_ex(lc_method_name, 0); + fbc = zend_hash_find_ptr_lc(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], method_name); } out: diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a9f6b579977e..37c45cb02168 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -859,7 +859,6 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze { smart_str param_indent = {0}; zend_function *overwrites; - zend_string *lc_name; /* TBD: Repair indenting of doc comment (or is this to be done in the parser?) * What's "wrong" is that any whitespace before the doc comment start is @@ -885,13 +884,11 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze if (fptr->common.scope != scope) { smart_str_append_printf(str, ", inherits %s", ZSTR_VAL(fptr->common.scope->name)); } else if (fptr->common.scope->parent) { - lc_name = zend_string_tolower(fptr->common.function_name); - if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parent->function_table, lc_name)) != NULL) { + if ((overwrites = zend_hash_find_ptr_lc(&fptr->common.scope->parent->function_table, fptr->common.function_name)) != NULL) { if (fptr->common.scope != overwrites->common.scope && !(overwrites->common.fn_flags & ZEND_ACC_PRIVATE)) { smart_str_append_printf(str, ", overwrites %s", ZSTR_VAL(overwrites->common.scope->name)); } } - zend_string_release_ex(lc_name, 0); } } if (fptr->common.prototype && fptr->common.prototype->common.scope) { diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index f49507827e2d..7efd8f159d59 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -95,6 +95,8 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS; # endif #endif +#include "zend_perf_stat.h" + #ifndef PHP_WIN32 /* XXX this will need to change later when threaded fastcgi is implemented. shane */ static struct sigaction act, old_term, old_quit, old_int; @@ -1733,6 +1735,7 @@ int main(int argc, char *argv[]) int warmup_repeats = 0; int repeats = 1; int benchmark = 0; + bool perf_enabled = false; #ifdef HAVE_GETTIMEOFDAY struct timeval start, end; #else @@ -2441,14 +2444,16 @@ consult the installation file that came with this distribution, or visit \n\ } } /* end !cgi && !fastcgi */ -#ifdef HAVE_VALGRIND if (warmup_repeats == 0) { + zend_perf_stat_enable(); + perf_enabled = true; +#ifdef HAVE_VALGRIND CALLGRIND_START_INSTRUMENTATION; # ifdef HAVE_VALGRIND_CACHEGRIND_H CACHEGRIND_START_INSTRUMENTATION; # endif - } #endif + } /* request startup only after we've done all we can to * get path_translated */ @@ -2568,6 +2573,10 @@ consult the installation file that came with this distribution, or visit \n\ SG(request_info).query_string = NULL; } + if (perf_enabled) { + zend_perf_stat_disable(); + perf_enabled = false; + } #ifdef HAVE_VALGRIND /* We're not interested in measuring shutdown */ CALLGRIND_STOP_INSTRUMENTATION; diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 9a05f8e68547..e5e573d1533c 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1093,10 +1093,9 @@ static int do_cli(int argc, char **argv) /* {{{ */ case PHP_CLI_MODE_REFLECTION_EXT_INFO: { size_t len = strlen(reflection_what); - char *lcname = zend_str_tolower_dup(reflection_what, len); zend_module_entry *module; - if ((module = zend_hash_str_find_ptr(&module_registry, lcname, len)) == NULL) { + if ((module = zend_hash_str_find_ptr_lc(&module_registry, reflection_what, len)) == NULL) { if (!strcmp(reflection_what, "main")) { display_ini_entries(NULL); } else { @@ -1107,7 +1106,6 @@ static int do_cli(int argc, char **argv) /* {{{ */ php_info_print_module(module); } - efree(lcname); break; } diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c index 4dfa89d4b0fe..f4e4ef81af2f 100644 --- a/sapi/phpdbg/phpdbg_bp.c +++ b/sapi/phpdbg/phpdbg_bp.c @@ -602,13 +602,13 @@ PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break) /* { if (new_break->class_name != NULL) { zend_class_entry *ce; - if (!(ce = zend_hash_str_find_ptr(EG(class_table), zend_str_tolower_dup(new_break->class_name, new_break->class_len), new_break->class_len))) { + if (!(ce = zend_hash_str_find_ptr_lc(EG(class_table), new_break->class_name, new_break->class_len))) { return FAILURE; } func_table = &ce->function_table; } - if (!(func = zend_hash_str_find_ptr(func_table, zend_str_tolower_dup(new_break->func_name, new_break->func_len), new_break->func_len))) { + if (!(func = zend_hash_str_find_ptr_lc(func_table, new_break->func_name, new_break->func_len))) { if (new_break->class_name != NULL && new_break->func_name != NULL) { phpdbg_error("Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name); return 2; @@ -966,13 +966,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function *f } if (ops->function_name) { - phpdbg_breakbase_t *brake; - zend_string *fname = zend_string_tolower(ops->function_name); - - brake = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], fname); - - zend_string_release(fname); - return brake; + return zend_hash_find_ptr_lc(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ops->function_name); } else { return zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ZEND_STRL("main")); } @@ -982,17 +976,11 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array *o { HashTable *class_table; phpdbg_breakbase_t *brake = NULL; - zend_string *class_lcname = zend_string_tolower(ops->scope->name); - - if ((class_table = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_lcname))) { - zend_string *lcname = zend_string_tolower(ops->function_name); - - brake = zend_hash_find_ptr(class_table, lcname); - zend_string_release(lcname); + if ((class_table = zend_hash_find_ptr_lc(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], ops->scope->name))) { + brake = zend_hash_find_ptr_lc(class_table, ops->function_name); } - zend_string_release(class_lcname); return brake; } /* }}} */ diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c index 505b04e81f91..3931fc9c1525 100644 --- a/sapi/phpdbg/phpdbg_list.c +++ b/sapi/phpdbg/phpdbg_list.c @@ -88,15 +88,12 @@ PHPDBG_LIST(method) /* {{{ */ if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) { zend_function *function; - char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); - if ((function = zend_hash_str_find_ptr(&ce->function_table, lcname, strlen(lcname)))) { + if ((function = zend_hash_str_find_ptr_lc(&ce->function_table, param->method.name, strlen(param->method.name)))) { phpdbg_list_function(function); } else { phpdbg_error("Could not find %s::%s", param->method.class, param->method.name); } - - efree(lcname); } else { phpdbg_error("Could not find the class %s", param->method.class); } diff --git a/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt b/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt new file mode 100644 index 000000000000..411697cee021 --- /dev/null +++ b/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt @@ -0,0 +1,26 @@ +--TEST-- +Resolving method/function opline breakpoints must not leak the lookup keys +--PHPDBG-- +b Foo::bar#0 +b baz#0 +b Foo::nope#0 +b Nope::bar#0 +q +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at Foo::bar#0] +prompt> [Breakpoint #1 added at baz#0] +prompt> [Method nope doesn't exist in class Foo] +prompt> [Pending breakpoint #3 at Nope::bar#0] +prompt> +--FILE-- +