From b64aee97069fc77c141c787e5408d9e12f5971b4 Mon Sep 17 00:00:00 2001 From: Vladyslav Startsev <17382248+vladyslavstartsev@users.noreply.github.com> Date: Sat, 25 Apr 2020 02:18:09 +0300 Subject: [PATCH] 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. --- ext/bcmath/bcmath.c | 112 ++++++++++++++++----- ext/bcmath/php_bcmath.h | 2 +- ext/bcmath/tests/bcscale_variation001.phpt | 9 +- ext/bcmath/tests/bug60377.phpt | 10 +- ext/bcmath/tests/bug72093.phpt | 10 +- ext/bcmath/tests/negative_scale.phpt | 70 +++++++++++++ 6 files changed, 177 insertions(+), 36 deletions(-) create mode 100644 ext/bcmath/tests/negative_scale.phpt diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index 8c46ee0f934..61ee95b2118 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -45,7 +45,7 @@ zend_module_entry bcmath_module_entry = { PHP_BCMATH_VERSION, PHP_MODULE_GLOBALS(bcmath), PHP_GINIT(bcmath), - PHP_GSHUTDOWN(bcmath), + PHP_GSHUTDOWN(bcmath), NULL, STANDARD_MODULE_PROPERTIES_EX }; @@ -57,9 +57,25 @@ ZEND_TSRMLS_CACHE_DEFINE() ZEND_GET_MODULE(bcmath) #endif +ZEND_INI_MH(OnUpdateScale) +{ + int *p; + zend_long tmp; + + tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + if (tmp < 0 || tmp > INT_MAX) { + return FAILURE; + } + + p = (int *) ZEND_INI_GET_ADDR(); + *p = (int) tmp; + + return SUCCESS; +} + /* {{{ PHP_INI */ PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals) + STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateScale, bc_precision, zend_bcmath_globals, bcmath_globals) PHP_INI_END() /* }}} */ @@ -142,7 +158,7 @@ PHP_FUNCTION(bcadd) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second, result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -152,7 +168,11 @@ PHP_FUNCTION(bcadd) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) (scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -177,7 +197,7 @@ PHP_FUNCTION(bcsub) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second, result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -187,7 +207,11 @@ PHP_FUNCTION(bcsub) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -212,7 +236,7 @@ PHP_FUNCTION(bcmul) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second, result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -222,7 +246,11 @@ PHP_FUNCTION(bcmul) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -247,7 +275,7 @@ PHP_FUNCTION(bcdiv) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second, result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -257,7 +285,11 @@ PHP_FUNCTION(bcdiv) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -289,7 +321,7 @@ PHP_FUNCTION(bcmod) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second, result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -299,7 +331,11 @@ PHP_FUNCTION(bcmod) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -329,18 +365,26 @@ PHP_FUNCTION(bcmod) PHP_FUNCTION(bcpowmod) { zend_string *left, *right, *modulus; + zend_long scale_param = 0; bc_num first, second, mod, result; - zend_long scale = BCG(bc_precision); - int scale_int; + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STR(left) Z_PARAM_STR(right) Z_PARAM_STR(modulus) Z_PARAM_OPTIONAL - Z_PARAM_LONG(scale) + Z_PARAM_LONG(scale_param) ZEND_PARSE_PARAMETERS_END(); + if (ZEND_NUM_ARGS() == 4) { + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(4, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; + } + bc_init_num(&first); bc_init_num(&second); bc_init_num(&mod); @@ -349,10 +393,8 @@ PHP_FUNCTION(bcpowmod) php_str2num(&second, ZSTR_VAL(right)); php_str2num(&mod, ZSTR_VAL(modulus)); - scale_int = (int) ((int)scale < 0 ? 0 : scale); - - if (bc_raisemod(first, second, mod, &result, scale_int) != -1) { - RETVAL_STR(bc_num2str_ex(result, scale_int)); + if (bc_raisemod(first, second, mod, &result, scale) != -1) { + RETVAL_STR(bc_num2str_ex(result, scale)); } else { RETVAL_FALSE; } @@ -372,7 +414,7 @@ PHP_FUNCTION(bcpow) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second, result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -382,7 +424,11 @@ PHP_FUNCTION(bcpow) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -407,7 +453,7 @@ PHP_FUNCTION(bcsqrt) zend_string *left; zend_long scale_param = 0; bc_num result; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(left) @@ -416,7 +462,11 @@ PHP_FUNCTION(bcsqrt) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 2) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(2, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&result); @@ -440,7 +490,7 @@ PHP_FUNCTION(bccomp) zend_string *left, *right; zend_long scale_param = 0; bc_num first, second; - int scale = (int)BCG(bc_precision); + int scale = BCG(bc_precision); ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_STR(left) @@ -450,7 +500,11 @@ PHP_FUNCTION(bccomp) ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() == 3) { - scale = (int) ((int)scale_param < 0 ? 0 : scale_param); + if (scale_param < 0 || scale_param > INT_MAX) { + zend_argument_value_error(3, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + scale = (int) scale_param; } bc_init_num(&first); @@ -460,7 +514,7 @@ PHP_FUNCTION(bccomp) php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed"); } if (!bc_str2num(&second, ZSTR_VAL(right), scale)) { - php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed"); + php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed"); } RETVAL_LONG(bc_compare(first, second)); @@ -484,7 +538,11 @@ PHP_FUNCTION(bcscale) old_scale = BCG(bc_precision); if (ZEND_NUM_ARGS() == 1) { - BCG(bc_precision) = ((int)new_scale < 0) ? 0 : new_scale; + if (new_scale < 0 || new_scale > INT_MAX) { + zend_argument_value_error(1, "must be between 0 and %d", INT_MAX); + RETURN_THROWS(); + } + BCG(bc_precision) = (int) new_scale; } RETURN_LONG(old_scale); diff --git a/ext/bcmath/php_bcmath.h b/ext/bcmath/php_bcmath.h index bafaf29e7a8..c7a8a85d735 100644 --- a/ext/bcmath/php_bcmath.h +++ b/ext/bcmath/php_bcmath.h @@ -33,7 +33,7 @@ ZEND_BEGIN_MODULE_GLOBALS(bcmath) bc_num _zero_; bc_num _one_; bc_num _two_; - zend_long bc_precision; + int bc_precision; ZEND_END_MODULE_GLOBALS(bcmath) #if defined(ZTS) && defined(COMPILE_DL_BCMATH) diff --git a/ext/bcmath/tests/bcscale_variation001.phpt b/ext/bcmath/tests/bcscale_variation001.phpt index 51c6767bd47..0718d724c2a 100644 --- a/ext/bcmath/tests/bcscale_variation001.phpt +++ b/ext/bcmath/tests/bcscale_variation001.phpt @@ -1,13 +1,18 @@ --TEST-- -bcscale() with negative argument +bcscale() fails with negative argument --SKIPIF-- --INI-- bcmath.scale=0 --FILE-- getMessage() . \PHP_EOL; +} ?> --EXPECT-- 5 +bcscale(): Argument #1 ($scale) must be between 0 and 2147483647 diff --git a/ext/bcmath/tests/bug60377.phpt b/ext/bcmath/tests/bug60377.phpt index eb140d92cf7..6caf7d46618 100644 --- a/ext/bcmath/tests/bug60377.phpt +++ b/ext/bcmath/tests/bug60377.phpt @@ -5,10 +5,14 @@ bcscale related problem on 64bits platforms if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?> --FILE-- getMessage() . \PHP_EOL; +} $var67 = bcsqrt(0); $var414 = bcadd(0,-1,10); -die('ALIVE'); ?> + --EXPECT-- -ALIVE +bcscale(): Argument #1 ($scale) must be between 0 and 2147483647 diff --git a/ext/bcmath/tests/bug72093.phpt b/ext/bcmath/tests/bug72093.phpt index 235a4e04a3e..7111bf6e3ac 100644 --- a/ext/bcmath/tests/bug72093.phpt +++ b/ext/bcmath/tests/bug72093.phpt @@ -1,16 +1,20 @@ --TEST-- -Bug 72093: bcpowmod accepts negative scale and corrupts _one_ definition +Bug 72093: bcpowmod fails on negative scale and corrupts _one_ definition --SKIPIF-- --FILE-- getMessage() . \PHP_EOL; +} var_dump(bcpowmod(1, 1.2, 1, 1)); ?> --EXPECTF-- -string(1) "1" +bcpowmod(): Argument #4 ($scale) must be between 0 and 2147483647 Warning: bcpowmod(): Non-zero scale in exponent in %s on line %d string(3) "0.0" diff --git a/ext/bcmath/tests/negative_scale.phpt b/ext/bcmath/tests/negative_scale.phpt new file mode 100644 index 00000000000..96d1e1b600f --- /dev/null +++ b/ext/bcmath/tests/negative_scale.phpt @@ -0,0 +1,70 @@ +--TEST-- +all errors on negative scale +--SKIPIF-- + +--INI-- +bcmath.scale=0 +--FILE-- +getMessage() . \PHP_EOL; +} +try { + bcsub('1','2',-1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcmul('1','2',-1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcdiv('1','2',-1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcmod('1','2',-1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcpowmod('1', '2', '3', -9); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcpow('1', '2', -1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcsqrt('9', -1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bccomp('1', '2', -1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + bcscale(-1); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +?> +--EXPECT-- +bcadd(): Argument #3 ($scale) must be between 0 and 2147483647 +bcsub(): Argument #3 ($scale) must be between 0 and 2147483647 +bcmul(): Argument #3 ($scale) must be between 0 and 2147483647 +bcdiv(): Argument #3 ($scale) must be between 0 and 2147483647 +bcmod(): Argument #3 ($scale) must be between 0 and 2147483647 +bcpowmod(): Argument #4 ($scale) must be between 0 and 2147483647 +bcpow(): Argument #3 ($scale) must be between 0 and 2147483647 +bcsqrt(): Argument #2 ($scale) must be between 0 and 2147483647 +bccomp(): Argument #3 ($scale) must be between 0 and 2147483647 +bcscale(): Argument #1 ($scale) must be between 0 and 2147483647