From eba954db2b495de517c66de5e008d7bf5f3619ad Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Tue, 5 May 2026 01:12:30 +0800 Subject: [PATCH 1/3] ext/phar: Readability Improvements in phar_fancy_stat() (#21865) --- ext/phar/func_interceptors.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index e2b45ac2a0ae..8f5d1ab068f1 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -323,10 +323,6 @@ static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value) zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev, stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks; int rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */ - char *stat_sb_names[13] = { - "dev", "ino", "mode", "nlink", "uid", "gid", "rdev", - "size", "atime", "mtime", "ctime", "blksize", "blocks" - }; if (type >= FS_IS_W && type <= FS_IS_X) { if(stat_sb->st_uid==getuid()) { @@ -443,19 +439,19 @@ static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value) zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks); /* Store string indexes referencing the same zval*/ - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("dev"), &stat_dev); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("ino"), &stat_ino); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("mode"), &stat_mode); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("nlink"), &stat_nlink); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("uid"), &stat_uid); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("gid"), &stat_gid); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("rdev"), &stat_rdev); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("size"), &stat_size); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("atime"), &stat_atime); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("mtime"), &stat_mtime); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("ctime"), &stat_ctime); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("blksize"), &stat_blksize); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("blocks"), &stat_blocks); return; } From 2f4a214e5dde0a58b0bd12ffe0b8c66a0d47db96 Mon Sep 17 00:00:00 2001 From: Weilin Du <108666168+LamentXU123@users.noreply.github.com> Date: Tue, 5 May 2026 01:19:07 +0800 Subject: [PATCH 2/3] ext/standard: Reject null bytes in parse_str() (#21942) --- NEWS | 2 ++ UPGRADING | 2 ++ ext/standard/string.c | 2 +- .../tests/strings/parse_str_null_bytes.phpt | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/strings/parse_str_null_bytes.phpt diff --git a/NEWS b/NEWS index 34fbfc5bb385..fe70343e8398 100644 --- a/NEWS +++ b/NEWS @@ -191,6 +191,8 @@ PHP NEWS (Weilin Du) . getenv() and putenv() now raises a ValueError when the first argument contains null bytes. (Weilin Du) + . parse_str() now raises a ValueError when the $string argument contains + null bytes. (Weilin Du) . proc_open() now raises a ValueError when the $cwd argument contains null bytes. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 3540aee482d9..fe44036383d0 100644 --- a/UPGRADING +++ b/UPGRADING @@ -99,6 +99,8 @@ PHP 8.6 UPGRADE NOTES argument value is passed. . getenv() and putenv() now raises a ValueError when the first argument contains null bytes. + . parse_str() now raises a ValueError when the $string argument contains + null bytes. . linkinfo() now raises a ValueError when the $path argument is empty. . pathinfo() now raises a ValueError when an invalid $flag argument value is passed. diff --git a/ext/standard/string.c b/ext/standard/string.c index ef9e66ab53f8..89b4e51e6c2c 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -5012,7 +5012,7 @@ PHP_FUNCTION(parse_str) size_t arglen; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(arg, arglen) + Z_PARAM_PATH(arg, arglen) Z_PARAM_ZVAL(arrayArg) ZEND_PARSE_PARAMETERS_END(); diff --git a/ext/standard/tests/strings/parse_str_null_bytes.phpt b/ext/standard/tests/strings/parse_str_null_bytes.phpt new file mode 100644 index 000000000000..fd0d94bb0fc8 --- /dev/null +++ b/ext/standard/tests/strings/parse_str_null_bytes.phpt @@ -0,0 +1,14 @@ +--TEST-- +parse_str() rejects null bytes +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +parse_str(): Argument #1 ($string) must not contain any null bytes From 6e871075758de4016e72913fbcef226ab6ea7ef1 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 2 May 2026 05:35:29 +0100 Subject: [PATCH 3/3] ext/mysqlnd: Fix persistent free of non-persistent connect_attr key. set_client_option_2d() built the temporary key string with the connection's persistent flag but always released it with persistent=1. On a duplicate-key update of the connect_attr hash, zend_hash_update() does not retain the passed key, so the caller-owned non-persistent string was freed via free() instead of efree(), tripping the IS_STR_PERSISTENT assertion in debug builds and mismatching allocators in release. Reachable by retrying mysqli_real_connect() on a handle whose first connect failed, since mysqlnd re-adds _client_name and _server_host on every connect attempt. close GH-21931 --- NEWS | 3 +++ .../tests/mysqli_real_connect_retry_attr.phpt | 26 +++++++++++++++++++ ext/mysqlnd/mysqlnd_connection.c | 9 +++---- 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt diff --git a/NEWS b/NEWS index 073a6a9acd44..eb0336f62933 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.22 +- MySQLnd: + . Fix persistent free of non-persistent connect_attr key (David Carlier). + - Opcache: . Fixed tracing JIT crash when a VM interrupt is handled during an observed user function call. (Levi Morrison) diff --git a/ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt b/ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt new file mode 100644 index 000000000000..7f7731807428 --- /dev/null +++ b/ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt @@ -0,0 +1,26 @@ +--TEST-- +mysqli_real_connect() retry on same handle does not corrupt mysqlnd connect_attr +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- + +--EXPECT-- +done! diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index 2f09ac5ee837..2402ad6fc8f4 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -1572,17 +1572,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * cons zval attrz; zend_string *str; + str = zend_string_init(key, strlen(key), conn->persistent); + ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), conn->persistent)); if (conn->persistent) { - str = zend_string_init(key, strlen(key), 1); GC_MAKE_PERSISTENT_LOCAL(str); - ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), 1)); GC_MAKE_PERSISTENT_LOCAL(Z_COUNTED(attrz)); - } else { - str = zend_string_init(key, strlen(key), 0); - ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), 0)); } zend_hash_update(conn->options->connect_attr, str, &attrz); - zend_string_release_ex(str, 1); + zend_string_release_ex(str, conn->persistent); } break; default: