diff --git a/NEWS b/NEWS index 77ea35389d5f..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 @@ -51,6 +55,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/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 */ 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; } 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 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/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-- 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/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 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} 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