Skip to content

Commit b64aee9

Browse files
vv12131415nikic
authored andcommitted
Ensure bcmath scale is between 0 and INT_MAX
Make sure bcmatch scale is between 0 and INT_MAX, both for the ini setting, and all the functions accepting a scale argument. A ValueError is thrown if a function argument is out of range. Closes GH-5455.
1 parent 48a34bc commit b64aee9

File tree

6 files changed

+177
-36
lines changed

6 files changed

+177
-36
lines changed

ext/bcmath/bcmath.c

+85-27
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ zend_module_entry bcmath_module_entry = {
4545
PHP_BCMATH_VERSION,
4646
PHP_MODULE_GLOBALS(bcmath),
4747
PHP_GINIT(bcmath),
48-
PHP_GSHUTDOWN(bcmath),
48+
PHP_GSHUTDOWN(bcmath),
4949
NULL,
5050
STANDARD_MODULE_PROPERTIES_EX
5151
};
@@ -57,9 +57,25 @@ ZEND_TSRMLS_CACHE_DEFINE()
5757
ZEND_GET_MODULE(bcmath)
5858
#endif
5959

60+
ZEND_INI_MH(OnUpdateScale)
61+
{
62+
int *p;
63+
zend_long tmp;
64+
65+
tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
66+
if (tmp < 0 || tmp > INT_MAX) {
67+
return FAILURE;
68+
}
69+
70+
p = (int *) ZEND_INI_GET_ADDR();
71+
*p = (int) tmp;
72+
73+
return SUCCESS;
74+
}
75+
6076
/* {{{ PHP_INI */
6177
PHP_INI_BEGIN()
62-
STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals)
78+
STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateScale, bc_precision, zend_bcmath_globals, bcmath_globals)
6379
PHP_INI_END()
6480
/* }}} */
6581

@@ -142,7 +158,7 @@ PHP_FUNCTION(bcadd)
142158
zend_string *left, *right;
143159
zend_long scale_param = 0;
144160
bc_num first, second, result;
145-
int scale = (int)BCG(bc_precision);
161+
int scale = BCG(bc_precision);
146162

147163
ZEND_PARSE_PARAMETERS_START(2, 3)
148164
Z_PARAM_STR(left)
@@ -152,7 +168,11 @@ PHP_FUNCTION(bcadd)
152168
ZEND_PARSE_PARAMETERS_END();
153169

154170
if (ZEND_NUM_ARGS() == 3) {
155-
scale = (int) (scale_param < 0 ? 0 : scale_param);
171+
if (scale_param < 0 || scale_param > INT_MAX) {
172+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
173+
RETURN_THROWS();
174+
}
175+
scale = (int) scale_param;
156176
}
157177

158178
bc_init_num(&first);
@@ -177,7 +197,7 @@ PHP_FUNCTION(bcsub)
177197
zend_string *left, *right;
178198
zend_long scale_param = 0;
179199
bc_num first, second, result;
180-
int scale = (int)BCG(bc_precision);
200+
int scale = BCG(bc_precision);
181201

182202
ZEND_PARSE_PARAMETERS_START(2, 3)
183203
Z_PARAM_STR(left)
@@ -187,7 +207,11 @@ PHP_FUNCTION(bcsub)
187207
ZEND_PARSE_PARAMETERS_END();
188208

189209
if (ZEND_NUM_ARGS() == 3) {
190-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
210+
if (scale_param < 0 || scale_param > INT_MAX) {
211+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
212+
RETURN_THROWS();
213+
}
214+
scale = (int) scale_param;
191215
}
192216

193217
bc_init_num(&first);
@@ -212,7 +236,7 @@ PHP_FUNCTION(bcmul)
212236
zend_string *left, *right;
213237
zend_long scale_param = 0;
214238
bc_num first, second, result;
215-
int scale = (int)BCG(bc_precision);
239+
int scale = BCG(bc_precision);
216240

217241
ZEND_PARSE_PARAMETERS_START(2, 3)
218242
Z_PARAM_STR(left)
@@ -222,7 +246,11 @@ PHP_FUNCTION(bcmul)
222246
ZEND_PARSE_PARAMETERS_END();
223247

224248
if (ZEND_NUM_ARGS() == 3) {
225-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
249+
if (scale_param < 0 || scale_param > INT_MAX) {
250+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
251+
RETURN_THROWS();
252+
}
253+
scale = (int) scale_param;
226254
}
227255

228256
bc_init_num(&first);
@@ -247,7 +275,7 @@ PHP_FUNCTION(bcdiv)
247275
zend_string *left, *right;
248276
zend_long scale_param = 0;
249277
bc_num first, second, result;
250-
int scale = (int)BCG(bc_precision);
278+
int scale = BCG(bc_precision);
251279

