Commit 6441f89857b for php.net
commit 6441f89857b3528e6b71a5f9ce6fa1ade3e67d7e
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Thu Apr 9 15:02:50 2026 -0400
Fix GH-21691: OPcache CFG optimizer drops QM_ASSIGN feeding JMPZ/JMPZ_EX with IS_VAR
The CFG optimizer (pass 5) substituted a QM_ASSIGN's source operand
into its consumer without checking the source's operand type. When
ASSIGN_REF produces IS_VAR and a QM_ASSIGN converts it to IS_TMP_VAR
before JMPZ/JMPNZ or JMPZ_EX/JMPNZ_EX, eliminating the QM_ASSIGN
leaves an IS_VAR operand on a consumer whose handler is declared
CONST|TMP|CV, producing "Invalid opcode 43/4/0" (or 46/4/0 for the
_EX variants).
Guard both substitution sites with src->op1_type != IS_VAR, folded
into the enclosing condition (per dstogov's review) so the BOOL_NOT
branch is defensively covered as well. Test exercises both JMPZ and
JMPZ_EX paths.
Closes GH-21691
Closes GH-21696
diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c
index 02c28ead33e..3775f8165c3 100644
--- a/Zend/Optimizer/block_pass.c
+++ b/Zend/Optimizer/block_pass.c
@@ -648,7 +648,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
} else if (opline->op1_type == IS_TMP_VAR &&
!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
src = VAR_SOURCE(opline->op1);
- if (src) {
+ if (src && (src->op1_type != IS_VAR)) {
if (src->opcode == ZEND_BOOL_NOT) {
VAR_SOURCE(opline->op1) = NULL;
COPY_NODE(opline->op1, src->op1);
@@ -692,7 +692,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
(!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
opline->result.var == opline->op1.var)) {
src = VAR_SOURCE(opline->op1);
- if (src) {
+ if (src && (src->op1_type != IS_VAR)) {
if (src->opcode == ZEND_BOOL ||
src->opcode == ZEND_QM_ASSIGN) {
VAR_SOURCE(opline->op1) = NULL;
diff --git a/ext/opcache/tests/gh21691.phpt b/ext/opcache/tests/gh21691.phpt
new file mode 100644
index 00000000000..3926b76c7a1
--- /dev/null
+++ b/ext/opcache/tests/gh21691.phpt
@@ -0,0 +1,20 @@
+--TEST--
+GH-21691 (OPcache CFG optimizer breaks reference returns with JMPZ/JMPZ_EX)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+
+function &getData() {
+ return $x;
+}
+
+$data = &getData() && isset($data['key']);
+var_dump($data);
+
+?>
+--EXPECT--
+NULL