1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fit JIT variable not stored before YIELD

JIT doesn't recognize that variables may be used after returning from a
trace due to YIELD, so some effects may never be stored to memory.

YIELD ops terminate trace recordings with ZEND_JIT_TRACE_STOP_RETURN, and are
handled mostly like RETURN. Here I change zend_jit_trace_execute() so that
YIELD terminates recordings with ZEND_JIT_TRACE_STOP_INTERPRETER instead,
to ensure that we recognize that variables may be used after returning from
the trace due to YIELD.

Fixes GH-19493
Closes GH-19515
This commit is contained in:
Arnaud Le Blanc
2025-08-18 15:14:35 +02:00
parent 0fc3a2e624
commit bc05bfe7c5
4 changed files with 64 additions and 2 deletions

3
NEWS
View File

@@ -6,6 +6,9 @@ PHP NEWS
. Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler()
triggers "Constant already defined" warning). (ilutov)
- Opcache:
. Fixed bug GH-19493 (JIT variable not stored before YIELD). (Arnaud)
- OpenSSL:
. Fixed bug GH-19245 (Success error message on TLS stream accept failure).
(Jakub Zelenka)

View File

@@ -937,11 +937,18 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
break;
}
#ifdef HAVE_GCC_GLOBAL_REGS
const zend_op *prev_opline = opline;
#endif
handler = (zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler;
#ifdef HAVE_GCC_GLOBAL_REGS
handler();
if (UNEXPECTED(opline == zend_jit_halt_op)) {
stop = ZEND_JIT_TRACE_STOP_RETURN;
if (prev_opline->opcode == ZEND_YIELD || prev_opline->opcode == ZEND_YIELD_FROM) {
stop = ZEND_JIT_TRACE_STOP_INTERPRETER;
} else {
stop = ZEND_JIT_TRACE_STOP_RETURN;
}
opline = NULL;
halt = ZEND_JIT_TRACE_HALT;
break;
@@ -951,7 +958,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
rc = handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
if (rc != 0) {
if (rc < 0) {
stop = ZEND_JIT_TRACE_STOP_RETURN;
if (opline->opcode == ZEND_YIELD || opline->opcode == ZEND_YIELD_FROM) {
stop = ZEND_JIT_TRACE_STOP_INTERPRETER;
} else {
stop = ZEND_JIT_TRACE_STOP_RETURN;
}
opline = NULL;
halt = ZEND_JIT_TRACE_HALT;
break;

View File

@@ -0,0 +1,24 @@
--TEST--
GH-19493 001: Var not stored before YIELD
--FILE--
<?php
function f() {
$offset = 0;
yield true;
for ($i = 0; $i < 100; $i++) {
$offset++;
if ($offset === 99) {
break;
}
yield true;
}
return $offset;
}
$gen = f();
foreach ($gen as $v) {}
var_dump($gen->getReturn());
?>
--EXPECT--
int(99)

View File

@@ -0,0 +1,24 @@
--TEST--
GH-19493 002: Var not stored before YIELD_FROM
--FILE--
<?php
function f() {
$offset = 0;
yield from [true];
for ($i = 0; $i < 100; $i++) {
$offset++;
if ($offset === 99) {
break;
}
yield from [true];
}
return $offset;
}
$gen = f();
foreach ($gen as $v) {}
var_dump($gen->getReturn());
?>
--EXPECT--
int(99)