Commit f04eaec41ac for php.net

commit f04eaec41ac88ea75e51592e91aa3f7c65939d21
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon Jul 6 15:26:00 2026 -0400

    Fix GH-22549: GMP compound pow/shift assign asserts on negative operand (#22582)

    shift_operator_helper() wrote ZVAL_UNDEF(return_value) on the out-of-range
    exponent/shift path, but for a compound assignment return_value aliases op1,
    so pow_function's fallback then reached the scalar conversion with op1 set to
    IS_UNDEF and hit ZEND_UNREACHABLE(). Drop the stray undef; the helper's other
    error paths already return FAILURE without touching return_value, leaving the
    operand intact for the thrown ValueError to propagate.

    Fixes GH-22549

diff --git a/NEWS b/NEWS
index 11b47065438..2a266243a74 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,10 @@ PHP                                                                        NEWS
   . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
     warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman)

+- GMP:
+  . Fixed bug GH-22549 (Assertion failure / UB on a compound GMP power or shift
+    assignment with a negative exponent). (iliaal)
+
 - Intl:
   . Fixed NumberFormatter::parse() and NumberFormatter::parseCurrency() to
     reject offset values outside the 32-bit range instead of silently
diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c
index ef06d0fda30..59209c4e1be 100644
--- a/ext/gmp/gmp.c
+++ b/ext/gmp/gmp.c
@@ -361,7 +361,6 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val
 			zend_ce_value_error, "%s must be between 0 and %lu",
 			opcode == ZEND_POW ? "Exponent" : "Shift", ULONG_MAX
 		);
-		ZVAL_UNDEF(return_value);
 		return FAILURE;
 	} else {
 		mpz_ptr gmpnum_op, gmpnum_result;
diff --git a/ext/gmp/tests/gh22549.phpt b/ext/gmp/tests/gh22549.phpt
new file mode 100644
index 00000000000..82a66901bd0
--- /dev/null
+++ b/ext/gmp/tests/gh22549.phpt
@@ -0,0 +1,55 @@
+--TEST--
+GH-22549 (Assertion failure in _zendi_try_convert_scalar_to_number with compound GMP shift/pow)
+--EXTENSIONS--
+gmp
+--FILE--
+<?php
+// Compound assignment aliases result and op1. A negative GMP shift/exponent
+// must throw a ValueError, not clobber op1 into IS_UNDEF and abort.
+$cases = [
+    'int ** gmp'   => [2,            fn(&$x) => $x **= gmp_init(-5)],
+    'gmp ** gmp'   => [gmp_init(2),  fn(&$x) => $x **= gmp_init(-5)],
+    'array ** gmp' => [[0],          fn(&$x) => $x **= gmp_init(-5)],
+    'int << gmp'   => [8,            fn(&$x) => $x <<= gmp_init(-2)],
+    'int >> gmp'   => [8,            fn(&$x) => $x >>= gmp_init(-2)],
+    'gmp << gmp'   => [gmp_init(8),  fn(&$x) => $x <<= gmp_init(-2)],
+];
+
+foreach ($cases as $label => [$x, $op]) {
+    try {
+        $op($x);
+        echo "$label: no exception\n";
+    } catch (\Throwable $e) {
+        echo "$label: ", $e::class, ": ", $e->getMessage(), "\n";
+    }
+    var_dump($x);
+}
+
+// Valid operations must still succeed (guard against an over-broad regression).
+$p = 2;   $p **= gmp_init(3);  echo "valid pow: ", gmp_strval($p), "\n";
+$s = 1;   $s <<= gmp_init(4);  echo "valid shift: ", gmp_strval($s), "\n";
+?>
+--EXPECTF--
+int ** gmp: ValueError: Exponent must be between 0 and %d
+int(2)
+gmp ** gmp: ValueError: Exponent must be between 0 and %d
+object(GMP)#%d (1) {
+  ["num"]=>
+  string(1) "2"
+}
+array ** gmp: ValueError: Exponent must be between 0 and %d
+array(1) {
+  [0]=>
+  int(0)
+}
+int << gmp: ValueError: Shift must be between 0 and %d
+int(8)
+int >> gmp: ValueError: Shift must be between 0 and %d
+int(8)
+gmp << gmp: ValueError: Shift must be between 0 and %d
+object(GMP)#%d (1) {
+  ["num"]=>
+  string(1) "8"
+}
+valid pow: 8
+valid shift: 16