252280
ZEND_PARSE_PARAMETERS_START(2, 3)
253281
Z_PARAM_STR(left)
@@ -257,7 +285,11 @@ PHP_FUNCTION(bcdiv)
257285
ZEND_PARSE_PARAMETERS_END();
258286

259287
if (ZEND_NUM_ARGS() == 3) {
260-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
288+
if (scale_param < 0 || scale_param > INT_MAX) {
289+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
290+
RETURN_THROWS();
291+
}
292+
scale = (int) scale_param;
261293
}
262294

263295
bc_init_num(&first);
@@ -289,7 +321,7 @@ PHP_FUNCTION(bcmod)
289321
zend_string *left, *right;
290322
zend_long scale_param = 0;
291323
bc_num first, second, result;
292-
int scale = (int)BCG(bc_precision);
324+
int scale = BCG(bc_precision);
293325

294326
ZEND_PARSE_PARAMETERS_START(2, 3)
295327
Z_PARAM_STR(left)
@@ -299,7 +331,11 @@ PHP_FUNCTION(bcmod)
299331
ZEND_PARSE_PARAMETERS_END();
300332

301333
if (ZEND_NUM_ARGS() == 3) {
302-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
334+
if (scale_param < 0 || scale_param > INT_MAX) {
335+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
336+
RETURN_THROWS();
337+
}
338+
scale = (int) scale_param;
303339
}
304340

