Skip to content

Commit 16f1d6e

Browse files
andypostclaude
andcommitted
Fix DO_UCALL may_be_trampoline flag to match DO_FCALL
The ZEND_DO_UCALL handler hardcodes may_be_trampoline=0 when calling i_init_func_execute_data(), while ZEND_DO_FCALL passes 1. When the optimizer converts DO_FCALL to DO_UCALL for user functions that return by reference, the ASSIGN_REF opcode receives an incorrectly initialized return value, producing "Invalid opcode" errors or segfaults. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dc7f6a8 commit 16f1d6e

3 files changed

Lines changed: 48 additions & 7 deletions

File tree

Zend/zend_vm_def.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4209,7 +4209,7 @@ ZEND_VM_HOT_HANDLER(130, ZEND_DO_UCALL, ANY, ANY, SPEC(RETVAL,OBSERVER))
42094209

42104210
call->prev_execute_data = execute_data;
42114211
execute_data = call;
4212-
i_init_func_execute_data(&fbc->op_array, ret, 0 EXECUTE_DATA_CC);
4212+
i_init_func_execute_data(&fbc->op_array, ret, 1 EXECUTE_DATA_CC);
42134213
LOAD_OPLINE_EX();
42144214
ZEND_OBSERVER_SAVE_OPLINE();
42154215
ZEND_OBSERVER_FCALL_BEGIN(execute_data);

Zend/zend_vm_execute.h

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
DO_UCALL must not be used for functions returning by reference
3+
--DESCRIPTION--
4+
The optimizer's zend_get_call_op() converts DO_FCALL to DO_UCALL for user
5+
functions, but DO_UCALL hardcodes return_reference=0 in
6+
i_init_func_execute_data(). When the called function returns by reference
7+
(e.g. an overridden method using ASSIGN_REF), this produces invalid opcode
8+
sequences. The fix is either to not use DO_UCALL when the function has
9+
ZEND_ACC_RETURN_REFERENCE, or to make DO_UCALL honor it.
10+
--FILE--
11+
<?php
12+
class Base {
13+
protected function &getData(): array {
14+
$x = [];
15+
return $x;
16+
}
17+
18+
public function process(): array {
19+
if ($data = &$this->getData() && !isset($data['key'])) {
20+
// unreachable
21+
}
22+
return $data;
23+
}
24+
}
25+
26+
class Child extends Base {
27+
protected function &getData(): array {
28+
static $x = ['value' => 42];
29+
return $x;
30+
}
31+
}
32+
33+
$child = new Child();
34+
$result = $child->process();
35+
var_dump($result);
36+
?>
37+
--EXPECT--
38+
array(1) {
39+
["value"]=>
40+
int(42)
41+
}

0 commit comments

Comments
 (0)