Commit 6eca1d0b1a6 for php

commit 6eca1d0b1a6ad64dc22c7a0d40538b6c54fa073d
Author: Jorg Sowa <jorg.sowa@gmail.com>
Date:   Thu Nov 12 22:19:29 2026 +0100

    Fix NumberFormatter symbol and attribute range checks on truncated values (GH-25425)

    getSymbol()/setSymbol() cast the zend_long to UNumberFormatSymbol before
    range-checking it, and getAttribute()/setAttribute() switched on the value
    after casting it to UNumberFormatAttribute, so values like 2**32 wrapped
    into the valid range.

    Closes GH-25425

diff --git a/NEWS b/NEWS
index bc922e61a30..003160c8b56 100644
--- a/NEWS
+++ b/NEWS
@@ -60,6 +60,11 @@ PHP                                                                        NEWS
   . Fixed stack overflow in filter_var() and filter_var_array() with deeply
     nested arrays. (Eyüp Can Akman)

+- Intl:
+  . Fixed NumberFormatter::getSymbol(), setSymbol(), getAttribute() and
+    setAttribute() accepting out-of-range values that truncate to a valid one.
+    (Jorg Sowa)
+
 - MBString:
   . Fixed stack overflow in mb_check_encoding() and mb_convert_encoding() with
     deeply nested arrays. (Eyüp Can Akman)
diff --git a/ext/intl/formatter/formatter_attr.cpp b/ext/intl/formatter/formatter_attr.cpp
index ff215fdffbd..815b8abcb97 100644
--- a/ext/intl/formatter/formatter_attr.cpp
+++ b/ext/intl/formatter/formatter_attr.cpp
@@ -45,7 +45,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_attribute )
 	/* Fetch the object. */
 	FORMATTER_METHOD_FETCH_OBJECT;

-	switch(attribute) {
+	switch(lattribute) {
 		case UNUM_PARSE_INT_ONLY:
 		case UNUM_GROUPING_USED:
 		case UNUM_DECIMAL_ALWAYS_SHOWN:
@@ -149,7 +149,7 @@ U_CFUNC PHP_FUNCTION( numfmt_set_attribute )

 	UNumberFormatAttribute attribute = static_cast<UNumberFormatAttribute>(lattribute);

-	switch(attribute) {
+	switch(lattribute) {
 		case UNUM_PARSE_INT_ONLY:
 		case UNUM_GROUPING_USED:
 		case UNUM_DECIMAL_ALWAYS_SHOWN:
@@ -232,13 +232,13 @@ U_CFUNC PHP_FUNCTION( numfmt_get_symbol )
 		RETURN_THROWS();
 	}

-	UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(lsymbol);
-
-	if (UNEXPECTED(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0)) {
+	if (UNEXPECTED(lsymbol < 0 || lsymbol >= UNUM_FORMAT_SYMBOL_COUNT)) {
 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,	"invalid symbol value");
 		RETURN_FALSE;
 	}

+	UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(lsymbol);
+
 	/* Fetch the object. */
 	FORMATTER_METHOD_FETCH_OBJECT;

@@ -274,13 +274,13 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol )
 		RETURN_THROWS();
 	}

-	UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(lsymbol);
-
-	if (UNEXPECTED(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0)) {
+	if (UNEXPECTED(lsymbol < 0 || lsymbol >= UNUM_FORMAT_SYMBOL_COUNT)) {
 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,	"invalid symbol value");
 		RETURN_FALSE;
 	}

+	UNumberFormatSymbol symbol = static_cast<UNumberFormatSymbol>(lsymbol);
+
 	/* Fetch the object. */
 	FORMATTER_METHOD_FETCH_OBJECT;

diff --git a/ext/intl/tests/bug53512.phpt b/ext/intl/tests/bug53512.phpt
index 9c6a128c300..7e8a5c626bf 100644
--- a/ext/intl/tests/bug53512.phpt
+++ b/ext/intl/tests/bug53512.phpt
@@ -8,7 +8,7 @@
 --FILE--
 <?php

-$badvals = array(4294901761, 2147483648, -2147483648, -1);
+$badvals = array(4294901761, 2147483648, -2147483648, -1, 4294967296, 4294967297);

 foreach ($badvals as $val) {
     $x = numfmt_create("en", NumberFormatter::PATTERN_DECIMAL);
@@ -26,3 +26,7 @@
 string(67) "numfmt_set_symbol(): invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
 bool(false)
 string(67) "numfmt_set_symbol(): invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
+bool(false)
+string(67) "numfmt_set_symbol(): invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
+bool(false)
+string(67) "numfmt_set_symbol(): invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
diff --git a/ext/intl/tests/formatter_get_set_attribute_truncation.phpt b/ext/intl/tests/formatter_get_set_attribute_truncation.phpt
new file mode 100644
index 00000000000..9b341ddc337
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_attribute_truncation.phpt
@@ -0,0 +1,42 @@
+--TEST--
+NumberFormatter::getAttribute()/setAttribute() reject attributes that truncate to a valid one
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die('skip 64-bit only'); ?>
+--FILE--
+<?php
+
+$fmt = new NumberFormatter("en", NumberFormatter::DECIMAL);
+
+foreach ([NumberFormatter::MAX_FRACTION_DIGITS, NumberFormatter::ROUNDING_INCREMENT] as $attr) {
+    $bad = $attr + (1 << 32);
+
+    var_dump($fmt->getAttribute($bad));
+    var_dump($fmt->getErrorMessage());
+    var_dump(numfmt_get_attribute($fmt, $bad));
+
+    var_dump($fmt->setAttribute($bad, 5));
+    var_dump($fmt->getErrorMessage());
+    var_dump(numfmt_set_attribute($fmt, $bad, 5));
+}
+
+var_dump($fmt->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS));
+var_dump($fmt->getAttribute(NumberFormatter::ROUNDING_INCREMENT));
+
+?>
+--EXPECT--
+bool(false)
+string(83) "NumberFormatter::getAttribute(): Error getting attribute value: U_UNSUPPORTED_ERROR"
+bool(false)
+bool(false)
+string(83) "NumberFormatter::setAttribute(): Error setting attribute value: U_UNSUPPORTED_ERROR"
+bool(false)
+bool(false)
+string(83) "NumberFormatter::getAttribute(): Error getting attribute value: U_UNSUPPORTED_ERROR"
+bool(false)
+bool(false)
+string(83) "NumberFormatter::setAttribute(): Error setting attribute value: U_UNSUPPORTED_ERROR"
+bool(false)
+int(3)
+float(0)
diff --git a/ext/intl/tests/formatter_get_set_symbol2.phpt b/ext/intl/tests/formatter_get_set_symbol2.phpt
index f6594d6a0b9..1cae6a204e5 100644
--- a/ext/intl/tests/formatter_get_set_symbol2.phpt
+++ b/ext/intl/tests/formatter_get_set_symbol2.phpt
@@ -75,9 +75,9 @@ function ut_main()
         // Restore attribute's symbol.
         ut_nfmt_set_symbol( $fmt, $symb, $orig_val );
     }
-    $badvals = array(2147483648, -2147483648, -1, 4294901761);
+    $badvals = array(2147483648, -2147483648, -1, 4294901761, 4294967296, 4294967297);
     foreach($badvals as $badval) {
-        if(ut_nfmt_get_symbol( $fmt, 2147483648 ))  {
+        if(ut_nfmt_get_symbol( $fmt, $badval ))  {
         $res_str .= "Bad value $badval should return false!\n";
         }
     }