Commit 9313e8f6c05 for php.net
commit 9313e8f6c05a040fea7d8cdf1d9949c16896ae83
Author: Weilin Du <weilindu@php.net>
Date: Fri Jun 26 16:24:23 2026 +0800
Fix GH-17384: Reject too large number_format decimals (#22435)
number_format($number, 9876543210); is now silently equals to
number_format($number, 2147483647); and generates 2147483647
decimal places and eat up 2 GB memory (and exhaust almost half
of them which cause a fatal error).
I only reject very large positive numbers here (as every input larger
than 2147483647 is silently turned into 2147483647). Because
negative ones is always returning 0 anyways and only very large
positive numbers can cause to such problems.
Fixes #17384
diff --git a/NEWS b/NEWS
index ce6e46a4c36..1fe33f473af 100644
--- a/NEWS
+++ b/NEWS
@@ -283,6 +283,8 @@ PHP NEWS
(sebastian)
. Fixed bug GH-22171 (Invalid auth header generation in http(s) stream
wrapper). (David Carlier)
+ . Fixed bug GH-17384 (number_format() may exhaust memory with decimals
+ outside the range from -2147483648 to 2147483647). (Weilin Du)
- Streams:
. Added new stream errors API including new StreamException, StreamError
diff --git a/ext/standard/math.c b/ext/standard/math.c
index 6550e965d17..1498a1efc0b 100644
--- a/ext/standard/math.c
+++ b/ext/standard/math.c
@@ -1415,6 +1415,11 @@ PHP_FUNCTION(number_format)
thousand_sep_len = 1;
}
+ if (UNEXPECTED(dec > INT_MAX || dec < INT_MIN)) {
+ zend_argument_value_error(2, "must be between %d and %d", INT_MIN, INT_MAX);
+ RETURN_THROWS();
+ }
+
switch (Z_TYPE_P(num)) {
case IS_LONG:
RETURN_STR(_php_math_number_format_long(Z_LVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
@@ -1429,11 +1434,7 @@ PHP_FUNCTION(number_format)
RETURN_STR(_php_math_number_format_long((zend_long)Z_DVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
}
- if (dec >= 0) {
- dec_int = ZEND_LONG_INT_OVFL(dec) ? INT_MAX : (int)dec;
- } else {
- dec_int = ZEND_LONG_INT_UDFL(dec) ? INT_MIN : (int)dec;
- }
+ dec_int = (int) dec;
RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), dec_int, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
default: ZEND_UNREACHABLE();
diff --git a/ext/standard/tests/math/gh17384.phpt b/ext/standard/tests/math/gh17384.phpt
new file mode 100644
index 00000000000..eff207a906a
--- /dev/null
+++ b/ext/standard/tests/math/gh17384.phpt
@@ -0,0 +1,25 @@
+--TEST--
+GH-17384: number_format() must reject decimals outside the int range
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE !== 8) die('skip only for 64-bit');
+?>
+--FILE--
+<?php
+
+foreach ([1.23456, 1] as $number) {
+ foreach ([9876543210, -9876543210] as $decimals) {
+ try {
+ number_format($number, $decimals);
+ } catch (ValueError $exception) {
+ echo $exception->getMessage(), "\n";
+ }
+ }
+}
+
+?>
+--EXPECT--
+number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
+number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
+number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
+number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
diff --git a/ext/standard/tests/math/number_format_basiclong_64bit.phpt b/ext/standard/tests/math/number_format_basiclong_64bit.phpt
index fd709fc648f..7a986efc1fa 100644
--- a/ext/standard/tests/math/number_format_basiclong_64bit.phpt
+++ b/ext/standard/tests/math/number_format_basiclong_64bit.phpt
@@ -30,7 +30,6 @@
-17,
-19,
-20,
- PHP_INT_MIN,
);
foreach ($numbers as $number) {
@@ -54,7 +53,6 @@
... with precision -17: string(25) "9,200,000,000,000,000,000"
... with precision -19: string(26) "10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(-9223372036854775808)
... with precision 5: string(32) "-9,223,372,036,854,775,808.00000"
... with precision 0: string(26) "-9,223,372,036,854,775,808"
@@ -65,7 +63,6 @@
... with precision -17: string(26) "-9,200,000,000,000,000,000"
... with precision -19: string(27) "-10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(2147483647)
... with precision 5: string(19) "2,147,483,647.00000"
... with precision 0: string(13) "2,147,483,647"
@@ -76,7 +73,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(-2147483648)
... with precision 5: string(20) "-2,147,483,648.00000"
... with precision 0: string(14) "-2,147,483,648"
@@ -87,7 +83,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(9223372034707292160)
... with precision 5: string(31) "9,223,372,034,707,292,160.00000"
... with precision 0: string(25) "9,223,372,034,707,292,160"
@@ -98,7 +93,6 @@
... with precision -17: string(25) "9,200,000,000,000,000,000"
... with precision -19: string(26) "10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(-9223372034707292160)
... with precision 5: string(32) "-9,223,372,034,707,292,160.00000"
... with precision 0: string(26) "-9,223,372,034,707,292,160"
@@ -109,7 +103,6 @@
... with precision -17: string(26) "-9,200,000,000,000,000,000"
... with precision -19: string(27) "-10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(2147483648)
... with precision 5: string(19) "2,147,483,648.00000"
... with precision 0: string(13) "2,147,483,648"
@@ -120,7 +113,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(-2147483649)
... with precision 5: string(20) "-2,147,483,649.00000"
... with precision 0: string(14) "-2,147,483,649"
@@ -131,7 +123,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(4294967294)
... with precision 5: string(19) "4,294,967,294.00000"
... with precision 0: string(13) "4,294,967,294"
@@ -142,7 +133,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(4294967295)
... with precision 5: string(19) "4,294,967,295.00000"
... with precision 0: string(13) "4,294,967,295"
@@ -153,7 +143,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(4294967293)
... with precision 5: string(19) "4,294,967,293.00000"
... with precision 0: string(13) "4,294,967,293"
@@ -164,7 +153,6 @@
... with precision -17: string(1) "0"
... with precision -19: string(1) "0"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(9223372036854775806)
... with precision 5: string(31) "9,223,372,036,854,775,806.00000"
... with precision 0: string(25) "9,223,372,036,854,775,806"
@@ -175,7 +163,6 @@
... with precision -17: string(25) "9,200,000,000,000,000,000"
... with precision -19: string(26) "10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: float(9.223372036854776E+18)
... with precision 5: string(31) "9,223,372,036,854,775,808.00000"
... with precision 0: string(25) "9,223,372,036,854,775,808"
@@ -186,7 +173,6 @@
... with precision -17: string(25) "9,200,000,000,000,000,000"
... with precision -19: string(26) "10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: int(-9223372036854775807)
... with precision 5: string(32) "-9,223,372,036,854,775,807.00000"
... with precision 0: string(26) "-9,223,372,036,854,775,807"
@@ -197,7 +183,6 @@
... with precision -17: string(26) "-9,200,000,000,000,000,000"
... with precision -19: string(27) "-10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: float(-9.223372036854776E+18)
... with precision 5: string(32) "-9,223,372,036,854,775,808.00000"
... with precision 0: string(26) "-9,223,372,036,854,775,808"
@@ -208,7 +193,6 @@
... with precision -17: string(26) "-9,200,000,000,000,000,000"
... with precision -19: string(27) "-10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: float(9.223372036854775E+18)
... with precision 5: string(31) "9,223,372,036,854,774,784.00000"
... with precision 0: string(25) "9,223,372,036,854,774,784"
@@ -219,7 +203,6 @@
... with precision -17: string(25) "9,200,000,000,000,000,000"
... with precision -19: string(26) "10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
--- testing: float(-9.223372036854775E+18)
... with precision 5: string(32) "-9,223,372,036,854,774,784.00000"
... with precision 0: string(26) "-9,223,372,036,854,774,784"
@@ -230,4 +213,3 @@
... with precision -17: string(26) "-9,200,000,000,000,000,000"
... with precision -19: string(27) "-10,000,000,000,000,000,000"
... with precision -20: string(1) "0"
-... with precision -9223372036854775808: string(1) "0"
diff --git a/ext/standard/tests/math/number_format_decimals.phpt b/ext/standard/tests/math/number_format_decimals.phpt
index 5fa45fddce2..b589f0aefeb 100644
--- a/ext/standard/tests/math/number_format_decimals.phpt
+++ b/ext/standard/tests/math/number_format_decimals.phpt
@@ -29,7 +29,7 @@
MIN_INT32,
);
-$decimals = array(0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, PHP_INT_MIN);
+$decimals = array(0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5);
foreach ($values as $value) {
echo 'testing ';
@@ -55,7 +55,6 @@
... with decimal places of -3: string(1) "0"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(15.151)
... with decimal places of 0: string(2) "15"
... with decimal places of 1: string(4) "15.2"
@@ -68,7 +67,6 @@
... with decimal places of -3: string(1) "0"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(151.51)
... with decimal places of 0: string(3) "152"
... with decimal places of 1: string(5) "151.5"
@@ -81,7 +79,6 @@
... with decimal places of -3: string(1) "0"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(1515.1)
... with decimal places of 0: string(5) "1,515"
... with decimal places of 1: string(7) "1,515.1"
@@ -94,7 +91,6 @@
... with decimal places of -3: string(5) "2,000"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing int(15151)
... with decimal places of 0: string(6) "15,151"
... with decimal places of 1: string(8) "15,151.0"
@@ -107,7 +103,6 @@
... with decimal places of -3: string(6) "15,000"
... with decimal places of -4: string(6) "20,000"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(-1.5151)
... with decimal places of 0: string(2) "-2"
... with decimal places of 1: string(4) "-1.5"
@@ -120,7 +115,6 @@
... with decimal places of -3: string(1) "0"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(-15.151)
... with decimal places of 0: string(3) "-15"
... with decimal places of 1: string(5) "-15.2"
@@ -133,7 +127,6 @@
... with decimal places of -3: string(1) "0"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(-151.51)
... with decimal places of 0: string(4) "-152"
... with decimal places of 1: string(6) "-151.5"
@@ -146,7 +139,6 @@
... with decimal places of -3: string(1) "0"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(-1515.1)
... with decimal places of 0: string(6) "-1,515"
... with decimal places of 1: string(8) "-1,515.1"
@@ -159,7 +151,6 @@
... with decimal places of -3: string(6) "-2,000"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing int(-15151)
... with decimal places of 0: string(7) "-15,151"
... with decimal places of 1: string(9) "-15,151.0"
@@ -172,7 +163,6 @@
... with decimal places of -3: string(7) "-15,000"
... with decimal places of -4: string(7) "-20,000"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing int(999)
... with decimal places of 0: string(3) "999"
... with decimal places of 1: string(5) "999.0"
@@ -185,7 +175,6 @@
... with decimal places of -3: string(5) "1,000"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing int(-999)
... with decimal places of 0: string(4) "-999"
... with decimal places of 1: string(6) "-999.0"
@@ -198,7 +187,6 @@
... with decimal places of -3: string(6) "-1,000"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(999)
... with decimal places of 0: string(3) "999"
... with decimal places of 1: string(5) "999.0"
@@ -211,7 +199,6 @@
... with decimal places of -3: string(5) "1,000"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing float(-999)
... with decimal places of 0: string(4) "-999"
... with decimal places of 1: string(6) "-999.0"
@@ -224,7 +211,6 @@
... with decimal places of -3: string(6) "-1,000"
... with decimal places of -4: string(1) "0"
... with decimal places of -5: string(1) "0"
-... with decimal places of %i: string(1) "0"
testing int(999999)
... with decimal places of 0: string(7) "999,999"
... with decimal places of 1: string(9) "999,999.0"
@@ -237,7 +223,6 @@
... with decimal places of -3: string(9) "1,000,000"
... with decimal places of -4: string(9) "1,000,000"
... with decimal places of -5: string(9) "1,000,000"
-... with decimal places of %i: string(1) "0"
testing int(-999999)
... with decimal places of 0: string(8) "-999,999"
... with decimal places of 1: string(10) "-999,999.0"
@@ -250,7 +235,6 @@
... with decimal places of -3: string(10) "-1,000,000"
... with decimal places of -4: string(10) "-1,000,000"
... with decimal places of -5: string(10) "-1,000,000"
-... with decimal places of %i: string(1) "0"
testing float(999999)
... with decimal places of 0: string(7) "999,999"
... with decimal places of 1: string(9) "999,999.0"
@@ -263,7 +247,6 @@
... with decimal places of -3: string(9) "1,000,000"
... with decimal places of -4: string(9) "1,000,000"
... with decimal places of -5: string(9) "1,000,000"
-... with decimal places of %i: string(1) "0"
testing float(-999999)
... with decimal places of 0: string(8) "-999,999"
... with decimal places of 1: string(10) "-999,999.0"
@@ -276,7 +259,6 @@
... with decimal places of -3: string(10) "-1,000,000"
... with decimal places of -4: string(10) "-1,000,000"
... with decimal places of -5: string(10) "-1,000,000"
-... with decimal places of %i: string(1) "0"
testing int(2147483647)
... with decimal places of 0: string(13) "2,147,483,647"
... with decimal places of 1: string(15) "2,147,483,647.0"
@@ -289,7 +271,6 @@
... with decimal places of -3: string(13) "2,147,484,000"
... with decimal places of -4: string(13) "2,147,480,000"
... with decimal places of -5: string(13) "2,147,500,000"
-... with decimal places of %i: string(1) "0"
testing int(-2147483648)
... with decimal places of 0: string(14) "-2,147,483,648"
... with decimal places of 1: string(16) "-2,147,483,648.0"
@@ -302,4 +283,3 @@
... with decimal places of -3: string(14) "-2,147,484,000"
... with decimal places of -4: string(14) "-2,147,480,000"
... with decimal places of -5: string(14) "-2,147,500,000"
-... with decimal places of %i: string(1) "0"