[php-src] master: ext/opcache: Do not reuse the assigned range through typed references (#24210)
| From: | Ilia Alshanetsky via GitHub | Date: | Sun, 11 Oct 2026 08:30:25 +0000 |
| Subject: | [php-src] master: ext/opcache: Do not reuse the assigned range through typed references (#24210) | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-142179@lists.php.net to get a copy of this message | ||
Author: Ilia Alshanetsky (iliaal)
Committer: GitHub (web-flow)
Pusher: iliaal
Date: 2026-10-11T04:30:22-04:00
Commit: https://github.com/php/php-src/commit/f0301f3ca2e2e278aa1b8d33de60f87c6a359337
Raw diff: https://github.com/php/php-src/commit/f0301f3ca2e2e278aa1b8d33de60f87c6a359337.diff
ext/opcache: Do not reuse the assigned range through typed references (#24210)
An assignment through a reference to a typed property coerces the value, so
its result can differ from the assigned operand, but range inference copied
the operand's range to the result. Reuse it only when the target is a CV
that cannot be a reference.
Changed paths:
A ext/opcache/tests/opt/assign_typed_ref_range.phpt
A ext/opcache/tests/opt/assign_typed_ref_range_64bit.phpt
M NEWS
M Zend/Optimizer/zend_inference.c
Diff:
diff --git a/NEWS b/NEWS
index 9c5f7bede786..44fb719c6982 100644
--- a/NEWS
+++ b/NEWS
@@ -54,6 +54,8 @@ PHP NEWS
. Fixed field_count not resetting on OK packet. (Kamil Tekiela)
- Opcache:
+ . Fixed range inference of assignments through typed references.
+ (Ilia Alshanetsky)
. Fixed bug GH-23693 (Tracing JIT produces wrong results for a guard on a
loop-invariant addition). (Ilia Alshanetsky)
. Fixed OSS-Fuzz #545352966 (default value AST of an SHM-persisted partial).
diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c
index 2e210414c307..effff75e00f2 100644
--- a/Zend/Optimizer/zend_inference.c
+++ b/Zend/Optimizer/zend_inference.c
@@ -1445,7 +1445,9 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array,
cons
break;
case ZEND_ASSIGN:
if (ssa_op->op1_def == var || ssa_op->op2_def == var || ssa_op->result_def == var) {
- if (OP2_HAS_RANGE()) {
+ if (OP2_HAS_RANGE()
+ && (ssa_op->op2_def == var
+ || (opline->op1_type == IS_CV && !(OP1_INFO() & MAY_BE_REF)))) {
tmp->min = OP2_MIN_RANGE();
tmp->max = OP2_MAX_RANGE();
tmp->underflow = OP2_RANGE_UNDERFLOW();
diff --git a/ext/opcache/tests/opt/assign_typed_ref_range.phpt
b/ext/opcache/tests/opt/assign_typed_ref_range.phpt
new file mode 100644
index 000000000000..5cd7b1ed4f2f
--- /dev/null
+++ b/ext/opcache/tests/opt/assign_typed_ref_range.phpt
@@ -0,0 +1,59 @@
+--TEST--
+Assignment through typed references must not reuse the assigned range
+--EXTENSIONS--
+opcache
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+error_reporting=E_ALL & ~E_DEPRECATED
+--FILE--
+<?php
+class Box {
+ public int $value = 0;
+}
+
+function assignBounded(&$reference, $value) {
+ if ($value > 5 && $value < 7) {
+ $result = ($reference = $value);
+ if (is_int($result)) {
+ return $result;
+ }
+ }
+}
+
+function assignEqual(&$reference, $value) {
+ if ($value == 6) {
+ $result = ($reference = $value);
+ if (is_int($result)) {
+ return $result;
+ }
+ }
+}
+
+function assignGlobal($value) {
+ if ($value > 5 && $value < 7) {
+ $result = ($GLOBALS['reference'] = $value);
+ if (is_int($result)) {
+ return $result;
+ }
+ }
+}
+
+$box = new Box();
+echo 'float: ', assignBounded($box->value, 5.5), ', stored: ',
$box->value, "\n";
+echo 'numeric string: ', assignBounded($box->value, '5.5'), ', stored:
', $box->value, "\n";
+echo 'boolean: ', assignEqual($box->value, true), ', stored: ',
$box->value, "\n";
+echo 'integer: ', assignBounded($box->value, 6), ', stored: ',
$box->value, "\n";
+
+$GLOBALS['reference'] = &$box->value;
+echo 'global float: ', assignGlobal(5.5), ', stored: ', $box->value,
"\n";
+echo 'global integer: ', assignGlobal(6), ', stored: ', $box->value,
"\n";
+?>
+--EXPECT--
+float: 5, stored: 5
+numeric string: 5, stored: 5
+boolean: 1, stored: 1
+integer: 6, stored: 6
+global float: 5, stored: 5
+global integer: 6, stored: 6
diff --git a/ext/opcache/tests/opt/assign_typed_ref_range_64bit.phpt
b/ext/opcache/tests/opt/assign_typed_ref_range_64bit.phpt
new file mode 100644
index 000000000000..8ca387c07b2c
--- /dev/null
+++ b/ext/opcache/tests/opt/assign_typed_ref_range_64bit.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Assignment through a float typed reference must not reuse the assigned integer range
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--FILE--
+<?php
+class Box {
+ public float $real = 0.0;
+}
+
+function assignGlobalFloat() {
+ return ($GLOBALS['real'] = 9007199254740993) & 1;
+}
+
+$box = new Box();
+$GLOBALS['real'] = &$box->real;
+var_dump(assignGlobalFloat());
+?>
+--EXPECT--
+int(0)