305341
bc_init_num(&first);
@@ -329,18 +365,26 @@ PHP_FUNCTION(bcmod)
329365
PHP_FUNCTION(bcpowmod)
330366
{
331367
zend_string *left, *right, *modulus;
368+
zend_long scale_param = 0;
332369
bc_num first, second, mod, result;
333-
zend_long scale = BCG(bc_precision);
334-
int scale_int;
370+
int scale = BCG(bc_precision);
335371

336372
ZEND_PARSE_PARAMETERS_START(3, 4)
337373
Z_PARAM_STR(left)
338374
Z_PARAM_STR(right)
339375
Z_PARAM_STR(modulus)
340376
Z_PARAM_OPTIONAL
341-
Z_PARAM_LONG(scale)
377+
Z_PARAM_LONG(scale_param)
342378
ZEND_PARSE_PARAMETERS_END();
343379

380+
if (ZEND_NUM_ARGS() == 4) {
381+
if (scale_param < 0 || scale_param > INT_MAX) {
382+
zend_argument_value_error(4, "must be between 0 and %d", INT_MAX);
383+
RETURN_THROWS();
384+
}
385+
scale = (int) scale_param;
386+
}
387+
344388
bc_init_num(&first);
345389
bc_init_num(&second);
346390
bc_init_num(&mod);
@@ -349,10 +393,8 @@ PHP_FUNCTION(bcpowmod)
349393
php_str2num(&second, ZSTR_VAL(right));
350394
php_str2num(&mod, ZSTR_VAL(modulus));
351395

352-
scale_int = (int) ((int)scale < 0 ? 0 : scale);
353-
354-
if (bc_raisemod(first, second, mod, &result, scale_int) != -1) {
355-
RETVAL_STR(bc_num2str_ex(result, scale_int));
396+
if (bc_raisemod(first, second, mod, &result, scale) != -1) {
397+
RETVAL_STR(bc_num2str_ex(result, scale));
356398
} else {
357399
RETVAL_FALSE;
358400
}
@@ -372,7 +414,7 @@ PHP_FUNCTION(bcpow)
372414
zend_string *left, *right;
373415
zend_long scale_param = 0;
374416
bc_num first, second, result;
375-
int scale = (int)BCG(bc_precision);
417+
int scale = BCG(bc_precision);
376418

377419
ZEND_PARSE_PARAMETERS_START(2, 3)
378420
Z_PARAM_STR(left)
@@ -382,7 +424,11 @@ PHP_FUNCTION(bcpow)
382424
ZEND_PARSE_PARAMETERS_END();
383425

384426
if (ZEND_NUM_ARGS() == 3) {
385-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
427+
if (scale_param < 0 || scale_param > INT_MAX) {
428+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
429+
RETURN_THROWS();
430+
}
431+
scale = (int) scale_param;
386432
}
387433

388434
bc_init_num(&first);
@@ -407,7 +453,7 @@ PHP_FUNCTION(bcsqrt)
407453
zend_string *left;
408454
zend_long scale_param = 0;
409455
bc_num result;
410-
int scale = (int)BCG(bc_precision);
456+
int scale = BCG(bc_precision);
411457

412458
ZEND_PARSE_PARAMETERS_START(1, 2)
413459
Z_PARAM_STR(left)
@@ -416,7 +462,11 @@ PHP_FUNCTION(bcsqrt)
416462
ZEND_PARSE_PARAMETERS_END();
417463

418464
if (ZEND_NUM_ARGS() == 2) {
419-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
465+
if (scale_param < 0 || scale_param > INT_MAX) {
466+
zend_argument_value_error(2, "must be between 0 and %d", INT_MAX);
467+
RETURN_THROWS();
468+
}
469+
scale = (int) scale_param;
420470
}
421471

422472
bc_init_num(&result);
@@ -440,7 +490,7 @@ PHP_FUNCTION(bccomp)
440490
zend_string *left, *right;
441491
zend_long scale_param = 0;
442492
bc_num first, second;
443-
int scale = (int)BCG(bc_precision);
493+
int scale = BCG(bc_precision);
444494

445495
ZEND_PARSE_PARAMETERS_START(2, 3)
446496
Z_PARAM_STR(left)
@@ -450,7 +500,11 @@ PHP_FUNCTION(bccomp)
450500
ZEND_PARSE_PARAMETERS_END();
451501

452502
if (ZEND_NUM_ARGS() == 3) {
453-
scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
503+
if (scale_param < 0 || scale_param > INT_MAX) {
504+
zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
505+
RETURN_THROWS();
506+
}
507+
scale = (int) scale_param;
454508
}
455509

456510
bc_init_num(&first);
@@ -460,7 +514,7 @@ PHP_FUNCTION(bccomp)
460514
php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
461515
}
462516
if (!bc_str2num(&second, ZSTR_VAL(right), scale)) {
463-
php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
517+
php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
464518
}
465519
RETVAL_LONG(bc_compare(first, second));
466520

@@ -484,7 +538,11 @@ PHP_FUNCTION(bcscale)
484538
old_scale = BCG(bc_precision);
485539

486540
if (ZEND_NUM_ARGS() == 1) {
487-
BCG(bc_precision) = ((int)new_scale < 0) ? 0 : new_scale;
541+
if (new_scale < 0 || new_scale > INT_MAX) {
542+
zend_argument_value_error(1, "must be between 0 and %d", INT_MAX);
543+
RETURN_THROWS();
544+
}
545+
BCG(bc_precision) = (int) new_scale;
488546
}
489547

490548
RETURN_LONG(old_scale);

ext/bcmath/php_bcmath.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ZEND_BEGIN_MODULE_GLOBALS(bcmath)
3333
bc_num _zero_;
3434
bc_num _one_;
3535
bc_num _two_;
36-
zend_long bc_precision;
36+
int bc_precision;
3737
ZEND_END_MODULE_GLOBALS(bcmath)
3838

3939
#if defined(ZTS) && defined(COMPILE_DL_BCMATH)
+7-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
--TEST--
2-
bcscale() with negative argument
2+
bcscale() fails with negative argument
33
--SKIPIF--
44
<?php if(!extension_loaded("bcmath")) print "skip"; ?>
55
--INI--
66
bcmath.scale=0
77
--FILE--
88
<?php
9-
bcscale(-4);
109
echo bcdiv("20.56", "4");
10+
try {
11+
bcscale(-4);
12+
} catch (\ValueError $e) {
13+
echo \PHP_EOL . $e->getMessage() . \PHP_EOL;
14+
}
1115
?>
1216
--EXPECT--
1317
5
18+
bcscale(): Argument #1 ($scale) must be between 0 and 2147483647

ext/bcmath/tests/bug60377.phpt

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ bcscale related problem on 64bits platforms
55
if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
66
--FILE--
77
<?php
8-
$var48 = bcscale(634314234334311);
8+
try {
9+
$var48 = bcscale(634314234334311);
10+
} catch (\ValueError $e) {
11+
echo $e->getMessage() . \PHP_EOL;
12+
}
913
$var67 = bcsqrt(0);
1014
$var414 = bcadd(0,-1,10);
11-
die('ALIVE');
1215
?>
16+
1317
--EXPECT--
14-
ALIVE
18+
bcscale(): Argument #1 ($scale) must be between 0 and 2147483647

ext/bcmath/tests/bug72093.phpt

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
--TEST--
2-
Bug 72093: bcpowmod accepts negative scale and corrupts _one_ definition
2+
Bug 72093: bcpowmod fails on negative scale and corrupts _one_ definition
33
--SKIPIF--
44
<?php
55
if(!extension_loaded("bcmath")) print "skip";
66
?>
77
--FILE--
88
<?php
9-
var_dump(bcpowmod(1, 0, 128, -200));
9+
try {
10+
var_dump(bcpowmod(1, 0, 128, -200));
11+
} catch (\ValueError $e) {
12+
echo $e->getMessage() . \PHP_EOL;
13+
}
1014
var_dump(bcpowmod(1, 1.2, 1, 1));
1115
?>
1216
--EXPECTF--
13-
string(1) "1"
17+
bcpowmod(): Argument #4 ($scale) must be between 0 and 2147483647
1418

1519
Warning: bcpowmod(): Non-zero scale in exponent in %s on line %d
1620
string(3) "0.0"

0 commit comments

Comments
 (0)