Skip to content

Commit 3d0885f

Browse files
committed
Fix GH-14873: PHP 8.4 min function fails on typed integer
The problem is that this line in the VM: `ZVAL_NULL(result);` changes the type of arg1 as well, because after the DFA pass the result and input both use CV0($result). We should not contract assignments with CVs in frameless calls with arguments. An older attempt is found at GH-14876 that tried to modify the VM/JIT.
1 parent 6bb9600 commit 3d0885f

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed

Zend/Optimizer/dfa_pass.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ static bool opline_supports_assign_contraction(
325325
return 0;
326326
}
327327

328+
if (opline->opcode >= ZEND_FRAMELESS_ICALL_1 && opline->opcode <= ZEND_FRAMELESS_ICALL_3) {
329+
/* Frameless calls override the return value, but the return value may overlap with the arguments. */
330+
return opline->op1_type != IS_CV || opline->op1.var != cv_var;
331+
}
332+
328333
if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
329334
|| opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
330335
/* Function calls may dtor the return value after it has already been written -- allow

ext/opcache/tests/opt/gh14873.phpt

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
--TEST--
2+
GH-14873 (PHP 8.4 min function fails on typed integer)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.optimization_level=0x7FFEBFFF
9+
opcache.opt_debug_level=0x20000
10+
--FILE--
11+
<?php
12+
13+
function testTrim1(string $value): string {
14+
$value = trim($value);
15+
return $value;
16+
}
17+
18+
function testMin2(int $value): int {
19+
$value = min($value, 100);
20+
return $value;
21+
}
22+
23+
function testMin2_TMP(int $value): int {
24+
$value = min($value + 1, 100);
25+
return $value;
26+
}
27+
28+
function testStrstr3(string $value): string {
29+
$value = strstr($value, "needle", false);
30+
return $value;
31+
}
32+
33+
var_dump(testTrim1(" boo "));
34+
var_dump(testMin2(5));
35+
var_dump(testMin2_TMP(5));
36+
var_dump(testStrstr3("needles"));
37+
38+
?>
39+
--EXPECTF--
40+
$_main:
41+
; (lines=25, args=0, vars=0, tmps=%d)
42+
; (after optimizer)
43+
; %s
44+
0000 INIT_FCALL 1 %d string("var_dump")
45+
0001 INIT_FCALL 1 %d string("testtrim1")
46+
0002 SEND_VAL string(" boo ") 1
47+
0003 V0 = DO_UCALL
48+
0004 SEND_VAR V0 1
49+
0005 DO_ICALL
50+
0006 INIT_FCALL 1 %d string("var_dump")
51+
0007 INIT_FCALL 1 %d string("testmin2")
52+
0008 SEND_VAL int(5) 1
53+
0009 V0 = DO_UCALL
54+
0010 SEND_VAR V0 1
55+
0011 DO_ICALL
56+
0012 INIT_FCALL 1 %d string("var_dump")
57+
0013 INIT_FCALL 1 %d string("testmin2_tmp")
58+
0014 SEND_VAL int(5) 1
59+
0015 V0 = DO_UCALL
60+
0016 SEND_VAR V0 1
61+
0017 DO_ICALL
62+
0018 INIT_FCALL 1 %d string("var_dump")
63+
0019 INIT_FCALL 1 %d string("teststrstr3")
64+
0020 SEND_VAL string("needles") 1
65+
0021 V0 = DO_UCALL
66+
0022 SEND_VAR V0 1
67+
0023 DO_ICALL
68+
0024 RETURN int(1)
69+
70+
testTrim1:
71+
; (lines=4, args=1, vars=1, tmps=%d)
72+
; (after optimizer)
73+
; %s
74+
0000 CV0($value) = RECV 1
75+
0001 T1 = FRAMELESS_ICALL_1(trim) CV0($value)
76+
0002 ASSIGN CV0($value) T1
77+
0003 RETURN CV0($value)
78+
79+
testMin2:
80+
; (lines=5, args=1, vars=1, tmps=%d)
81+
; (after optimizer)
82+
; %s
83+
0000 CV0($value) = RECV 1
84+
0001 T1 = FRAMELESS_ICALL_2(min) CV0($value) int(100)
85+
0002 CV0($value) = QM_ASSIGN T1
86+
0003 VERIFY_RETURN_TYPE CV0($value)
87+
0004 RETURN CV0($value)
88+
89+
testMin2_TMP:
90+
; (lines=5, args=1, vars=1, tmps=%d)
91+
; (after optimizer)
92+
; %s
93+
0000 CV0($value) = RECV 1
94+
0001 T1 = ADD CV0($value) int(1)
95+
0002 CV0($value) = FRAMELESS_ICALL_2(min) T1 int(100)
96+
0003 VERIFY_RETURN_TYPE CV0($value)
97+
0004 RETURN CV0($value)
98+
99+
testStrstr3:
100+
; (lines=6, args=1, vars=1, tmps=%d)
101+
; (after optimizer)
102+
; %s
103+
0000 CV0($value) = RECV 1
104+
0001 T1 = FRAMELESS_ICALL_3(strstr) CV0($value) string("needle")
105+
0002 OP_DATA bool(false)
106+
0003 ASSIGN CV0($value) T1
107+
0004 VERIFY_RETURN_TYPE CV0($value)
108+
0005 RETURN CV0($value)
109+
LIVE RANGES:
110+
1: 0002 - 0003 (tmp/var)
111+
string(3) "boo"
112+
int(5)
113+
int(6)
114+
string(7) "needles"

0 commit comments

Comments
 (0)