Skip to content

Commit f0301f3

Browse files
authored
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.
1 parent d8a976e commit f0301f3

4 files changed

Lines changed: 90 additions & 1 deletion

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ PHP NEWS
5454
. Fixed field_count not resetting on OK packet. (Kamil Tekiela)
5555

5656
- Opcache:
57+
. Fixed range inference of assignments through typed references.
58+
(Ilia Alshanetsky)
5759
. Fixed bug GH-23693 (Tracing JIT produces wrong results for a guard on a
5860
loop-invariant addition). (Ilia Alshanetsky)
5961
. Fixed OSS-Fuzz #545352966 (default value AST of an SHM-persisted partial).

‎Zend/Optimizer/zend_inference.c‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,9 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, cons
14451445
break;
14461446
case ZEND_ASSIGN:
14471447
if (ssa_op->op1_def == var || ssa_op->op2_def == var || ssa_op->result_def == var) {
1448-
if (OP2_HAS_RANGE()) {
1448+
if (OP2_HAS_RANGE()
1449+
&& (ssa_op->op2_def == var
1450+
|| (opline->op1_type == IS_CV && !(OP1_INFO() & MAY_BE_REF)))) {
14491451
tmp->min = OP2_MIN_RANGE();
14501452
tmp->max = OP2_MAX_RANGE();
14511453
tmp->underflow = OP2_RANGE_UNDERFLOW();
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
Assignment through typed references must not reuse the assigned range
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.optimization_level=-1
9+
error_reporting=E_ALL & ~E_DEPRECATED
10+
--FILE--
11+
<?php
12+
class Box {
13+
public int $value = 0;
14+
}
15+
16+
function assignBounded(&$reference, $value) {
17+
if ($value > 5 && $value < 7) {
18+
$result = ($reference = $value);
19+
if (is_int($result)) {
20+
return $result;
21+
}
22+
}
23+
}
24+
25+
function assignEqual(&$reference, $value) {
26+
if ($value == 6) {
27+
$result = ($reference = $value);
28+
if (is_int($result)) {
29+
return $result;
30+
}
31+
}
32+
}
33+
34+
function assignGlobal($value) {
35+
if ($value > 5 && $value < 7) {
36+
$result = ($GLOBALS['reference'] = $value);
37+
if (is_int($result)) {
38+
return $result;
39+
}
40+
}
41+
}
42+
43+
$box = new Box();
44+
echo 'float: ', assignBounded($box->value, 5.5), ', stored: ', $box->value, "\n";
45+
echo 'numeric string: ', assignBounded($box->value, '5.5'), ', stored: ', $box->value, "\n";
46+
echo 'boolean: ', assignEqual($box->value, true), ', stored: ', $box->value, "\n";
47+
echo 'integer: ', assignBounded($box->value, 6), ', stored: ', $box->value, "\n";
48+
49+
$GLOBALS['reference'] = &$box->value;
50+
echo 'global float: ', assignGlobal(5.5), ', stored: ', $box->value, "\n";
51+
echo 'global integer: ', assignGlobal(6), ', stored: ', $box->value, "\n";
52+
?>
53+
--EXPECT--
54+
float: 5, stored: 5
55+
numeric string: 5, stored: 5
56+
boolean: 1, stored: 1
57+
integer: 6, stored: 6
58+
global float: 5, stored: 5
59+
global integer: 6, stored: 6
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Assignment through a float typed reference must not reuse the assigned integer range
3+
--EXTENSIONS--
4+
opcache
5+
--SKIPIF--
6+
<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
7+
--INI--
8+
opcache.enable=1
9+
opcache.enable_cli=1
10+
opcache.optimization_level=-1
11+
--FILE--
12+
<?php
13+
class Box {
14+
public float $real = 0.0;
15+
}
16+
17+
function assignGlobalFloat() {
18+
return ($GLOBALS['real'] = 9007199254740993) & 1;
19+
}
20+
21+
$box = new Box();
22+
$GLOBALS['real'] = &$box->real;
23+
var_dump(assignGlobalFloat());
24+
?>
25+
--EXPECT--
26+
int(0)

0 commit comments

Comments
 (0)