Commit e2ea940fc95 for php.net
commit e2ea940fc95393452b2d8d52ea268368bfb43afe
Author: Weilin Du <weilindu@php.net>
Date: Wed Jul 15 20:40:19 2026 +0800
ext/gmp: Fix GMP operator RHS overflow for GMP values (#22656)
```php
<?php
$too_large = gmp_init("18446744073709551616");
var_dump(gmp_init(2) ** $too_large);
var_dump(gmp_init(2) << $too_large);
```
```
object(GMP)#3 (1) {
["num"]=>
string(1) "1"
}
object(GMP)#2 (1) {
["num"]=>
string(1) "2"
}
```
This PR fix the overflow by rejecting large gmp objects in these
calculation (throws ValueError)
diff --git a/NEWS b/NEWS
index 5bb78b98c4e..cb10b3be0b6 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0alpha3
+- GMP:
+ . Fixed GMP power and shift operators to reject GMP right operands outside
+ the unsigned long range instead of silently truncating them. (Weilin Du)
+
- ODBC:
. Fixed bug GH-22668 (Heap buffer over-read when a column value exceeds the
driver-reported display size). (iliaal)
diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c
index 59209c4e1be..b05706e4829 100644
--- a/ext/gmp/gmp.c
+++ b/ext/gmp/gmp.c
@@ -327,8 +327,17 @@ static zend_result binop_operator_helper(gmp_binary_op_t gmp_op, zval *return_va
typedef void (*gmp_binary_ui_op_t)(mpz_ptr, mpz_srcptr, gmp_ulong);
+static void gmp_shift_operator_range_error(uint8_t opcode) {
+ zend_throw_error(
+ zend_ce_value_error, "%s must be between 0 and %lu",
+ opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
+ );
+}
+
static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zval *op1, zval *op2, uint8_t opcode) {
zend_long shift = 0;
+ gmp_ulong shift_ui = 0;
+ bool have_shift_ui = false;
if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) {
if (UNEXPECTED(!IS_GMP(op2))) {
@@ -349,31 +358,36 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
goto typeof_op_failure;
}
} else {
- // TODO We shouldn't cast the GMP object to int here
- shift = zval_get_long(op2);
+ mpz_ptr gmpnum_shift = GET_GMP_FROM_ZVAL(op2);
+ if (!mpz_fits_ulong_p(gmpnum_shift)) {
+ gmp_shift_operator_range_error(opcode);
+ return FAILURE;
+ }
+ shift_ui = (gmp_ulong) mpz_get_ui(gmpnum_shift);
+ have_shift_ui = true;
}
} else {
shift = Z_LVAL_P(op2);
}
- if (shift < 0 || shift > ULONG_MAX) {
- zend_throw_error(
- zend_ce_value_error, "%s must be between 0 and %lu",
- opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
- );
- return FAILURE;
- } else {
- mpz_ptr gmpnum_op, gmpnum_result;
-
- if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
- goto typeof_op_failure;
+ if (!have_shift_ui) {
+ if (shift < 0 || shift > ULONG_MAX) {
+ gmp_shift_operator_range_error(opcode);
+ return FAILURE;
}
+ shift_ui = (gmp_ulong) shift;
+ }
- INIT_GMP_RETVAL(gmpnum_result);
- op(gmpnum_result, gmpnum_op, (gmp_ulong) shift);
- return SUCCESS;
+ mpz_ptr gmpnum_op, gmpnum_result;
+
+ if (!gmp_zend_parse_arg_into_mpz_ex(op1, &gmpnum_op, 1, true)) {
+ goto typeof_op_failure;
}
+ INIT_GMP_RETVAL(gmpnum_result);
+ op(gmpnum_result, gmpnum_op, shift_ui);
+ return SUCCESS;
+
typeof_op_failure: ;
/* Returning FAILURE without throwing an exception would emit the
* Unsupported operand types: GMP OP TypeOfOp2
diff --git a/ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt b/ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt
new file mode 100644
index 00000000000..e8b6139ace0
--- /dev/null
+++ b/ext/gmp/tests/gmp_operator_rhs_gmp_overflow.phpt
@@ -0,0 +1,33 @@
+--TEST--
+GMP operator right operand rejects values outside the unsigned long range
+--EXTENSIONS--
+gmp
+--FILE--
+<?php
+$too_large = gmp_init("18446744073709551616");
+
+try {
+ var_dump(gmp_init(2) ** $too_large);
+} catch (ValueError $e) {
+ echo $e->getMessage(), PHP_EOL;
+}
+
+try {
+ var_dump(gmp_init(2) << $too_large);
+} catch (ValueError $e) {
+ echo $e->getMessage(), PHP_EOL;
+}
+
+try {
+ var_dump(gmp_init(2) >> $too_large);
+} catch (ValueError $e) {
+ echo $e->getMessage(), PHP_EOL;
+}
+
+echo "Done\n";
+?>
+--EXPECTF--
+Exponent must be between 0 and %d
+Shift must be between 0 and %d
+Shift must be between 0 and %d
+Done