Skip to content

Commit 0fe2444

Browse files
committed
Fix incorrect opline after deoptimization
Blacklisted side traces (aka JIT'ed exits) may return the previous opline after calling the original op handler. As a result, the op handler is called again by the VM. Fix this by always returning the opline returned by the original op handler. Always use zend_jit_vm_enter(jit, ref) to signal the VM that it must reload EG(current_execute_data) as it may have changed during the execution of the trace. Fixes GH-19486 Closes GH-19535
1 parent 5d5ef50 commit 0fe2444

File tree

3 files changed

+45
-10
lines changed

3 files changed

+45
-10
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ PHP NEWS
5454

5555
- Opcache:
5656
. Fixed bug GH-19493 (JIT variable not stored before YIELD). (Arnaud)
57+
. Fixed bug GH-19486 (Incorrect opline after deoptimization). (Arnaud)
5758

5859
- OpenSSL:
5960
. Implement #81724 (openssl_cms_encrypt only allows specific ciphers).

ext/opcache/jit/zend_jit_ir.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17377,16 +17377,8 @@ static int zend_jit_trace_return(zend_jit_ctx *jit, bool original_handler, const
1737717377
addr = ir_CAST_FC_FUNC(addr);
1737817378
#endif
1737917379
ref = ir_CALL_2(IR_ADDR, addr, jit_FP(jit), jit_IP(jit));
17380-
if (opline &&
17381-
(opline->opcode == ZEND_RETURN
17382-
|| opline->opcode == ZEND_RETURN_BY_REF
17383-
|| opline->opcode == ZEND_GENERATOR_RETURN
17384-
|| opline->opcode == ZEND_GENERATOR_CREATE
17385-
|| opline->opcode == ZEND_YIELD
17386-
|| opline->opcode == ZEND_YIELD_FROM)) {
17387-
zend_jit_vm_enter(jit, ref);
17388-
return 1;
17389-
}
17380+
zend_jit_vm_enter(jit, ref);
17381+
return 1;
1739017382
}
1739117383
zend_jit_vm_enter(jit, jit_IP(jit));
1739217384
}

ext/opcache/tests/jit/gh19486.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
GH-19486: incorrect opline after deoptimization
3+
--INI--
4+
opcache.jit_blacklist_root_trace=1
5+
opcache.jit_blacklist_side_trace=1
6+
--FILE--
7+
<?php
8+
9+
(new GameMap())->getLakeArea(0, 0);
10+
11+
class GameMap
12+
{
13+
public $lakeID = [];
14+
15+
public function getLakeArea(int $x, int $y): int
16+
{
17+
$this->floodFill(0, 0, 0);
18+
}
19+
20+
public function floodFill(int $x, int $y, int $id): void
21+
{
22+
if (($x < 0) or ($x >= 50) or ($y < 0) or ($y >= 50)) {
23+
return;
24+
}
25+
if (isset($this->lakeID[$y][$x]) and ($this->lakeID[$y][$x] == $id)) {
26+
return;
27+
}
28+
$this->lakeID[$y][$x] = $id;
29+
$this->floodFill($x - 1, $y, $id);
30+
$this->floodFill($x + 1, $y, $id);
31+
$this->floodFill($x, $y - 1, $id);
32+
$this->floodFill($x, $y + 1, $id);
33+
}
34+
}
35+
36+
?>
37+
--EXPECTF--
38+
Fatal error: Uncaught TypeError: GameMap::getLakeArea(): Return value must be of type int, none returned in %s:%d
39+
Stack trace:
40+
#0 %s(%d): GameMap->getLakeArea(0, 0)
41+
#1 {main}
42+
thrown in %s on line %d

0 commit comments

Comments
 (0)