Skip to content

Commit 2a69a8f

Browse files
committed
number_format: prevent integer overflow on big decimal number
1 parent b24b351 commit 2a69a8f

File tree

4 files changed

+83
-27
lines changed

4 files changed

+83
-27
lines changed

ext/standard/math.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ PHP_FUNCTION(round)
286286
if (precision >= 0) {
287287
places = precision > INT_MAX ? INT_MAX : (int)precision;
288288
} else {
289-
places = precision <= INT_MIN ? INT_MIN+1 : (int)precision;
289+
places = precision <= INT_MIN ? INT_MIN : (int)precision;
290290
}
291291
#else
292292
places = precision;
@@ -1007,12 +1007,12 @@ PHP_FUNCTION(base_convert)
10071007
/* }}} */
10081008

10091009
/* {{{ _php_math_number_format */
1010-
PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1010+
PHPAPI zend_string *_php_math_number_format(double d, zend_long dec, char dec_point, char thousand_sep)
10111011
{
10121012
return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
10131013
}
10141014

1015-
PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *dec_point,
1015+
PHPAPI zend_string *_php_math_number_format_ex(double d, zend_long dec, const char *dec_point,
10161016
size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len)
10171017
{
10181018
zend_string *res;
@@ -1023,15 +1023,30 @@ PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *de
10231023
size_t reslen = 0;
10241024
int count = 0;
10251025
int is_negative = 0;
1026+
int dec_round;
1027+
1028+
// prevent integer overflow
1029+
#if SIZEOF_ZEND_LONG > SIZEOF_INT
1030+
if (dec >= 0) {
1031+
dec_round = dec > INT_MAX ? INT_MAX : (int)dec;
1032+
} else {
1033+
dec_round = dec <= INT_MIN ? INT_MIN : (int)dec;
1034+
}
1035+
#else
1036+
dec_round = dec;
1037+
#endif
10261038

10271039
if (d < 0) {
10281040
is_negative = 1;
10291041
d = -d;
10301042
}
10311043

1032-
d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1044+
d = _php_math_round(d, dec_round, PHP_ROUND_HALF_UP);
10331045
dec = MAX(0, dec);
1034-
tmpbuf = strpprintf(0, "%.*F", dec, d);
1046+
dec_round = MAX(0, dec_round);
1047+
1048+
tmpbuf = strpprintf(0, "%.*F", dec_round, d);
1049+
10351050
if (tmpbuf == NULL) {
10361051
return NULL;
10371052
} else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
@@ -1114,7 +1129,7 @@ PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *de
11141129
* separator every three digits */
11151130
while (s >= ZSTR_VAL(tmpbuf)) {
11161131
*t-- = *s--;
1117-
if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) {
1132+
if (thousand_sep && (++count % 3) == 0 && s >= ZSTR_VAL(tmpbuf)) {
11181133
t -= thousand_sep_len;
11191134
memcpy(t + 1, thousand_sep, thousand_sep_len);
11201135
}
@@ -1279,7 +1294,7 @@ PHP_FUNCTION(number_format)
12791294
break;
12801295

12811296
case IS_DOUBLE:
1282-
RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), (int)dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1297+
RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len));
12831298
break;
12841299

12851300
EMPTY_SWITCH_DEFAULT_CASE()

ext/standard/php_math.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
#define PHP_MATH_H
2020

2121
PHPAPI double _php_math_round(double value, int places, int mode);
22-
PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep);
23-
PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *dec_point, size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len);
22+
PHPAPI zend_string *_php_math_number_format(double d, zend_long dec, char dec_point, char thousand_sep);
23+
PHPAPI zend_string *_php_math_number_format_ex(double d, zend_long dec, const char *dec_point, size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len);
2424
PHPAPI zend_string *_php_math_number_format_long(zend_long num, zend_long dec, const char *dec_point, size_t dec_point_len, const char *thousand_sep, size_t thousand_sep_len);
2525
PHPAPI zend_string * _php_math_longtobase(zend_long arg, int base);
2626
PHPAPI zend_long _php_math_basetolong(zval *arg, int base);

ext/standard/tests/math/number_format_basiclong_64bit.phpt

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ define("MIN_32Bit", -2147483647 - 1);
1515
$longVals = array(
1616
MAX_64Bit, MIN_64Bit, MAX_32Bit, MIN_32Bit, MAX_64Bit - MAX_32Bit, MIN_64Bit - MIN_32Bit,
1717
MAX_32Bit + 1, MIN_32Bit - 1, MAX_32Bit * 2, (MAX_32Bit * 2) + 1, (MAX_32Bit * 2) - 1,
18-
MAX_64Bit -1, MIN_64Bit + 1
18+
MAX_64Bit -1, MAX_64Bit + 1, MIN_64Bit + 1, MIN_64Bit - 1
1919
);
2020

