Skip to content

Commit 89e383d

Browse files
committed
ext/bcmath: bounds-check $precision in bcround() and Number::round()
Fix ASAN issue withh entry points passed $precision to bc_round() unchecked, allowing PHP_INT_MAX / PHP_INT_MIN to trigger oversized allocations and signed overflow in libbcmath/src/round.c.
1 parent be41c36 commit 89e383d

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

ext/bcmath/bcmath.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ static zend_always_inline zend_result bcmath_check_scale(zend_long scale, uint32
155155
return SUCCESS;
156156
}
157157

158+
static zend_always_inline zend_result bcmath_check_precision(zend_long precision, uint32_t arg_num)
159+
{
160+
if (UNEXPECTED(precision < -(zend_long) INT_MAX || precision > INT_MAX)) {
161+
zend_argument_value_error(arg_num, "must be between %d and %d", -INT_MAX, INT_MAX);
162+
return FAILURE;
163+
}
164+
return SUCCESS;
165+
}
166+
158167
static void php_long2num(bc_num *num, zend_long lval)
159168
{
160169
*num = bc_long2num(lval);
@@ -795,6 +804,10 @@ PHP_FUNCTION(bcround)
795804
Z_PARAM_ENUM(rounding_mode, rounding_mode_ce)
796805
ZEND_PARSE_PARAMETERS_END();
797806

807+
if (bcmath_check_precision(precision, 2) == FAILURE) {
808+
RETURN_THROWS();
809+
}
810+
798811
switch (rounding_mode) {
799812
case ZEND_ENUM_RoundingMode_HalfAwayFromZero:
800813
case ZEND_ENUM_RoundingMode_HalfTowardsZero:
@@ -1794,6 +1807,10 @@ PHP_METHOD(BcMath_Number, round)
17941807
Z_PARAM_ENUM(rounding_mode, rounding_mode_ce);
17951808
ZEND_PARSE_PARAMETERS_END();
17961809

1810+
if (bcmath_check_precision(precision, 1) == FAILURE) {
1811+
RETURN_THROWS();
1812+
}
1813+
17971814
switch (rounding_mode) {
17981815
case ZEND_ENUM_RoundingMode_HalfAwayFromZero:
17991816
case ZEND_ENUM_RoundingMode_HalfTowardsZero:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
bcround() and BcMath\Number::round() reject out-of-range $precision
3+
--EXTENSIONS--
4+
bcmath
5+
--FILE--
6+
<?php
7+
try {
8+
bcround('1', PHP_INT_MAX);
9+
} catch (\ValueError $e) {
10+
echo $e->getMessage() . \PHP_EOL;
11+
}
12+
try {
13+
bcround('12345', -PHP_INT_MAX, RoundingMode::AwayFromZero);
14+
} catch (\ValueError $e) {
15+
echo $e->getMessage() . \PHP_EOL;
16+
}
17+
try {
18+
bcround('12345', PHP_INT_MIN, RoundingMode::AwayFromZero);
19+
} catch (\ValueError $e) {
20+
echo $e->getMessage() . \PHP_EOL;
21+
}
22+
try {
23+
(new BcMath\Number('1'))->round(PHP_INT_MAX);
24+
} catch (\ValueError $e) {
25+
echo $e->getMessage() . \PHP_EOL;
26+
}
27+
?>
28+
--EXPECT--
29+
bcround(): Argument #2 ($precision) must be between -2147483647 and 2147483647
30+
bcround(): Argument #2 ($precision) must be between -2147483647 and 2147483647
31+
bcround(): Argument #2 ($precision) must be between -2147483647 and 2147483647
32+
BcMath\Number::round(): Argument #1 ($precision) must be between -2147483647 and 2147483647

0 commit comments

Comments
 (0)