From d8d8a3628428104bf8ab631f63e686506cdcdeee Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 3 Jul 2026 18:20:41 +0800 Subject: [PATCH 1/2] ext/intl: Fix NumberFormatter parse offset overflow --- NEWS | 5 ++ ext/intl/formatter/formatter_parse.cpp | 16 +++++-- .../formatter_parse_offset_overflow.phpt | 46 +++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 ext/intl/tests/formatter_parse_offset_overflow.phpt diff --git a/NEWS b/NEWS index caddbd717490..153e12581d75 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,11 @@ 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) +- Intl: + . Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to + reject offset values outside the 32-bit range instead of silently + truncating them. (Weilin Du) + - Opcache: . Fixed bug GH-21770 (Infinite recursion in property hook getter in opcache preloaded trait). (iliaal) diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index a475960809b8..7aa92488704a 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -50,7 +50,12 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) } if (zposition) { - position = (int32_t) zval_get_long(zposition); + zend_long long_position = zval_get_long(zposition); + if (ZEND_LONG_EXCEEDS_INT(long_position)) { + zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); + RETURN_THROWS(); + } + position = (int32_t) long_position; } /* Fetch the object. */ @@ -155,8 +160,13 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" ); - if(zposition) { - position = (int32_t) zval_get_long(zposition); + if (zposition) { + zend_long long_position = zval_get_long(zposition); + if (ZEND_LONG_EXCEEDS_INT(long_position)) { + zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); + RETURN_THROWS(); + } + position = (int32_t) long_position; } icu::ParsePosition pp(position); diff --git a/ext/intl/tests/formatter_parse_offset_overflow.phpt b/ext/intl/tests/formatter_parse_offset_overflow.phpt new file mode 100644 index 000000000000..9421c8897511 --- /dev/null +++ b/ext/intl/tests/formatter_parse_offset_overflow.phpt @@ -0,0 +1,46 @@ +--TEST-- +NumberFormatter parse offset overflow +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; + } +} + +$offset = PHP_INT_MAX; +print_error(function () use ($fmt, &$offset) { + $fmt->parse('123', NumberFormatter::TYPE_DOUBLE, $offset); +}); + +$offset = PHP_INT_MAX; +print_error(function () use ($fmt, &$offset) { + numfmt_parse($fmt, '123', NumberFormatter::TYPE_DOUBLE, $offset); +}); + +$currency = ''; +$offset = PHP_INT_MAX; +print_error(function () use ($currencyFmt, &$currency, &$offset) { + $currencyFmt->parseCurrency('$123.00', $currency, $offset); +}); + +$currency = ''; +$offset = PHP_INT_MAX; +print_error(function () use ($currencyFmt, &$currency, &$offset) { + numfmt_parse_currency($currencyFmt, '$123.00', $currency, $offset); +}); +?> +--EXPECT-- +ValueError: NumberFormatter::parse(): Argument #3 ($offset) must be between -2147483648 and 2147483647 +ValueError: numfmt_parse(): Argument #4 ($offset) must be between -2147483648 and 2147483647 +ValueError: NumberFormatter::parseCurrency(): Argument #3 ($offset) must be between -2147483648 and 2147483647 +ValueError: numfmt_parse_currency(): Argument #4 ($offset) must be between -2147483648 and 2147483647 From 7d000d838127c6639b4c7ccca9a53ef28149d525 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 4 Jul 2026 01:43:00 +0800 Subject: [PATCH 2/2] check INT32_MIN and INT32_MAX manually --- ext/intl/formatter/formatter_parse.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index 7aa92488704a..63c0fba4329f 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -51,7 +51,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) if (zposition) { zend_long long_position = zval_get_long(zposition); - if (ZEND_LONG_EXCEEDS_INT(long_position)) { + if (long_position < INT32_MIN || long_position > INT32_MAX) { zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); RETURN_THROWS(); } @@ -162,7 +162,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) if (zposition) { zend_long long_position = zval_get_long(zposition); - if (ZEND_LONG_EXCEEDS_INT(long_position)) { + if (long_position < INT32_MIN || long_position > INT32_MAX) { zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX); RETURN_THROWS(); }