Skip to content

Commit 9313e8f

Browse files
authored
Fix phpGH-17384: Reject too large number_format decimals (php#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 php#17384
1 parent c4b74d4 commit 9313e8f

5 files changed

Lines changed: 34 additions & 44 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ PHP NEWS
283283
(sebastian)
284284
. Fixed bug GH-22171 (Invalid auth header generation in http(s) stream
285285
wrapper). (David Carlier)
286+
. Fixed bug GH-17384 (number_format() may exhaust memory with decimals
287+
outside the range from -2147483648 to 2147483647). (Weilin Du)
286288

287289
- Streams:
288290
. Added new stream errors API including new StreamException, StreamError

ext/standard/math.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,11 @@ PHP_FUNCTION(number_format)
14151415
thousand_sep_len = 1;
14161416
}
14171417

1418+
if (UNEXPECTED(dec > INT_MAX || dec < INT_MIN)) {
1419+
zend_argument_value_error(2, "must be between %d and %d", INT_MIN, INT_MAX);
1420+
RETURN_THROWS();
1421+
}
1422+
14181423
switch (Z_TYPE_P(num)) {
14191424
case IS_LONG:
14201425
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)
14291434
RETURN_STR(_php_math_number_format_long((zend_long)Z_DVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
14301435
}
14311436

1432-
if (dec >= 0) {
1433-
dec_int = ZEND_LONG_INT_OVFL(dec) ? INT_MAX : (int)dec;
1434-
} else {
1435-
dec_int = ZEND_LONG_INT_UDFL(dec) ? INT_MIN : (int)dec;
1436-
}
1437+
dec_int = (int) dec;
14371438
RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), dec_int, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
14381439

14391440
default: ZEND_UNREACHABLE();
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
GH-17384: number_format() must reject decimals outside the int range
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE !== 8) die('skip only for 64-bit');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
foreach ([1.23456, 1] as $number) {
11+
foreach ([9876543210, -9876543210] as $decimals) {
12+
try {
13+
number_format($number, $decimals);
14+
} catch (ValueError $exception) {
15+
echo $exception->getMessage(), "\n";
16+
}
17+
}
18+
}
19+
20+
?>
21+
--EXPECT--
22+
number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
23+
number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
24+
number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647
25+
number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647

ext/standard/tests/math/number_format_basiclong_64bit.phpt

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ $precisions = array(
3030
-17,
3131
-19,
3232
-20,
33-
PHP_INT_MIN,
3433
);
3534

