Commit 14a7eadfb25 for php.net
commit 14a7eadfb25508af19ea831e51e66dc32a7b871e
Author: Weilin Du <weilindu@php.net>
Date: Fri Jul 3 18:12:50 2026 +0800
ext/intl: Fix NumberFormatter parse offset overflow
diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp
index a475960809b..7aa92488704 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 00000000000..9421c889751
--- /dev/null
+++ b/ext/intl/tests/formatter_parse_offset_overflow.phpt
@@ -0,0 +1,46 @@
+--TEST--
+NumberFormatter parse offset overflow
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
+--FILE--
+<?php
+$fmt = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
+$currencyFmt = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
+
+function print_error(callable $callback): void {
+ try {
+ $callback();
+ } catch (Throwable $e) {
+ echo $e::class, ': ', $e->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