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