From 21c2c07a249ff03d0d1dff3b06545dbd3322e86f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:06:21 +0200 Subject: [PATCH 1/5] Fix GH-19998: ext/standard/tests/file/bug46347.phpt sometimes fails: racy in parallel The same test file name is already used in parse_ini_file.phpt. Closes GH-20000. --- ext/standard/tests/file/bug46347.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/file/bug46347.phpt b/ext/standard/tests/file/bug46347.phpt index 903a6e35cc2b2..e337ade9eb8fb 100644 --- a/ext/standard/tests/file/bug46347.phpt +++ b/ext/standard/tests/file/bug46347.phpt @@ -8,14 +8,14 @@ $str = <<< EOF part1.*.part2 = 1 EOF; -$file = __DIR__ . '/parse.ini'; +$file = __DIR__ . '/bug46347.ini'; file_put_contents($file, $str); var_dump(parse_ini_file($file)); ?> --CLEAN-- --EXPECT-- array(1) { From a380dcae4dfe72079d9a019c48daf3e8dc3aa917 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:37:19 +0200 Subject: [PATCH 2/5] Fix GH-20006: Power of 0 of BcMath number causes UB Closes GH-20007. --- NEWS | 3 +++ ext/bcmath/libbcmath/src/raise.c | 7 ++++++- ext/bcmath/tests/gh20006.phpt | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 ext/bcmath/tests/gh20006.phpt diff --git a/NEWS b/NEWS index 4c77067840165..740755ecd407c 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ PHP NEWS - Core: . Fix OSS-Fuzz #447521098 (Fatal error during sccp shift eval). (ilutov) +- BcMath: + . Fixed bug GH-20006 (Power of 0 of BcMath number causes UB). (nielsdos) + - Opcache: . Fixed segfault in function JIT due to NAN to bool warning. (Girgias) diff --git a/ext/bcmath/libbcmath/src/raise.c b/ext/bcmath/libbcmath/src/raise.c index 5df8130c24219..959ba924e57fd 100644 --- a/ext/bcmath/libbcmath/src/raise.c +++ b/ext/bcmath/libbcmath/src/raise.c @@ -193,7 +193,12 @@ bc_raise_status bc_raise(bc_num base, long exponent, bc_num *result, size_t scal if (bc_is_zero(base)) { /* If the exponent is negative, it divides by 0 */ - return is_neg ? BC_RAISE_STATUS_DIVIDE_BY_ZERO : BC_RAISE_STATUS_OK; + if (is_neg) { + return BC_RAISE_STATUS_DIVIDE_BY_ZERO; + } + bc_free_num (result); + *result = bc_copy_num(BCG(_zero_)); + return BC_RAISE_STATUS_OK; } /* check overflow */ diff --git a/ext/bcmath/tests/gh20006.phpt b/ext/bcmath/tests/gh20006.phpt new file mode 100644 index 0000000000000..ab0db915152d3 --- /dev/null +++ b/ext/bcmath/tests/gh20006.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-20006 (Power of 0 of BcMath number causes crash) +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECTF-- +object(BcMath\Number)#%d (2) { + ["value"]=> + string(1) "0" + ["scale"]=> + int(0) +} From 6db12e7cd89fa4643f42a884e8323a3aef1b52cc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:08:10 +0200 Subject: [PATCH 3/5] Fix bug #67563: mysqli compiled with mysqlnd does not take ipv6 adress as parameter In the past, when libmysqlclient could be used, it accepted ipv6 addresses as hostname without enclosing it first in brackets. However, in mysqlnd this never worked. In the past this caused a discrepancy between the two implementations. Nowadays, mysqli only works with mysqlnd so we don't even have to cater to libmysqlclient. However, a plain ipv6 address should still work as a hostname. Also for people migrating to newer PHP versions it's nice if this keeps working. The solution is to check if we're dealing with an ipv6 address not yet enclosed in brackets. In that case we add the brackets automatically. Closes GH-19750. --- NEWS | 4 ++++ ext/mysqli/tests/bug67563.phpt | 40 ++++++++++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_connection.c | 18 +++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ext/mysqli/tests/bug67563.phpt diff --git a/NEWS b/NEWS index 58d9ebc69ad07..067cceab3be13 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,10 @@ PHP NEWS - GD: . FIxed GH-19955 (imagefttext() memory leak). (David Carlier) +- MySQLnd: + . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress + as parameter). (nielsdos) + - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) diff --git a/ext/mysqli/tests/bug67563.phpt b/ext/mysqli/tests/bug67563.phpt new file mode 100644 index 0000000000000..72a51ecdccd2c --- /dev/null +++ b/ext/mysqli/tests/bug67563.phpt @@ -0,0 +1,40 @@ +--TEST-- +Fix bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress as parameter) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--INI-- +max_execution_time=240 +--FILE-- +close(); + } +} + +print "done!"; +?> +--EXPECT-- +done! diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index d46029889fad0..a97f2820a3190 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -513,6 +513,16 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, } /* }}} */ +/* ipv6 addresses have at least two colons, which is how we can differentiate between domain names and addresses */ +static bool mysqlnd_fast_is_ipv6_address(const char *s) +{ + const char *first_colon = strchr(s, ':'); + if (!first_colon) { + return false; + } + return strchr(first_colon + 1, ':') != NULL; +} + /* {{{ mysqlnd_conn_data::get_scheme */ static MYSQLND_STRING MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, bool * unix_socket, bool * named_pipe) @@ -542,7 +552,13 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ if (!port) { port = 3306; } - transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + + /* ipv6 addresses are in the format [address]:port */ + if (hostname.s[0] != '[' && mysqlnd_fast_is_ipv6_address(hostname.s)) { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); + } else { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + } } DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM"); DBG_RETURN(transport); From d9ee711baf7ce799b3b1cd8d7c726aa62bbb878d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:21:47 +0200 Subject: [PATCH 4/5] Fix NEWS formatting --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 067cceab3be13..d1d7335c132e6 100644 --- a/NEWS +++ b/NEWS @@ -28,7 +28,7 @@ PHP NEWS . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) - GD: - . FIxed GH-19955 (imagefttext() memory leak). (David Carlier) + . Fixed GH-19955 (imagefttext() memory leak). (David Carlier) - MySQLnd: . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress From c7da72857474ae5ac01f809b789cf1189e53c985 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Tue, 30 Sep 2025 15:17:56 +0100 Subject: [PATCH 5/5] ext/filter: Check callback validity once (#19921) The call_user_function() API redoes the zend_is_callable() check, which has been just done. We can check validity and retrieve the FCC to call it directly rather than having a useless double check --- ext/filter/callback_filter.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ext/filter/callback_filter.c b/ext/filter/callback_filter.c index 719b66767980f..dea39c6cc7c30 100644 --- a/ext/filter/callback_filter.c +++ b/ext/filter/callback_filter.c @@ -19,22 +19,21 @@ zend_result php_filter_callback(PHP_INPUT_FILTER_PARAM_DECL) { zval retval; - int status; + zend_fcall_info_cache fcc; - if (!option_array || !zend_is_callable(option_array, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL)) { + if (!option_array || !zend_is_callable_ex(option_array, NULL, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, NULL)) { zend_type_error("%s(): Option must be a valid callback", get_active_function_name()); zval_ptr_dtor(value); ZVAL_NULL(value); return SUCCESS; } - status = call_user_function(NULL, NULL, option_array, &retval, 1, value); + zend_call_known_fcc(&fcc, &retval, 1, value, NULL); + zval_ptr_dtor(value); - if (status == SUCCESS && !Z_ISUNDEF(retval)) { - zval_ptr_dtor(value); + if (!Z_ISUNDEF(retval)) { ZVAL_COPY_VALUE(value, &retval); } else { - zval_ptr_dtor(value); ZVAL_NULL(value); } return SUCCESS;