From 90f1c24cf380a728724e79068cf920d63fa2cbbe Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Thu, 9 Apr 2026 15:02:50 -0400 Subject: [PATCH] Fix GH-21691: OPcache CFG optimizer eliminates QM_ASSIGN feeding JMPZ with VAR operand The CFG optimizer (pass 5) removed a QM_ASSIGN that converted IS_VAR to IS_TMP_VAR before JMPZ. JMPZ has no handler for IS_VAR operands, producing "Invalid opcode 43/4/0." This occurred when ASSIGN_REF (which produces IS_VAR) fed into a conditional via QM_ASSIGN. Skip the QM_ASSIGN elimination when the source operand is IS_VAR. Closes GH-21691 --- Zend/Optimizer/block_pass.c | 3 +++ ext/opcache/tests/gh21691.phpt | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 ext/opcache/tests/gh21691.phpt diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c index 61a69dae51e1a..704a16b3ae668 100644 --- a/Zend/Optimizer/block_pass.c +++ b/Zend/Optimizer/block_pass.c @@ -714,6 +714,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array continue; } else if (src->opcode == ZEND_BOOL || src->opcode == ZEND_QM_ASSIGN) { + if (src->op1_type == IS_VAR) { + break; + } VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); diff --git a/ext/opcache/tests/gh21691.phpt b/ext/opcache/tests/gh21691.phpt new file mode 100644 index 0000000000000..0956a0d62816a --- /dev/null +++ b/ext/opcache/tests/gh21691.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-21691 (OPcache CFG optimizer breaks reference returns with JMPZ) +--INI-- +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- +getData() && !isset($data['key'])) { + } + return $data; + } +} + +class Child extends Base { + protected function &getData(): array { + static $x = ['value' => 42]; + return $x; + } +} + +$child = new Child(); +var_dump($child->process()); +?> +--EXPECT-- +array(1) { + ["value"]=> + int(42) +}