Skip to content

Commit 7e08018

Browse files
committed
Fix crush after compilation of nullsafe operator introduced in 307e476
Now we flush only delayed opcodes realted to this nullsafe operator. Fixes oss-fuzz #42152
1 parent 7d4fdf1 commit 7e08018

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

Zend/tests/bug81216_2.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Bug #81216_2: Nullsafe operator leaks dynamic property name
3+
--FILE--
4+
<?php
5+
$a = [null];
6+
$a[1] = $a[0]?->x;
7+
var_dump($a);
8+
?>
9+
--EXPECT--
10+
array(2) {
11+
[0]=>
12+
NULL
13+
[1]=>
14+
NULL
15+
}

Zend/zend_compile.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,9 +2258,11 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
22582258

22592259
ZEND_ASSERT(count >= offset);
22602260
for (i = offset; i < count; ++i) {
2261-
if (oplines[i].opcode != ZEND_NOP) {
2261+
if (EXPECTED(oplines[i].opcode != ZEND_NOP)) {
22622262
opline = get_next_op();
22632263
memcpy(opline, &oplines[i], sizeof(zend_op));
2264+
} else {
2265+
opline = CG(active_op_array)->opcodes + oplines[i].extended_value;
22642266
}
22652267
}
22662268

@@ -2888,15 +2890,28 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
28882890
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
28892891
zend_separate_if_call_and_write(&obj_node, obj_ast, type);
28902892
if (nullsafe) {
2891-
/* Flush delayed oplines */
2892-
zend_op *opline = NULL, *oplines = zend_stack_base(&CG(delayed_oplines_stack));
2893-
uint32_t i, count = zend_stack_count(&CG(delayed_oplines_stack));
2894-
2895-
for (i = 0; i < count; ++i) {
2896-
if (oplines[i].opcode != ZEND_NOP) {
2897-
opline = get_next_op();
2898-
memcpy(opline, &oplines[i], sizeof(zend_op));
2899-
oplines[i].opcode = ZEND_NOP;
2893+
if (obj_node.op_type == IS_TMP_VAR) {
2894+
/* Flush delayed oplines */
2895+
zend_op *opline = NULL, *oplines = zend_stack_base(&CG(delayed_oplines_stack));
2896+
uint32_t var = obj_node.u.op.var;
2897+
uint32_t count = zend_stack_count(&CG(delayed_oplines_stack));
2898+
uint32_t i = count;
2899+
2900+
while (i > 0 && oplines[i-1].result_type == IS_TMP_VAR && oplines[i-1].result.var == var) {
2901+
i--;
2902+
if (oplines[i].op1_type == IS_TMP_VAR) {
2903+
var = oplines[i].op1.var;
2904+
} else {
2905+
break;
2906+
}
2907+
}
2908+
for (; i < count; ++i) {
2909+
if (oplines[i].opcode != ZEND_NOP) {
2910+
opline = get_next_op();
2911+
memcpy(opline, &oplines[i], sizeof(zend_op));
2912+
oplines[i].opcode = ZEND_NOP;
2913+
oplines[i].extended_value = opline - CG(active_op_array)->opcodes;
2914+
}
29002915
}
29012916
}
29022917
zend_emit_jmp_null(&obj_node);

0 commit comments

Comments
 (0)