]> granicus.if.org Git - php/commitdiff
Ensure bcmath scale is between 0 and INT_MAX
authorVladyslav Startsev <17382248+vladyslavstartsev@users.noreply.github.com>
Fri, 24 Apr 2020 23:18:09 +0000 (02:18 +0300)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 27 Apr 2020 09:53:26 +0000 (11:53 +0200)
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
ext/bcmath/php_bcmath.h
ext/bcmath/tests/bcscale_variation001.phpt
ext/bcmath/tests/bug60377.phpt
ext/bcmath/tests/bug72093.phpt
ext/bcmath/tests/negative_scale.phpt [new file with mode: 0644]

index 8c46ee0f9344e7659e8a3f248085fb7201bdb679..61ee95b2118a2f32e190a44237640fc5d3e651a4 100644 (file)
@@ -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);
index bafaf29e7a8eb9cbc03cb5b314ec6255043af23a..c7a8a85d735e2ce90c1a6681fcbf844d179ae031 100644 (file)
@@ -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)
index 51c6767bd47bbfdbff7c1b3772f945c0c1d0903a..0718d724c2ae2bde7faf91e4f1d1c1afeb065ab1 100644 (file)
@@ -1,13 +1,18 @@
 --TEST--
-bcscale() with negative argument
+bcscale() fails with negative argument
 --SKIPIF--
 <?php if(!extension_loaded("bcmath")) print "skip"; ?>
 --INI--
 bcmath.scale=0
 --FILE--
 <?php
-bcscale(-4);
 echo bcdiv("20.56", "4");
+try {
+    bcscale(-4);
+} catch (\ValueError $e) {
+    echo \PHP_EOL . $e->getMessage() . \PHP_EOL;
+}
 ?>
 --EXPECT--
 5
+bcscale(): Argument #1 ($scale) must be between 0 and 2147483647
index eb140d92cf718673dbd8f96b6ed829d91bcb4046..6caf7d466182a5b6f991d939fec2033e4e6b38bd 100644 (file)
@@ -5,10 +5,14 @@ bcscale related problem on 64bits platforms
 if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
 --FILE--
 <?php
-$var48 = bcscale(634314234334311);
+try {
+    $var48 = bcscale(634314234334311);
+} catch (\ValueError $e) {
+    echo $e->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
index 235a4e04a3e05e5242d0e2445a8a5d15897d0124..7111bf6e3aca8eb9883d01067579d7e219c61c7c 100644 (file)
@@ -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--
 <?php
 if(!extension_loaded("bcmath")) print "skip";
 ?>
 --FILE--
 <?php
-var_dump(bcpowmod(1, 0, 128, -200));
+try {
+    var_dump(bcpowmod(1, 0, 128, -200));
+} catch (\ValueError $e) {
+    echo $e->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 (file)
index 0000000..96d1e1b
--- /dev/null
@@ -0,0 +1,70 @@
+--TEST--
+all errors on negative scale
+--SKIPIF--
+<?php if(!extension_loaded("bcmath")) print "skip"; ?>
+--INI--
+bcmath.scale=0
+--FILE--
+<?php
+try {
+    bcadd('1','2',-1);
+} catch (\ValueError $e) {
+    echo $e->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