Commit 617e4c38e0d for php.net
commit 617e4c38e0df927a6500f037970a62ca2951e703
Author: David Carlier <devnexen@gmail.com>
Date: Sun Apr 19 08:44:55 2026 +0100
ext/gmp: reject values larger than unsigned long in gmp_pow/binomial/root/rootrem and shift/pow operators.
Applies the gmp_fact() pattern (0236667bd94) 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 GH-21812
diff --git a/NEWS b/NEWS
index 481c4ea2c38..73a2b3d6637 100644
--- a/NEWS
+++ b/NEWS
@@ -43,6 +43,8 @@ PHP NEWS
- GMP:
. gmp_fact() reject values larger than unsigned long. (David Carlier)
+ . gmp_pow/binomial/root/rootrem and shift/pow operators reject values
+ larger than unsigned long. (David Carlier)
- Hash:
. Upgrade xxHash to 0.8.2. (timwolla)
diff --git a/UPGRADING b/UPGRADING
index 0e66a96909f..9c3d5a2b29a 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -155,6 +155,13 @@ PHP 8.6 UPGRADE NOTES
- GMP:
. gmp_fact() now throws a ValueError() if $num does not fit into
a unsigned long.
+ . gmp_pow(), gmp_binomial(), gmp_root() and gmp_rootrem() now throw a
+ ValueError if their second argument does not fit into an unsigned long.
+ . The shift (<<, >>) and exponentiation (**) operators on GMP objects
+ now throw a ValueError if the right operand does not fit into an
+ unsigned long.
+ . gmp_powm() modulo-by-zero now raises a DivisionByZeroError whose
+ message includes the function name and argument index ($modulus).
- mysqli:
. The return structure of mysqli_get_charset() no longer contains
diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c
index a2097458090..b04fc9f5c1b 100644
--- a/ext/gmp/gmp.c
+++ b/ext/gmp/gmp.c
@@ -356,10 +356,10 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
shift = Z_LVAL_P(op2);
}
- if (shift < 0) {
+ if (shift < 0 || shift > ULONG_MAX) {
zend_throw_error(
- zend_ce_value_error, "%s must be greater than or equal to 0",
- opcode == ZEND_POW ? "Exponent" : "Shift"
+ zend_ce_value_error, "%s must be between 0 and %lu",
+ opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
);
ZVAL_UNDEF(return_value);
return FAILURE;
@@ -1087,11 +1087,6 @@ ZEND_FUNCTION(gmp_fact)
GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum)
ZEND_PARSE_PARAMETERS_END();
- if (mpz_sgn(gmpnum) < 0) {
- zend_argument_value_error(1, "must be greater than or equal to 0");
- RETURN_THROWS();
- }
-
if (!mpz_fits_ulong_p(gmpnum)) {
zend_argument_value_error(1, "must be between 0 and %lu", ULONG_MAX);
RETURN_THROWS();
@@ -1114,8 +1109,8 @@ ZEND_FUNCTION(gmp_binomial)
Z_PARAM_LONG(k)
ZEND_PARSE_PARAMETERS_END();
- if (k < 0) {
- zend_argument_value_error(2, "must be greater than or equal to 0");
+ if (k < 0 || k > ULONG_MAX) {
+ zend_argument_value_error(2, "must be between 0 and %lu", ULONG_MAX);
RETURN_THROWS();
}
@@ -1136,8 +1131,8 @@ ZEND_FUNCTION(gmp_pow)
Z_PARAM_LONG(exp)
ZEND_PARSE_PARAMETERS_END();
- if (exp < 0) {
- zend_argument_value_error(2, "must be greater than or equal to 0");
+ if (exp < 0 || exp > ULONG_MAX) {
+ zend_argument_value_error(2, "must be between 0 and %lu", ULONG_MAX);
RETURN_THROWS();
}
@@ -1163,7 +1158,7 @@ ZEND_FUNCTION(gmp_powm)
}
if (!mpz_cmp_ui(gmpnum_mod, 0)) {
- zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
+ zend_argument_error(zend_ce_division_by_zero_error, 3, "Modulo by zero");
RETURN_THROWS();
}
@@ -1226,8 +1221,8 @@ ZEND_FUNCTION(gmp_root)
Z_PARAM_LONG(nth)
ZEND_PARSE_PARAMETERS_END();
- if (nth <= 0) {
- zend_argument_value_error(2, "must be greater than 0");
+ if (nth <= 0 || nth > ULONG_MAX) {
+ zend_argument_value_error(2, "must be between 1 and %lu", ULONG_MAX);
RETURN_THROWS();
}
@@ -1253,8 +1248,8 @@ ZEND_FUNCTION(gmp_rootrem)
Z_PARAM_LONG(nth)
ZEND_PARSE_PARAMETERS_END();
- if (nth <= 0) {
- zend_argument_value_error(2, "must be greater than or equal to 1");
+ if (nth <= 0 || nth > ULONG_MAX) {
+ zend_argument_value_error(2, "must be between 1 and %lu", ULONG_MAX);
RETURN_THROWS();
}
diff --git a/ext/gmp/tests/gmp_binomial.phpt b/ext/gmp/tests/gmp_binomial.phpt
index 4598a4c47ee..1bf371e68a1 100644
--- a/ext/gmp/tests/gmp_binomial.phpt
+++ b/ext/gmp/tests/gmp_binomial.phpt
@@ -26,7 +26,7 @@
echo $e->getMessage() . \PHP_EOL;
}
?>
---EXPECT--
+--EXPECTF--
object(GMP)#1 (1) {
["num"]=>
string(3) "252"
@@ -67,4 +67,4 @@
["num"]=>
string(1) "7"
}
-gmp_binomial(): Argument #2 ($k) must be greater than or equal to 0
+gmp_binomial(): Argument #2 ($k) must be between 0 and %d
diff --git a/ext/gmp/tests/gmp_fact.phpt b/ext/gmp/tests/gmp_fact.phpt
index e0393145494..b28f572c697 100644
--- a/ext/gmp/tests/gmp_fact.phpt
+++ b/ext/gmp/tests/gmp_fact.phpt
@@ -45,17 +45,17 @@
echo "Done\n";
?>
---EXPECT--
+--EXPECTF--
string(1) "1"
gmp_fact(): Argument #1 ($num) is not an integer string
string(1) "1"
-gmp_fact(): Argument #1 ($num) must be greater than or equal to 0
-gmp_fact(): Argument #1 ($num) must be greater than or equal to 0
+gmp_fact(): Argument #1 ($num) must be between 0 and %d
+gmp_fact(): Argument #1 ($num) must be between 0 and %d
string(19) "2432902008176640000"
string(65) "30414093201713378043612608166064768844377641568960512000000000000"
string(7) "3628800"
string(1) "1"
string(9) "479001600"
-gmp_fact(): Argument #1 ($num) must be greater than or equal to 0
+gmp_fact(): Argument #1 ($num) must be between 0 and %d
gmp_fact(): Argument #1 ($num) must be of type GMP|string|int, array given
Done
diff --git a/ext/gmp/tests/gmp_overflow_llp64.phpt b/ext/gmp/tests/gmp_overflow_llp64.phpt
new file mode 100644
index 00000000000..ea242bc5215
--- /dev/null
+++ b/ext/gmp/tests/gmp_overflow_llp64.phpt
@@ -0,0 +1,58 @@
+--TEST--
+GMP functions reject values larger than unsigned long on LLP64
+--EXTENSIONS--
+gmp
+--SKIPIF--
+<?php
+if (PHP_OS_FAMILY !== "Windows" || PHP_INT_SIZE !== 8) die("skip LLP64 (Windows 64-bit) only");
+?>
+--FILE--
+<?php
+
+try {
+ gmp_pow(2, PHP_INT_MAX);
+} catch (ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+try {
+ gmp_binomial(10, PHP_INT_MAX);
+} catch (ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+try {
+ gmp_root(10, PHP_INT_MAX);
+} catch (ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+try {
+ gmp_rootrem(10, PHP_INT_MAX);
+} catch (ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+$n = gmp_init(2);
+try {
+ $n << PHP_INT_MAX;
+} catch (ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+try {
+ $n ** PHP_INT_MAX;
+} catch (ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+echo "Done\n";
+?>
+--EXPECTF--
+gmp_pow(): Argument #2 ($exponent) must be between 0 and %d
+gmp_binomial(): Argument #2 ($k) must be between 0 and %d
+gmp_root(): Argument #2 ($nth) must be between 1 and %d
+gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d
+Shift must be between 0 and %d
+Exponent must be between 0 and %d
+Done
diff --git a/ext/gmp/tests/gmp_pow.phpt b/ext/gmp/tests/gmp_pow.phpt
index f42e44e31ab..36d0d16d8cc 100644
--- a/ext/gmp/tests/gmp_pow.phpt
+++ b/ext/gmp/tests/gmp_pow.phpt
@@ -43,17 +43,17 @@
echo "Done\n";
?>
---EXPECT--
+--EXPECTF--
string(4) "1024"
string(4) "1024"
string(5) "-2048"
string(4) "1024"
string(1) "1"
-gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0
+gmp_pow(): Argument #2 ($exponent) must be between 0 and %d
string(4) "1024"
string(14) "10240000000000"
string(17) "97656250000000000"
-gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0
+gmp_pow(): Argument #2 ($exponent) must be between 0 and %d
string(14) "10240000000000"
string(14) "10240000000000"
gmp_pow(): Argument #2 ($exponent) must be of type int, array given
diff --git a/ext/gmp/tests/gmp_pow2.phpt b/ext/gmp/tests/gmp_pow2.phpt
index 43bf9bb5aaa..96f33e8c25c 100644
--- a/ext/gmp/tests/gmp_pow2.phpt
+++ b/ext/gmp/tests/gmp_pow2.phpt
@@ -31,5 +31,5 @@
["num"]=>
string(4) "1024"
}
-Exponent must be greater than or equal to 0
-Exponent must be greater than or equal to 0
+Exponent must be between 0 and %d
+Exponent must be between 0 and %d
diff --git a/ext/gmp/tests/gmp_pown.phpt b/ext/gmp/tests/gmp_pown.phpt
index 0e7b3bd1fa5..79709f27271 100644
--- a/ext/gmp/tests/gmp_pown.phpt
+++ b/ext/gmp/tests/gmp_pown.phpt
@@ -63,7 +63,7 @@
echo "Done\n";
?>
---EXPECT--
+--EXPECTF--
string(1) "0"
string(1) "5"
string(1) "5"
@@ -73,8 +73,8 @@
string(3) "331"
string(3) "171"
string(3) "371"
-Modulo by zero
-Modulo by zero
+gmp_powm(): Argument #3 ($modulus) Modulo by zero
+gmp_powm(): Argument #3 ($modulus) Modulo by zero
gmp_powm(): Argument #1 ($num) must be of type GMP|string|int, array given
gmp_powm(): Argument #2 ($exponent) must be of type GMP|string|int, array given
gmp_powm(): Argument #2 ($exponent) must be of type GMP|string|int, TypeError given
diff --git a/ext/gmp/tests/gmp_remroot.phpt b/ext/gmp/tests/gmp_remroot.phpt
index 8e53858c604..de54d0e91c3 100644
--- a/ext/gmp/tests/gmp_remroot.phpt
+++ b/ext/gmp/tests/gmp_remroot.phpt
@@ -105,5 +105,5 @@
string(1) "0"
}
}
-gmp_rootrem(): Argument #2 ($nth) must be greater than or equal to 1
-gmp_rootrem(): Argument #2 ($nth) must be greater than or equal to 1
+gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d
+gmp_rootrem(): Argument #2 ($nth) must be between 1 and %d
diff --git a/ext/gmp/tests/gmp_root.phpt b/ext/gmp/tests/gmp_root.phpt
index 2313b207cc8..36793e24972 100644
--- a/ext/gmp/tests/gmp_root.phpt
+++ b/ext/gmp/tests/gmp_root.phpt
@@ -58,5 +58,5 @@
["num"]=>
string(1) "0"
}
-gmp_root(): Argument #2 ($nth) must be greater than 0
-gmp_root(): Argument #2 ($nth) must be greater than 0
+gmp_root(): Argument #2 ($nth) must be between 1 and %d
+gmp_root(): Argument #2 ($nth) must be between 1 and %d
diff --git a/ext/gmp/tests/overloading.phpt b/ext/gmp/tests/overloading.phpt
index 14c35ea8470..f55a83f0bd5 100644
--- a/ext/gmp/tests/overloading.phpt
+++ b/ext/gmp/tests/overloading.phpt
@@ -123,7 +123,7 @@
var_dump($a);
?>
---EXPECT--
+--EXPECTF--
object(GMP)#3 (1) {
["num"]=>
string(2) "59"
@@ -254,8 +254,8 @@
["num"]=>
string(3) "-11"
}
-Shift must be greater than or equal to 0
-Shift must be greater than or equal to 0
+Shift must be between 0 and %d
+Shift must be between 0 and %d
object(GMP)#5 (1) {
["num"]=>
string(3) "-43"