Skip to content

Commit 1a07bb9

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. Closes GH-14903.
1 parent 8fe127a commit 1a07bb9

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ PHP NEWS
1111
- LibXML:
1212
. Added LIBXML_NO_XXE constant. (nielsdos)
1313

14+
- Opcache:
15+
. Fixed bug GH-14873 (PHP 8.4 min function fails on typed integer).
16+
(nielsdos)
17+
1418
- PDO:
1519
. Fixed bug GH-14792 (Compilation failure on pdo_* extensions).
1620
(Peter Kokot)

Zend/Optimizer/dfa_pass.c

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

328+
/* Frameless calls override the return value, but the return value may overlap with the arguments. */
329+
switch (opline->opcode) {
330+
case ZEND_FRAMELESS_ICALL_3:
331+
if ((opline + 1)->op1_type == IS_CV && (opline + 1)->op1.var == cv_var) return 0;
332+
ZEND_FALLTHROUGH;
333+
case ZEND_FRAMELESS_ICALL_2:
334+
if (opline->op2_type == IS_CV && opline->op2.var == cv_var) return 0;
335+
ZEND_FALLTHROUGH;
336+
case ZEND_FRAMELESS_ICALL_1:
337+
if (opline->op1_type == IS_CV && opline->op1.var == cv_var) return 0;
338+
return 1;
339+
}
340+
328341
if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
329342
|| opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
330343
/* Function calls may dtor the return value after it has already been written -- allow

ext/opcache/tests/opt/gh14873.phpt

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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 testMin2First(int $value): int {
19+
$value = min($value, 100);
20+
return $value;
21+
}
22+
23+
function testMin2Second(int $value): int {
24+
$value = min(100, $value);
25+
return $value;
26+
}
27+
28+
function testMin2_TMP(int $value): int {
29+
$value = min($value + 1, 100);
30+
return $value;
31+
}
32+
33+
function testStrstr3First(string $value): string {
34+
$value = strstr($value, "needle", false);
35+
return $value;
36+
}
37+
38+
function testStrstr3Second(string $value): string {
39+
$value = strstr("needles", $value, false);
40+
return $value;
41+
}
42+
43+
function testStrstr3Third(bool $value): string {
44+
$value = strstr("needles", "needle", $value);
45+
return $value;
46+
}
47+
48+
var_dump(testTrim1(" boo "));
49+
var_dump(testMin2First(5));
50+
var_dump(testMin2Second(5));
51+
var_dump(testMin2_TMP(5));
52+
var_dump(testStrstr3First("needles"));
53+
var_dump(testStrstr3Second("needle"));
54+
var_dump(testStrstr3Third(false));
55+
56+
?>
57+
--EXPECTF--
58+
$_main:
59+
; (lines=43, args=0, vars=0, tmps=%d)
60+
; (after optimizer)
61+
; %s
62+
0000 INIT_FCALL 1 %d string("var_dump")
63+
0001 INIT_FCALL 1 %d string("testtrim1")
64+
0002 SEND_VAL string(" boo ") 1
65+
0003 V0 = DO_UCALL
66+
0004 SEND_VAR V0 1
67+
0005 DO_ICALL
68+
0006 INIT_FCALL 1 %d string("var_dump")
69+
0007 INIT_FCALL 1 %d string("testmin2first")
70+
0008 SEND_VAL int(5) 1
71+
0009 V0 = DO_UCALL
72+
0010 SEND_VAR V0 1
73+
0011 DO_ICALL
74+
0012 INIT_FCALL 1 %d string("var_dump")
75+
0013 INIT_FCALL 1 %d string("testmin2second")
76+
0014 SEND_VAL int(5) 1
77+
0015 V0 = DO_UCALL
78+
0016 SEND_VAR V0 1
79+
0017 DO_ICALL
80+
0018 INIT_FCALL 1 %d string("var_dump")
81+
0019 INIT_FCALL 1 %d string("testmin2_tmp")
82+
0020 SEND_VAL int(5) 1
83+
0021 V0 = DO_UCALL
84+
0022 SEND_VAR V0 1
85+
0023 DO_ICALL
86+
0024 INIT_FCALL 1 %d string("var_dump")
87+
0025 INIT_FCALL 1 %d string("teststrstr3first")
88+
0026 SEND_VAL string("needles") 1
89+
0027 V0 = DO_UCALL
90+
0028 SEND_VAR V0 1
91+
0029 DO_ICALL
92+
0030 INIT_FCALL 1 %d string("var_dump")
93+
0031 INIT_FCALL 1 %d string("teststrstr3second")
94+
0032 SEND_VAL string("needle") 1
95+
0033 V0 = DO_UCALL
96+
0034 SEND_VAR V0 1
97+
0035 DO_ICALL
98+
0036 INIT_FCALL 1 %d string("var_dump")
99+
0037 INIT_FCALL 1 %d string("teststrstr3third")
100+
0038 SEND_VAL bool(false) 1
101+
0039 V0 = DO_UCALL
102+
0040 SEND_VAR V0 1
103+
0041 DO_ICALL
104+
0042 RETURN int(1)
105+
106+
testTrim1:
107+
; (lines=4, args=1, vars=1, tmps=%d)
108+
; (after optimizer)
109+
; %s
110+
0000 CV0($value) = RECV 1
111+
0001 T1 = FRAMELESS_ICALL_1(trim) CV0($value)
112+
0002 ASSIGN CV0($value) T1
113+
0003 RETURN CV0($value)
114+
115+
testMin2First:
116+
; (lines=5, args=1, vars=1, tmps=%d)
117+
; (after optimizer)
118+
; %s
119+
0000 CV0($value) = RECV 1
120+
0001 T1 = FRAMELESS_ICALL_2(min) CV0($value) int(100)
121+
0002 CV0($value) = QM_ASSIGN T1
122+
0003 VERIFY_RETURN_TYPE CV0($value)
123+
0004 RETURN CV0($value)
124+
125+
testMin2Second:
126+
; (lines=5, args=1, vars=1, tmps=%d)
127+
; (after optimizer)
128+
; %s
129+
0000 CV0($value) = RECV 1
130+
0001 T1 = FRAMELESS_ICALL_2(min) int(100) CV0($value)
131+
0002 CV0($value) = QM_ASSIGN T1
132+
0003 VERIFY_RETURN_TYPE CV0($value)
133+
0004 RETURN CV0($value)
134+
135+
testMin2_TMP:
136+
; (lines=5, args=1, vars=1, tmps=%d)
137+
; (after optimizer)
138+
; %s
139+
0000 CV0($value) = RECV 1
140+
0001 T1 = ADD CV0($value) int(1)
141+
0002 CV0($value) = FRAMELESS_ICALL_2(min) T1 int(100)
142+
0003 VERIFY_RETURN_TYPE CV0($value)
143+
0004 RETURN CV0($value)
144+
145+
testStrstr3First:
146+
; (lines=6, args=1, vars=1, tmps=%d)
147+
; (after optimizer)
148+
; %s
149+
0000 CV0($value) = RECV 1
150+
0001 T1 = FRAMELESS_ICALL_3(strstr) CV0($value) string("needle")
151+
0002 OP_DATA bool(false)
152+
0003 ASSIGN CV0($value) T1
153+
0004 VERIFY_RETURN_TYPE CV0($value)
154+
0005 RETURN CV0($value)
155+
LIVE RANGES:
156+
1: 0002 - 0003 (tmp/var)
157+
158+
testStrstr3Second:
159+
; (lines=6, args=1, vars=1, tmps=%d)
160+
; (after optimizer)
161+
; %s
162+
0000 CV0($value) = RECV 1
163+
0001 T1 = FRAMELESS_ICALL_3(strstr) string("needles") CV0($value)
164+
0002 OP_DATA bool(false)
165+
0003 ASSIGN CV0($value) T1
166+
0004 VERIFY_RETURN_TYPE CV0($value)
167+
0005 RETURN CV0($value)
168+
LIVE RANGES:
169+
1: 0002 - 0003 (tmp/var)
170+
171+
testStrstr3Third:
172+
; (lines=6, args=1, vars=1, tmps=%d)
173+
; (after optimizer)
174+
; %s
175+
0000 CV0($value) = RECV 1
176+
0001 T1 = FRAMELESS_ICALL_3(strstr) string("needles") string("needle")
177+
0002 OP_DATA CV0($value)
178+
0003 CV0($value) = QM_ASSIGN T1
179+
0004 VERIFY_RETURN_TYPE CV0($value)
180+
0005 RETURN CV0($value)
181+
LIVE RANGES:
182+
1: 0002 - 0003 (tmp/var)
183+
string(3) "boo"
184+
int(5)
185+
int(5)
186+
int(6)
187+
string(7) "needles"
188+
string(7) "needles"
189+
string(7) "needles"

0 commit comments

Comments
 (0)