Skip to content

Commit 617e4c3

Browse files
committed
ext/gmp: reject values larger than unsigned long in gmp_pow/binomial/root/rootrem and shift/pow operators.
Applies the gmp_fact() pattern (0236667) to the remaining zend_long -> gmp_ulong casts that could silently overflow on LLP64 platforms. gmp_powm() switches to zend_argument_error() with the correct argument index ($modulus) for modulo-by-zero. close phpGH-21812
1 parent c36c50e commit 617e4c3

12 files changed

Lines changed: 100 additions & 38 deletions

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ PHP NEWS
4343

4444
- GMP:
4545
. gmp_fact() reject values larger than unsigned long. (David Carlier)
46+
. gmp_pow/binomial/root/rootrem and shift/pow operators reject values
47+
larger than unsigned long. (David Carlier)
4648

4749
- Hash:
4850
. Upgrade xxHash to 0.8.2. (timwolla)

UPGRADING

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ PHP 8.6 UPGRADE NOTES
155155
- GMP:
156156
. gmp_fact() now throws a ValueError() if $num does not fit into
157157
a unsigned long.
158+
. gmp_pow(), gmp_binomial(), gmp_root() and gmp_rootrem() now throw a
159+
ValueError if their second argument does not fit into an unsigned long.
160+
. The shift (<<, >>) and exponentiation (**) operators on GMP objects
161+
now throw a ValueError if the right operand does not fit into an
162+
unsigned long.
163+
. gmp_powm() modulo-by-zero now raises a DivisionByZeroError whose
164+
message includes the function name and argument index ($modulus).
158165

159166
- mysqli:
160167
. The return structure of mysqli_get_charset() no longer contains

ext/gmp/gmp.c

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -356,10 +356,10 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
356356
shift = Z_LVAL_P(op2);
357357
}
358358

