From 76c40ecbe33a698899fbb016ad882aed5c093452 Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Mon, 6 Jul 2026 19:06:53 +0200 Subject: [PATCH 1/7] Fix incorrect method case for SplFileInfo::getPathname() in gen_stub.php (#22597) getPathName() was called instead of the correctly-cased getPathname(). --- build/gen_stub.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 1e1d53c5146c..715a47182340 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -84,7 +84,7 @@ function processDirectory(string $dir, Context $context): array { RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (str_ends_with($pathName, '.stub.php')) { $pathNames[] = $pathName; } @@ -5561,7 +5561,7 @@ function replacePredefinedConstants(string $targetDirectory, array $constMap, ar ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (!preg_match('/(?:[\w\.]*constants[\w\._]*|tokens).xml$/i', basename($pathName))) { continue; } @@ -5742,7 +5742,7 @@ function replaceClassSynopses( ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (!preg_match('/\.xml$/i', $pathName)) { continue; } @@ -5959,7 +5959,7 @@ function replaceMethodSynopses( ); foreach ($it as $file) { - $pathName = $file->getPathName(); + $pathName = $file->getPathname(); if (!preg_match('/\.xml$/i', $pathName)) { continue; } From 016a1f1bc9e06fc94e83f914a7f0bbb81b3cd4ae Mon Sep 17 00:00:00 2001 From: Jorg Adam Sowa Date: Mon, 6 Jul 2026 19:20:19 +0200 Subject: [PATCH 2/7] Zend: reuse zend_get_unqualified_name() in zend_add_const_name_literal() (#22598) Avoid duplicating the namespace-separator split logic. --- Zend/zend_compile.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c75333e6d3c5..d9fa3a1cb504 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -673,12 +673,10 @@ static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* { int ret = zend_add_literal_string(&name); - size_t ns_len = 0, after_ns_len = ZSTR_LEN(name); - const char *after_ns = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name)); - if (after_ns) { - after_ns += 1; - ns_len = after_ns - ZSTR_VAL(name) - 1; - after_ns_len = ZSTR_LEN(name) - ns_len - 1; + const char *after_ns; + size_t after_ns_len; + if (zend_get_unqualified_name(name, &after_ns, &after_ns_len)) { + size_t ns_len = after_ns - ZSTR_VAL(name) - 1; /* lowercased namespace name & original constant name */ tmp_name = zend_string_init(ZSTR_VAL(name), ZSTR_LEN(name), 0); @@ -690,6 +688,7 @@ static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* { } } else { after_ns = ZSTR_VAL(name); + after_ns_len = ZSTR_LEN(name); } /* original unqualified constant name */ From de6828f50a4823226934d881dba12d144400d12e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Tue, 7 Jul 2026 01:20:33 +0800 Subject: [PATCH 3/7] Remove several useless `include ` (#22611) None of these files have use the sys/param.h file. --- ext/random/random.c | 4 ---- ext/standard/crypt_sha256.c | 1 - ext/standard/crypt_sha512.c | 1 - 3 files changed, 6 deletions(-) diff --git a/ext/random/random.c b/ext/random/random.c index bd6f9f8a9018..e7fe2c21801e 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -45,10 +45,6 @@ # include #endif -#ifdef HAVE_SYS_PARAM_H -# include -#endif - #include "random_arginfo.h" PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random) diff --git a/ext/standard/crypt_sha256.c b/ext/standard/crypt_sha256.c index 2673dcf2b08e..3f3d9cdeb03c 100644 --- a/ext/standard/crypt_sha256.c +++ b/ext/standard/crypt_sha256.c @@ -23,7 +23,6 @@ #ifdef PHP_WIN32 # include #else -# include # include # include #endif diff --git a/ext/standard/crypt_sha512.c b/ext/standard/crypt_sha512.c index e8cedaa55c20..4a308e2f9af2 100644 --- a/ext/standard/crypt_sha512.c +++ b/ext/standard/crypt_sha512.c @@ -22,7 +22,6 @@ #ifdef PHP_WIN32 # include #else -# include # include # include #endif From 5ab3b338ac8b6d921a0cc595d7db31853874a423 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 6 Jul 2026 19:23:26 +0200 Subject: [PATCH 4/7] session: ensure expected INI directives are set (#22577) --- ext/session/tests/bug80774.phpt | 2 ++ ext/session/tests/gh9200.phpt | 2 ++ ext/session/tests/session_regenerate_id_cookie.phpt | 2 ++ ext/session/tests/session_start_partitioned_headers.phpt | 2 ++ 4 files changed, 8 insertions(+) diff --git a/ext/session/tests/bug80774.phpt b/ext/session/tests/bug80774.phpt index ba5d39e1953a..1e98da74e9d6 100644 --- a/ext/session/tests/bug80774.phpt +++ b/ext/session/tests/bug80774.phpt @@ -4,6 +4,8 @@ Bug #80774 (session_name() problem with backslash) session --INI-- session.use_strict_mode=0 +session.cookie_samesite=Lax +session.cookie_httponly=1 --FILE-- Date: Tue, 7 Jul 2026 02:28:19 +0800 Subject: [PATCH 5/7] ext/standard: Fix `sleep` and `usleep` unsigned integer overflow (#22619) sleep(4294967296) actually sleep 0 seconds because of a unsigned int overflow bug. This is also true in usleep. I target this to master as a fix that is classified as a stricter parsing. Now we throw ValueError instead. p.s. in Windows, the sleep function is implemented with SleepEx (t * 1000, TRUE) not php_sleep, phew, so we need to lower the threshold to UINT_MAX / 1000 in Windows --- NEWS | 4 +++ ext/standard/basic_functions.c | 13 +++++++--- .../tests/general_functions/sleep_error.phpt | 2 +- .../sleep_usleep_uint_overflow.phpt | 25 +++++++++++++++++++ .../tests/general_functions/usleep_error.phpt | 2 +- 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt diff --git a/NEWS b/NEWS index 77ea35389d5f..11b470654384 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,10 @@ PHP NEWS . Fixed bug GH-22585 (OOM on bailout with uninitialized do_request() parameters). (David Carlier) +- Standard: + . Fixed sleep() and usleep() to reject values that overflow the underlying + unsigned int timeout. (Weilin Du) + - Streams: . Fixed bug GH-21468 (Segfault in file_get_contents w/ a https URL and a proxy set). (CVE-2026-12184) (ndossche) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index f8b1cb5c4fdb..e81050323754 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1123,13 +1123,18 @@ PHP_FUNCTION(flush) PHP_FUNCTION(sleep) { zend_long num; +#ifdef PHP_WIN32 + const unsigned int max = UINT_MAX / 1000; +#else + const unsigned int max = UINT_MAX; +#endif ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(num) ZEND_PARSE_PARAMETERS_END(); - if (num < 0) { - zend_argument_value_error(1, "must be greater than or equal to 0"); + if (num < 0 || (zend_ulong) num > max) { + zend_argument_value_error(1, "must be between 0 and %u", max); RETURN_THROWS(); } @@ -1146,8 +1151,8 @@ PHP_FUNCTION(usleep) Z_PARAM_LONG(num) ZEND_PARSE_PARAMETERS_END(); - if (num < 0) { - zend_argument_value_error(1, "must be greater than or equal to 0"); + if (num < 0 || (zend_ulong) num > UINT_MAX) { + zend_argument_value_error(1, "must be between 0 and %u", UINT_MAX); RETURN_THROWS(); } diff --git a/ext/standard/tests/general_functions/sleep_error.phpt b/ext/standard/tests/general_functions/sleep_error.phpt index bfe66480fa03..47685fc3a5ae 100644 --- a/ext/standard/tests/general_functions/sleep_error.phpt +++ b/ext/standard/tests/general_functions/sleep_error.phpt @@ -7,7 +7,7 @@ sleep(-10); ?> --EXPECTF-- -Fatal error: Uncaught ValueError: sleep(): Argument #1 ($seconds) must be greater than or equal to 0 in %s:%d +Fatal error: Uncaught ValueError: sleep(): Argument #1 ($seconds) must be between 0 and %d in %s:%d Stack trace: #0 %s(%d): sleep(-10) #1 {main} diff --git a/ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt b/ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt new file mode 100644 index 000000000000..6f0a7b3fa9e8 --- /dev/null +++ b/ext/standard/tests/general_functions/sleep_usleep_uint_overflow.phpt @@ -0,0 +1,25 @@ +--TEST-- +sleep() and usleep() reject values above their unsigned int limits +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } +} +?> +--EXPECTF-- +int(0) +usleep(0) ok +sleep(): Argument #1 ($seconds) must be between 0 and %d +usleep(): Argument #1 ($microseconds) must be between 0 and 4294967295 diff --git a/ext/standard/tests/general_functions/usleep_error.phpt b/ext/standard/tests/general_functions/usleep_error.phpt index 46bd4420580b..9c3825f18b56 100644 --- a/ext/standard/tests/general_functions/usleep_error.phpt +++ b/ext/standard/tests/general_functions/usleep_error.phpt @@ -7,7 +7,7 @@ usleep(-10); ?> --EXPECTF-- -Fatal error: Uncaught ValueError: usleep(): Argument #1 ($microseconds) must be greater than or equal to 0 in %s:%d +Fatal error: Uncaught ValueError: usleep(): Argument #1 ($microseconds) must be between 0 and 4294967295 in %s:%d Stack trace: #0 %s(%d): usleep(-10) #1 {main} From f04eaec41ac88ea75e51592e91aa3f7c65939d21 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 6 Jul 2026 15:26:00 -0400 Subject: [PATCH 6/7] Fix GH-22549: GMP compound pow/shift assign asserts on negative operand (#22582) shift_operator_helper() wrote ZVAL_UNDEF(return_value) on the out-of-range exponent/shift path, but for a compound assignment return_value aliases op1, so pow_function's fallback then reached the scalar conversion with op1 set to IS_UNDEF and hit ZEND_UNREACHABLE(). Drop the stray undef; the helper's other error paths already return FAILURE without touching return_value, leaving the operand intact for the thrown ValueError to propagate. Fixes GH-22549 --- NEWS | 4 +++ ext/gmp/gmp.c | 1 - ext/gmp/tests/gh22549.phpt | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 ext/gmp/tests/gh22549.phpt diff --git a/NEWS b/NEWS index 11b470654384..2a266243a747 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size" warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman) +- GMP: + . Fixed bug GH-22549 (Assertion failure / UB on a compound GMP power or shift + assignment with a negative exponent). (iliaal) + - Intl: . Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to reject offset values outside the 32-bit range instead of silently diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index ef06d0fda30c..59209c4e1be3 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -361,7 +361,6 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val zend_ce_value_error, "%s must be between 0 and %lu", opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX ); - ZVAL_UNDEF(return_value); return FAILURE; } else { mpz_ptr gmpnum_op, gmpnum_result; diff --git a/ext/gmp/tests/gh22549.phpt b/ext/gmp/tests/gh22549.phpt new file mode 100644 index 000000000000..82a66901bd09 --- /dev/null +++ b/ext/gmp/tests/gh22549.phpt @@ -0,0 +1,55 @@ +--TEST-- +GH-22549 (Assertion failure in _zendi_try_convert_scalar_to_number with compound GMP shift/pow) +--EXTENSIONS-- +gmp +--FILE-- + [2, fn(&$x) => $x **= gmp_init(-5)], + 'gmp ** gmp' => [gmp_init(2), fn(&$x) => $x **= gmp_init(-5)], + 'array ** gmp' => [[0], fn(&$x) => $x **= gmp_init(-5)], + 'int << gmp' => [8, fn(&$x) => $x <<= gmp_init(-2)], + 'int >> gmp' => [8, fn(&$x) => $x >>= gmp_init(-2)], + 'gmp << gmp' => [gmp_init(8), fn(&$x) => $x <<= gmp_init(-2)], +]; + +foreach ($cases as $label => [$x, $op]) { + try { + $op($x); + echo "$label: no exception\n"; + } catch (\Throwable $e) { + echo "$label: ", $e::class, ": ", $e->getMessage(), "\n"; + } + var_dump($x); +} + +// Valid operations must still succeed (guard against an over-broad regression). +$p = 2; $p **= gmp_init(3); echo "valid pow: ", gmp_strval($p), "\n"; +$s = 1; $s <<= gmp_init(4); echo "valid shift: ", gmp_strval($s), "\n"; +?> +--EXPECTF-- +int ** gmp: ValueError: Exponent must be between 0 and %d +int(2) +gmp ** gmp: ValueError: Exponent must be between 0 and %d +object(GMP)#%d (1) { + ["num"]=> + string(1) "2" +} +array ** gmp: ValueError: Exponent must be between 0 and %d +array(1) { + [0]=> + int(0) +} +int << gmp: ValueError: Shift must be between 0 and %d +int(8) +int >> gmp: ValueError: Shift must be between 0 and %d +int(8) +gmp << gmp: ValueError: Shift must be between 0 and %d +object(GMP)#%d (1) { + ["num"]=> + string(1) "8" +} +valid pow: 8 +valid shift: 16 From 3704518154580db06687c631c1f201644785c80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 6 Jul 2026 21:26:28 +0200 Subject: [PATCH 7/7] ext/uri: Increase minimum uriparser version dependency (#22575) uriparser 1.0.3 has to be released containing some missing symbols in order to be able to build PHP with the "--with-external-uriparser" option. Closes https://github.com/php/php-src/issues/22553 --- ext/uri/config.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index a518cf84b3bd..dddf0c6e00b8 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -34,7 +34,7 @@ if test "$PHP_EXTERNAL_URIPARSER" = "no"; then $URIPARSER_DIR/src/UriSetScheme.c $URIPARSER_DIR/src/UriSetUserInfo.c $URIPARSER_DIR/src/UriShorten.c $URIPARSER_DIR/src/UriVersion.c" URI_CFLAGS="-DURI_STATIC_BUILD" else - PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 1.0.0]) + PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 1.0.3]) PHP_EVAL_LIBLINE([$LIBURIPARSER_LIBS]) PHP_EVAL_INCLINE([$LIBURIPARSER_CFLAGS]) fi