2121
$precisions = array(
@@ -41,7 +41,7 @@ foreach ($longVals as $longVal) {
4141
}
4242

4343
?>
44-
--EXPECTF--
44+
--EXPECT--
4545
--- testing: int(9223372036854775807)
4646
... with precision 5: string(31) "9,223,372,036,854,775,807.00000"
4747
... with precision 0: string(25) "9,223,372,036,854,775,807"
@@ -52,7 +52,7 @@ foreach ($longVals as $longVal) {
5252
... with precision -17: string(25) "9,200,000,000,000,000,000"
5353
... with precision -19: string(26) "10,000,000,000,000,000,000"
5454
... with precision -20: string(1) "0"
55-
... with precision %i: string(1) "0"
55+
... with precision -9223372036854775808: string(1) "0"
5656
--- testing: int(-9223372036854775808)
5757
... with precision 5: string(32) "-9,223,372,036,854,775,808.00000"
5858
... with precision 0: string(26) "-9,223,372,036,854,775,808"
@@ -63,7 +63,7 @@ foreach ($longVals as $longVal) {
6363
... with precision -17: string(26) "-9,200,000,000,000,000,000"
6464
... with precision -19: string(27) "-10,000,000,000,000,000,000"
6565
... with precision -20: string(1) "0"
66-
... with precision %i: string(1) "0"
66+
... with precision -9223372036854775808: string(1) "0"
6767
--- testing: int(2147483647)
6868
... with precision 5: string(19) "2,147,483,647.00000"
6969
... with precision 0: string(13) "2,147,483,647"
@@ -74,7 +74,7 @@ foreach ($longVals as $longVal) {
7474
... with precision -17: string(1) "0"
7575
... with precision -19: string(1) "0"
7676
... with precision -20: string(1) "0"
77-
... with precision %i: string(1) "0"
77+
... with precision -9223372036854775808: string(1) "0"
7878
--- testing: int(-2147483648)
7979
... with precision 5: string(20) "-2,147,483,648.00000"
8080
... with precision 0: string(14) "-2,147,483,648"
@@ -85,7 +85,7 @@ foreach ($longVals as $longVal) {
8585
... with precision -17: string(1) "0"
8686
... with precision -19: string(1) "0"
8787
... with precision -20: string(1) "0"
88-
... with precision %i: string(1) "0"
88+
... with precision -9223372036854775808: string(1) "0"
8989
--- testing: int(9223372034707292160)
9090
... with precision 5: string(31) "9,223,372,034,707,292,160.00000"
9191
... with precision 0: string(25) "9,223,372,034,707,292,160"
@@ -96,7 +96,7 @@ foreach ($longVals as $longVal) {
9696
... with precision -17: string(25) "9,200,000,000,000,000,000"
9797
... with precision -19: string(26) "10,000,000,000,000,000,000"
9898
... with precision -20: string(1) "0"
99-
... with precision %i: string(1) "0"
99+
... with precision -9223372036854775808: string(1) "0"
100100
--- testing: int(-9223372034707292160)
101101
... with precision 5: string(32) "-9,223,372,034,707,292,160.00000"
102102
... with precision 0: string(26) "-9,223,372,034,707,292,160"
@@ -107,7 +107,7 @@ foreach ($longVals as $longVal) {
107107
... with precision -17: string(26) "-9,200,000,000,000,000,000"
108108
... with precision -19: string(27) "-10,000,000,000,000,000,000"
109109
... with precision -20: string(1) "0"
110-
... with precision %i: string(1) "0"
110+
... with precision -9223372036854775808: string(1) "0"
111111
--- testing: int(2147483648)
112112
... with precision 5: string(19) "2,147,483,648.00000"
113113
... with precision 0: string(13) "2,147,483,648"
@@ -118,7 +118,7 @@ foreach ($longVals as $longVal) {
118118
... with precision -17: string(1) "0"
119119
... with precision -19: string(1) "0"
120120
... with precision -20: string(1) "0"
121-
... with precision %i: string(1) "0"
121+
... with precision -9223372036854775808: string(1) "0"
122122
--- testing: int(-2147483649)
123123
... with precision 5: string(20) "-2,147,483,649.00000"
124124
... with precision 0: string(14) "-2,147,483,649"
@@ -129,7 +129,7 @@ foreach ($longVals as $longVal) {
129129
... with precision -17: string(1) "0"
130130
... with precision -19: string(1) "0"
131131
... with precision -20: string(1) "0"
132-
... with precision %i: string(1) "0"
132+
... with precision -9223372036854775808: string(1) "0"
133133
--- testing: int(4294967294)
134134
... with precision 5: string(19) "4,294,967,294.00000"
135135
... with precision 0: string(13) "4,294,967,294"
@@ -140,7 +140,7 @@ foreach ($longVals as $longVal) {
140140
... with precision -17: string(1) "0"
141141
... with precision -19: string(1) "0"
142142
... with precision -20: string(1) "0"
143-
... with precision %i: string(1) "0"
143+
... with precision -9223372036854775808: string(1) "0"
144144
--- testing: int(4294967295)
145145
... with precision 5: string(19) "4,294,967,295.00000"
146146
... with precision 0: string(13) "4,294,967,295"
@@ -151,7 +151,7 @@ foreach ($longVals as $longVal) {
151151
... with precision -17: string(1) "0"
152152
... with precision -19: string(1) "0"
153153
... with precision -20: string(1) "0"
154-
... with precision %i: string(1) "0"
154+
... with precision -9223372036854775808: string(1) "0"
155155
--- testing: int(4294967293)
156156
... with precision 5: string(19) "4,294,967,293.00000"
157157
... with precision 0: string(13) "4,294,967,293"
@@ -162,7 +162,7 @@ foreach ($longVals as $longVal) {
162162
... with precision -17: string(1) "0"
163163
... with precision -19: string(1) "0"
164164
... with precision -20: string(1) "0"
165-
... with precision %i: string(1) "0"
165+
... with precision -9223372036854775808: string(1) "0"
166166
--- testing: int(9223372036854775806)
167167
... with precision 5: string(31) "9,223,372,036,854,775,806.00000"
168168
... with precision 0: string(25) "9,223,372,036,854,775,806"
@@ -173,7 +173,18 @@ foreach ($longVals as $longVal) {
173173
... with precision -17: string(25) "9,200,000,000,000,000,000"
174174
... with precision -19: string(26) "10,000,000,000,000,000,000"
175175
... with precision -20: string(1) "0"
176-
... with precision %i: string(1) "0"
176+
... with precision -9223372036854775808: string(1) "0"
177+
--- testing: float(9.223372036854776E+18)
178+
... with precision 5: string(31) "9,223,372,036,854,775,808.00000"
179+
... with precision 0: string(25) "9,223,372,036,854,775,808"
180+
... with precision -1: string(25) "9,223,372,036,854,775,808"
181+
... with precision -5: string(25) "9,223,372,036,854,800,384"
182+
... with precision -10: string(25) "9,223,372,040,000,000,000"
183+
... with precision -11: string(25) "9,223,372,000,000,000,000"
184+
... with precision -17: string(25) "9,200,000,000,000,000,000"
185+
... with precision -19: string(26) "10,000,000,000,000,000,000"
186+
... with precision -20: string(1) "0"
187+
... with precision -9223372036854775808: string(1) "0"
177188
--- testing: int(-9223372036854775807)
178189
... with precision 5: string(32) "-9,223,372,036,854,775,807.00000"
179190
... with precision 0: string(26) "-9,223,372,036,854,775,807"
@@ -184,4 +195,15 @@ foreach ($longVals as $longVal) {
184195
... with precision -17: string(26) "-9,200,000,000,000,000,000"
185196
... with precision -19: string(27) "-10,000,000,000,000,000,000"
186197
... with precision -20: string(1) "0"
187-
... with precision %i: string(1) "0"
198+
... with precision -9223372036854775808: string(1) "0"
199+
--- testing: float(-9.223372036854776E+18)
200+
... with precision 5: string(32) "-9,223,372,036,854,775,808.00000"
201+
... with precision 0: string(26) "-9,223,372,036,854,775,808"
202+
... with precision -1: string(26) "-9,223,372,036,854,775,808"
203+
... with precision -5: string(26) "-9,223,372,036,854,800,384"
204+
... with precision -10: string(26) "-9,223,372,040,000,000,000"
205+
... with precision -11: string(26) "-9,223,372,000,000,000,000"
206+
... with precision -17: string(26) "-9,200,000,000,000,000,000"
207+
... with precision -19: string(27) "-10,000,000,000,000,000,000"
208+
... with precision -20: string(1) "0"
209+
... with precision -9223372036854775808: string(1) "0"

0 commit comments

Comments
 (0)