359-
if (shift < 0) {
359+
if (shift < 0 || shift > ULONG_MAX) {
360360
zend_throw_error(
361-
zend_ce_value_error, "%s must be greater than or equal to 0",
362-
opcode == ZEND_POW ? "Exponent" : "Shift"
361+
zend_ce_value_error, "%s must be between 0 and %lu",
362+
opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
363363
);
364364
ZVAL_UNDEF(return_value);
365365
return FAILURE;
@@ -1087,11 +1087,6 @@ ZEND_FUNCTION(gmp_fact)
10871087
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum)
10881088
ZEND_PARSE_PARAMETERS_END();
10891089

1090-
if (mpz_sgn(gmpnum) < 0) {
1091-
zend_argument_value_error(1, "must be greater than or equal to 0");
1092-
RETURN_THROWS();
1093-
}
1094-
10951090
if (!mpz_fits_ulong_p(gmpnum)) {
10961091
zend_argument_value_error(1, "must be between 0 and %lu", ULONG_MAX);
10971092
RETURN_THROWS();
@@ -1114,8 +1109,8 @@ ZEND_FUNCTION(gmp_binomial)
11141109
Z_PARAM_LONG(k)
11151110
ZEND_PARSE_PARAMETERS_END();
11161111

1117-
if (k < 0) {
1118-
zend_argument_value_error(2, "must be greater than or equal to 0");
1112+
if (k < 0 || k > ULONG_MAX) {
1113+
zend_argument_value_error(2, "must be between 0 and %lu", ULONG_MAX);
11191114
RETURN_THROWS();
11201115
}
11211116

@@ -1136,8 +1131,8 @@ ZEND_FUNCTION(gmp_pow)
11361131
Z_PARAM_LONG(exp)
11371132
ZEND_PARSE_PARAMETERS_END();
11381133

1139-
if (exp < 0) {
1140-
zend_argument_value_error(2, "must be greater than or equal to 0");
1134+
if (exp < 0 || exp > ULONG_MAX) {
1135+
zend_argument_value_error(2, "must be between 0 and %lu", ULONG_MAX);
11411136
RETURN_THROWS();
11421137
}
11431138

@@ -1163,7 +1158,7 @@ ZEND_FUNCTION(gmp_powm)
11631158
}
11641159

11651160
if (!mpz_cmp_ui(gmpnum_mod, 0)) {
1166-
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
1161+
zend_argument_error(zend_ce_division_by_zero_error, 3, "Modulo by zero");
11671162
RETURN_THROWS();
11681163
}
11691164

@@ -1226,8 +1221,8 @@ ZEND_FUNCTION(gmp_root)
12261221
Z_PARAM_LONG(nth)
12271222
ZEND_PARSE_PARAMETERS_END();
12281223

1229-
if (nth <= 0) {
1230-
zend_argument_value_error(2, "must be greater than 0");
1224+
if (nth <= 0 || nth > ULONG_MAX) {
1225+
zend_argument_value_error(2, "must be between 1 and %lu", ULONG_MAX);
12311226
RETURN_THROWS();
12321227
}
12331228

@@ -1253,8 +1248,8 @@ ZEND_FUNCTION(gmp_rootrem)
12531248
Z_PARAM_LONG(nth)
12541249
ZEND_PARSE_PARAMETERS_END();
12551250

1256-
if (nth <= 0) {
1257-
zend_argument_value_error(2, "must be greater than or equal to 1");
1251+
if (nth <= 0 || nth > ULONG_MAX) {
1252+
zend_argument_value_error(2, "must be between 1 and %lu", ULONG_MAX);
12581253
RETURN_THROWS();
12591254
}
12601255

ext/gmp/tests/gmp_binomial.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ try {
2626
echo $e->getMessage() . \PHP_EOL;
2727
}
2828
?>
29-
--EXPECT--
29+
--EXPECTF--
3030
object(GMP)#1 (1) {
3131
["num"]=>
3232
string(3) "252"
@@ -67,4 +67,4 @@ object(GMP)#2 (1) {
6767
["num"]=>
6868
string(1) "7"
6969
}
70-
gmp_binomial(): Argument #2 ($k) must be greater than or equal to 0
70+
gmp_binomial(): Argument #2 ($k) must be between 0 and %d

ext/gmp/tests/gmp_fact.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ try {
4545

4646
echo "Done\n";
4747
?>
48-
--EXPECT--
48+
--EXPECTF--
4949
string(1) "1"
5050
gmp_fact(): Argument #1 ($num) is not an integer string
5151
string(1) "1"
52-
gmp_fact(): Argument #1 ($num) must be greater than or equal to 0
53-
gmp_fact(): Argument #1 ($num) must be greater than or equal to 0
52+
gmp_fact(): Argument #1 ($num) must be between 0 and %d
53+
gmp_fact(): Argument #1 ($num) must be between 0 and %d
5454
string(19) "2432902008176640000"
5555
string(65) "30414093201713378043612608166064768844377641568960512000000000000"
5656
string(7) "3628800"
5757
string(1) "1"
5858
string(9) "479001600"
59-
gmp_fact(): Argument #1 ($num) must be greater than or equal to 0
59+
gmp_fact(): Argument #1 ($num) must be between 0 and %d
6060
gmp_fact(): Argument #1 ($num) must be of type GMP|string|int, array given
6161
Done
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
GMP functions reject values larger than unsigned long on LLP64
3+
--EXTENSIONS--
4+
gmp
5+
--SKIPIF--
6+
<?php
7+
if (PHP_OS_FAMILY !== "Windows" || PHP_INT_SIZE !== 8) die("skip LLP64 (Windows 64-bit) only");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
try {
13+
gmp_pow(2, PHP_INT_MAX);
14+
} catch (ValueError $e) {
15+
echo $e->getMessage() . PHP_EOL;
16+
}
17+
18+
try {
19+
gmp_binomial(10, PHP_INT_MAX);
20+
} catch (ValueError $e) {
21+
echo $e->getMessage() . PHP_EOL;
22+
}
23+
24+
try {
25+
gmp_root(10, PHP_INT_MAX);
26+
} catch (ValueError $e) {
27+
echo $e->getMessage() . PHP_EOL;
28+
}
29+
30+
try {
31+
gmp_rootrem(10, PHP_INT_MAX);
32+
} catch (ValueError $e) {
33+
echo $e->getMessage() . PHP_EOL;
34+
}
35+
36+
$n = gmp_init(2);
37+
try {
38+
$n << PHP_INT_MAX;
39+
} catch (ValueError $e) {
40+
echo $e->getMessage() . PHP_EOL;
41+
}
42+
43+
try {
44+
$n ** PHP_INT_MAX;
45+
} catch (ValueError $e) {
46+
echo $e->getMessage() . PHP_EOL;
47+
}
48+
49+
echo "Done\n";
50+
?>
51+
--EXPECTF--
52+
gmp_pow(): Argument #2 ($exponent) must be between 0 and %d
53+
gmp_binomial(): Argument #2 ($k) must be between 0 and %d
54+
gmp_root(): Argument #2 ($nth) must be between 1 and %d
55+
gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d
56+
Shift must be between 0 and %d
57+
Exponent must be between 0 and %d
58+
Done

ext/gmp/tests/gmp_pow.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ try {
4343

4444
echo "Done\n";
4545
?>
46-
--EXPECT--
46+
--EXPECTF--
4747
string(4) "1024"
4848
string(4) "1024"
4949
string(5) "-2048"
5050
string(4) "1024"
5151
string(1) "1"
52-
gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0
52+
gmp_pow(): Argument #2 ($exponent) must be between 0 and %d
5353
string(4) "1024"
5454
string(14) "10240000000000"
5555
string(17) "97656250000000000"
56-
gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0
56+
gmp_pow(): Argument #2 ($exponent) must be between 0 and %d
5757
string(14) "10240000000000"
5858
string(14) "10240000000000"
5959
gmp_pow(): Argument #2 ($exponent) must be of type int, array given

ext/gmp/tests/gmp_pow2.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ object(GMP)#%d (1) {
3131
["num"]=>
3232
string(4) "1024"
3333
}
34-
Exponent must be greater than or equal to 0
35-
Exponent must be greater than or equal to 0
34+
Exponent must be between 0 and %d
35+
Exponent must be between 0 and %d

ext/gmp/tests/gmp_pown.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ var_dump(gmp_powm(10, $n, 10));
6363

6464
echo "Done\n";
6565
?>
66-
--EXPECT--
66+
--EXPECTF--
6767
string(1) "0"
6868
string(1) "5"
6969
string(1) "5"
@@ -73,8 +73,8 @@ string(3) "533"
7373
string(3) "331"
7474
string(3) "171"
7575
string(3) "371"
76-
Modulo by zero
77-
Modulo by zero
76+
gmp_powm(): Argument #3 ($modulus) Modulo by zero
77+
gmp_powm(): Argument #3 ($modulus) Modulo by zero
7878
gmp_powm(): Argument #1 ($num) must be of type GMP|string|int, array given
7979
gmp_powm(): Argument #2 ($exponent) must be of type GMP|string|int, array given
8080
gmp_powm(): Argument #2 ($exponent) must be of type GMP|string|int, TypeError given

ext/gmp/tests/gmp_remroot.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,5 @@ array(2) {
105105
string(1) "0"
106106
}
107107
}
108-
gmp_rootrem(): Argument #2 ($nth) must be greater than or equal to 1
109-
gmp_rootrem(): Argument #2 ($nth) must be greater than or equal to 1
108+
gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d
109+
gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d

0 commit comments

Comments
 (0)