3635
foreach ($numbers as $number) {
@@ -54,7 +53,6 @@ foreach ($numbers as $number) {
5453
... with precision -17: string(25) "9,200,000,000,000,000,000"
5554
... with precision -19: string(26) "10,000,000,000,000,000,000"
5655
... with precision -20: string(1) "0"
57-
... with precision -9223372036854775808: string(1) "0"
5856
--- testing: int(-9223372036854775808)
5957
... with precision 5: string(32) "-9,223,372,036,854,775,808.00000"
6058
... with precision 0: string(26) "-9,223,372,036,854,775,808"
@@ -65,7 +63,6 @@ foreach ($numbers as $number) {
6563
... with precision -17: string(26) "-9,200,000,000,000,000,000"
6664
... with precision -19: string(27) "-10,000,000,000,000,000,000"
6765
... with precision -20: string(1) "0"
68-
... with precision -9223372036854775808: string(1) "0"
6966
--- testing: int(2147483647)
7067
... with precision 5: string(19) "2,147,483,647.00000"
7168
... with precision 0: string(13) "2,147,483,647"
@@ -76,7 +73,6 @@ foreach ($numbers as $number) {
7673
... with precision -17: string(1) "0"
7774
... with precision -19: string(1) "0"
7875
... with precision -20: string(1) "0"
79-
... with precision -9223372036854775808: string(1) "0"
8076
--- testing: int(-2147483648)
8177
... with precision 5: string(20) "-2,147,483,648.00000"
8278
... with precision 0: string(14) "-2,147,483,648"
@@ -87,7 +83,6 @@ foreach ($numbers as $number) {
8783
... with precision -17: string(1) "0"
8884
... with precision -19: string(1) "0"
8985
... with precision -20: string(1) "0"
90-
... with precision -9223372036854775808: string(1) "0"
9186
--- testing: int(9223372034707292160)
9287
... with precision 5: string(31) "9,223,372,034,707,292,160.00000"
9388
... with precision 0: string(25) "9,223,372,034,707,292,160"
@@ -98,7 +93,6 @@ foreach ($numbers as $number) {
9893
... with precision -17: string(25) "9,200,000,000,000,000,000"
9994
... with precision -19: string(26) "10,000,000,000,000,000,000"
10095
... with precision -20: string(1) "0"
101-
... with precision -9223372036854775808: string(1) "0"
10296
--- testing: int(-9223372034707292160)
10397
... with precision 5: string(32) "-9,223,372,034,707,292,160.00000"
10498
... with precision 0: string(26) "-9,223,372,034,707,292,160"
@@ -109,7 +103,6 @@ foreach ($numbers as $number) {
109103
... with precision -17: string(26) "-9,200,000,000,000,000,000"
110104
... with precision -19: string(27) "-10,000,000,000,000,000,000"
111105
... with precision -20: string(1) "0"
112-
... with precision -9223372036854775808: string(1) "0"
113106
--- testing: int(2147483648)
114107
... with precision 5: string(19) "2,147,483,648.00000"
115108
... with precision 0: string(13) "2,147,483,648"
@@ -120,7 +113,6 @@ foreach ($numbers as $number) {
120113
... with precision -17: string(1) "0"
121114
... with precision -19: string(1) "0"
122115
... with precision -20: string(1) "0"
123-
... with precision -9223372036854775808: string(1) "0"
124116
--- testing: int(-2147483649)
125117
... with precision 5: string(20) "-2,147,483,649.00000"
126118
... with precision 0: string(14) "-2,147,483,649"
@@ -131,7 +123,6 @@ foreach ($numbers as $number) {
131123
... with precision -17: string(1) "0"
132124
... with precision -19: string(1) "0"
133125
... with precision -20: string(1) "0"
134-
... with precision -9223372036854775808: string(1) "0"
135126
--- testing: int(4294967294)
136127
... with precision 5: string(19) "4,294,967,294.00000"
137128
... with precision 0: string(13) "4,294,967,294"
@@ -142,7 +133,6 @@ foreach ($numbers as $number) {
142133
... with precision -17: string(1) "0"
143134
... with precision -19: string(1) "0"
144135
... with precision -20: string(1) "0"
145-
... with precision -9223372036854775808: string(1) "0"
146136
--- testing: int(4294967295)
147137
... with precision 5: string(19) "4,294,967,295.00000"
148138
... with precision 0: string(13) "4,294,967,295"
@@ -153,7 +143,6 @@ foreach ($numbers as $number) {
153143
... with precision -17: string(1) "0"
154144
... with precision -19: string(1) "0"
155145
... with precision -20: string(1) "0"
156-
... with precision -9223372036854775808: string(1) "0"
157146
--- testing: int(4294967293)
158147
... with precision 5: string(19) "4,294,967,293.00000"
159148
... with precision 0: string(13) "4,294,967,293"
@@ -164,7 +153,6 @@ foreach ($numbers as $number) {
164153
... with precision -17: string(1) "0"
165154
... with precision -19: string(1) "0"
166155
... with precision -20: string(1) "0"
167-
... with precision -9223372036854775808: string(1) "0"
168156
--- testing: int(9223372036854775806)
169157
... with precision 5: string(31) "9,223,372,036,854,775,806.00000"
170158
... with precision 0: string(25) "9,223,372,036,854,775,806"
@@ -175,7 +163,6 @@ foreach ($numbers as $number) {
175163
... with precision -17: string(25) "9,200,000,000,000,000,000"
176164
... with precision -19: string(26) "10,000,000,000,000,000,000"
177165
... with precision -20: string(1) "0"
178-
... with precision -9223372036854775808: string(1) "0"
179166
--- testing: float(9.223372036854776E+18)
180167
... with precision 5: string(31) "9,223,372,036,854,775,808.00000"
181168
... with precision 0: string(25) "9,223,372,036,854,775,808"
@@ -186,7 +173,6 @@ foreach ($numbers as $number) {
186173
... with precision -17: string(25) "9,200,000,000,000,000,000"
187174
... with precision -19: string(26) "10,000,000,000,000,000,000"
188175
... with precision -20: string(1) "0"
189-
... with precision -9223372036854775808: string(1) "0"
190176
--- testing: int(-9223372036854775807)
191177
... with precision 5: string(32) "-9,223,372,036,854,775,807.00000"
192178
... with precision 0: string(26) "-9,223,372,036,854,775,807"
@@ -197,7 +183,6 @@ foreach ($numbers as $number) {
197183
... with precision -17: string(26) "-9,200,000,000,000,000,000"
198184
... with precision -19: string(27) "-10,000,000,000,000,000,000"
199185
... with precision -20: string(1) "0"
200-
... with precision -9223372036854775808: string(1) "0"
201186
--- testing: float(-9.223372036854776E+18)
202187
... with precision 5: string(32) "-9,223,372,036,854,775,808.00000"
203188
... with precision 0: string(26) "-9,223,372,036,854,775,808"
@@ -208,7 +193,6 @@ foreach ($numbers as $number) {
208193
... with precision -17: string(26) "-9,200,000,000,000,000,000"
209194
... with precision -19: string(27) "-10,000,000,000,000,000,000"
210195
... with precision -20: string(1) "0"
211-
... with precision -9223372036854775808: string(1) "0"
212196
--- testing: float(9.223372036854775E+18)
213197
... with precision 5: string(31) "9,223,372,036,854,774,784.00000"
214198
... with precision 0: string(25) "9,223,372,036,854,774,784"
@@ -219,7 +203,6 @@ foreach ($numbers as $number) {
219203
... with precision -17: string(25) "9,200,000,000,000,000,000"
220204
... with precision -19: string(26) "10,000,000,000,000,000,000"
221205
... with precision -20: string(1) "0"
222-
... with precision -9223372036854775808: string(1) "0"
223206
--- testing: float(-9.223372036854775E+18)
224207
... with precision 5: string(32) "-9,223,372,036,854,774,784.00000"
225208
... with precision 0: string(26) "-9,223,372,036,854,774,784"
@@ -230,4 +213,3 @@ foreach ($numbers as $number) {
230213
... with precision -17: string(26) "-9,200,000,000,000,000,000"
231214
... with precision -19: string(27) "-10,000,000,000,000,000,000"
232215
... with precision -20: string(1) "0"
233-
... with precision -9223372036854775808: string(1) "0"

ext/standard/tests/math/number_format_decimals.phpt

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ $values = array(
2929
MIN_INT32,
3030
);
3131

32-
$decimals = array(0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, PHP_INT_MIN);
32+
$decimals = array(0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5);
3333

3434
foreach ($values as $value) {
3535
echo 'testing ';
@@ -55,7 +55,6 @@ testing float(1.5151)
5555
... with decimal places of -3: string(1) "0"
5656
... with decimal places of -4: string(1) "0"
5757
... with decimal places of -5: string(1) "0"
58-
... with decimal places of %i: string(1) "0"
5958
testing float(15.151)
6059
... with decimal places of 0: string(2) "15"
6160
... with decimal places of 1: string(4) "15.2"
@@ -68,7 +67,6 @@ testing float(15.151)
6867
... with decimal places of -3: string(1) "0"
6968
... with decimal places of -4: string(1) "0"
7069
... with decimal places of -5: string(1) "0"
71-
... with decimal places of %i: string(1) "0"
7270
testing float(151.51)
7371
... with decimal places of 0: string(3) "152"
7472
... with decimal places of 1: string(5) "151.5"
@@ -81,7 +79,6 @@ testing float(151.51)
8179
... with decimal places of -3: string(1) "0"
8280
... with decimal places of -4: string(1) "0"
8381
... with decimal places of -5: string(1) "0"
84-
... with decimal places of %i: string(1) "0"
8582
testing float(1515.1)
8683
... with decimal places of 0: string(5) "1,515"
8784
... with decimal places of 1: string(7) "1,515.1"
@@ -94,7 +91,6 @@ testing float(1515.1)
9491
... with decimal places of -3: string(5) "2,000"
9592
... with decimal places of -4: string(1) "0"
9693
... with decimal places of -5: string(1) "0"
97-
... with decimal places of %i: string(1) "0"
9894
testing int(15151)
9995
... with decimal places of 0: string(6) "15,151"
10096
... with decimal places of 1: string(8) "15,151.0"
@@ -107,7 +103,6 @@ testing int(15151)
107103
... with decimal places of -3: string(6) "15,000"
108104
... with decimal places of -4: string(6) "20,000"
109105
... with decimal places of -5: string(1) "0"
110-
... with decimal places of %i: string(1) "0"
111106
testing float(-1.5151)
112107
... with decimal places of 0: string(2) "-2"
113108
... with decimal places of 1: string(4) "-1.5"
@@ -120,7 +115,6 @@ testing float(-1.5151)
120115
... with decimal places of -3: string(1) "0"
121116
... with decimal places of -4: string(1) "0"
122117
... with decimal places of -5: string(1) "0"
123-
... with decimal places of %i: string(1) "0"
124118
testing float(-15.151)
125119
... with decimal places of 0: string(3) "-15"
126120
... with decimal places of 1: string(5) "-15.2"
@@ -133,7 +127,6 @@ testing float(-15.151)
133127
... with decimal places of -3: string(1) "0"
134128
... with decimal places of -4: string(1) "0"
135129
... with decimal places of -5: string(1) "0"
136-
... with decimal places of %i: string(1) "0"
137130
testing float(-151.51)
138131
... with decimal places of 0: string(4) "-152"
139132
... with decimal places of 1: string(6) "-151.5"
@@ -146,7 +139,6 @@ testing float(-151.51)
146139
... with decimal places of -3: string(1) "0"
147140
... with decimal places of -4: string(1) "0"
148141
... with decimal places of -5: string(1) "0"
149-
... with decimal places of %i: string(1) "0"
150142
testing float(-1515.1)
151143
... with decimal places of 0: string(6) "-1,515"
152144
... with decimal places of 1: string(8) "-1,515.1"
@@ -159,7 +151,6 @@ testing float(-1515.1)
159151
... with decimal places of -3: string(6) "-2,000"
160152
... with decimal places of -4: string(1) "0"
161153
... with decimal places of -5: string(1) "0"
162-
... with decimal places of %i: string(1) "0"
163154
testing int(-15151)
164155
... with decimal places of 0: string(7) "-15,151"
165156
... with decimal places of 1: string(9) "-15,151.0"
@@ -172,7 +163,6 @@ testing int(-15151)
172163
... with decimal places of -3: string(7) "-15,000"
173164
... with decimal places of -4: string(7) "-20,000"
174165
... with decimal places of -5: string(1) "0"
175-
... with decimal places of %i: string(1) "0"
176166
testing int(999)
177167
... with decimal places of 0: string(3) "999"
178168
... with decimal places of 1: string(5) "999.0"
@@ -185,7 +175,6 @@ testing int(999)
185175
... with decimal places of -3: string(5) "1,000"
186176
... with decimal places of -4: string(1) "0"
187177
... with decimal places of -5: string(1) "0"
188-
... with decimal places of %i: string(1) "0"
189178
testing int(-999)
190179
... with decimal places of 0: string(4) "-999"
191180
... with decimal places of 1: string(6) "-999.0"
@@ -198,7 +187,6 @@ testing int(-999)
198187
... with decimal places of -3: string(6) "-1,000"
199188
... with decimal places of -4: string(1) "0"
200189
... with decimal places of -5: string(1) "0"
201-
... with decimal places of %i: string(1) "0"
202190
testing float(999)
203191
... with decimal places of 0: string(3) "999"
204192
... with decimal places of 1: string(5) "999.0"
@@ -211,7 +199,6 @@ testing float(999)
211199
... with decimal places of -3: string(5) "1,000"
212200
... with decimal places of -4: string(1) "0"
213201
... with decimal places of -5: string(1) "0"
214-
... with decimal places of %i: string(1) "0"
215202
testing float(-999)
216203
... with decimal places of 0: string(4) "-999"
217204
... with decimal places of 1: string(6) "-999.0"
@@ -224,7 +211,6 @@ testing float(-999)
224211
... with decimal places of -3: string(6) "-1,000"
225212
... with decimal places of -4: string(1) "0"
226213
... with decimal places of -5: string(1) "0"
227-
... with decimal places of %i: string(1) "0"
228214
testing int(999999)
229215
... with decimal places of 0: string(7) "999,999"
230216
... with decimal places of 1: string(9) "999,999.0"
@@ -237,7 +223,6 @@ testing int(999999)
237223
... with decimal places of -3: string(9) "1,000,000"
238224
... with decimal places of -4: string(9) "1,000,000"
239225
... with decimal places of -5: string(9) "1,000,000"
240-
... with decimal places of %i: string(1) "0"
241226
testing int(-999999)
242227
... with decimal places of 0: string(8) "-999,999"
243228
... with decimal places of 1: string(10) "-999,999.0"
@@ -250,7 +235,6 @@ testing int(-999999)
250235
... with decimal places of -3: string(10) "-1,000,000"
251236
... with decimal places of -4: string(10) "-1,000,000"
252237
... with decimal places of -5: string(10) "-1,000,000"
253-
... with decimal places of %i: string(1) "0"
254238
testing float(999999)
255239
... with decimal places of 0: string(7) "999,999"
256240
... with decimal places of 1: string(9) "999,999.0"
@@ -263,7 +247,6 @@ testing float(999999)
263247
... with decimal places of -3: string(9) "1,000,000"
264248
... with decimal places of -4: string(9) "1,000,000"
265249
... with decimal places of -5: string(9) "1,000,000"
266-
... with decimal places of %i: string(1) "0"
267250
testing float(-999999)
268251
... with decimal places of 0: string(8) "-999,999"
269252
... with decimal places of 1: string(10) "-999,999.0"
@@ -276,7 +259,6 @@ testing float(-999999)
276259
... with decimal places of -3: string(10) "-1,000,000"
277260
... with decimal places of -4: string(10) "-1,000,000"
278261
... with decimal places of -5: string(10) "-1,000,000"
279-
... with decimal places of %i: string(1) "0"
280262
testing int(2147483647)
281263
... with decimal places of 0: string(13) "2,147,483,647"
282264
... with decimal places of 1: string(15) "2,147,483,647.0"
@@ -289,7 +271,6 @@ testing int(2147483647)
289271
... with decimal places of -3: string(13) "2,147,484,000"
290272
... with decimal places of -4: string(13) "2,147,480,000"
291273
... with decimal places of -5: string(13) "2,147,500,000"
292-
... with decimal places of %i: string(1) "0"
293274
testing int(-2147483648)
294275
... with decimal places of 0: string(14) "-2,147,483,648"
295276
... with decimal places of 1: string(16) "-2,147,483,648.0"
@@ -302,4 +283,3 @@ testing int(-2147483648)
302283
... with decimal places of -3: string(14) "-2,147,484,000"
303284
... with decimal places of -4: string(14) "-2,147,480,000"
304285
... with decimal places of -5: string(14) "-2,147,500,000"
305-
... with decimal places of %i: string(1) "0"

0 commit comments

Comments
 (0)