From 2537b97beedd81c391dbf65ec7b1c7c6c11d728c Mon Sep 17 00:00:00 2001 From: ChrisAbner Date: Thu, 29 Oct 2026 18:49:57 -0500 Subject: [PATCH] Fix GH-24792: Assignment-result writes compile but fail with invalid opcode Since the VAR|TMP overhaul, zend_compile_var_inner() compiles assignment expressions for any fetch type, so using an assignment as the container of a write (e.g. ($a = [1])[0] = 2, unset(($a = [1])[0])) emitted a write fetch on a TMP operand, which has no handler. Only compile assignments in write context when they are passed by reference, which is the case the overhaul needed (f([&$x] = $arr)). Otherwise fall through to the "Cannot use temporary expression in write context" compile error, as in PHP 8.5. --- NEWS | 2 ++ Zend/tests/gh24792.phpt | 35 +++++++++++++++++++++++++++++++++++ Zend/tests/gh24792_001.phpt | 8 ++++++++ Zend/tests/gh24792_002.phpt | 8 ++++++++ Zend/tests/gh24792_003.phpt | 8 ++++++++ Zend/tests/gh24792_004.phpt | 8 ++++++++ Zend/tests/gh24792_005.phpt | 8 ++++++++ Zend/tests/gh24792_006.phpt | 9 +++++++++ Zend/tests/gh24792_007.phpt | 9 +++++++++ Zend/tests/gh24792_008.phpt | 9 +++++++++ Zend/zend_compile.c | 14 ++++++++++---- 11 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/gh24792.phpt create mode 100644 Zend/tests/gh24792_001.phpt create mode 100644 Zend/tests/gh24792_002.phpt create mode 100644 Zend/tests/gh24792_003.phpt create mode 100644 Zend/tests/gh24792_004.phpt create mode 100644 Zend/tests/gh24792_005.phpt create mode 100644 Zend/tests/gh24792_006.phpt create mode 100644 Zend/tests/gh24792_007.phpt create mode 100644 Zend/tests/gh24792_008.phpt diff --git a/NEWS b/NEWS index e1e63f21b0ad..1b735ee835c7 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ PHP NEWS (lazerg) . Fixed bug GH-25007 (Partial function application: named args in a constant expression cause out-of-bounds write in zp_compile()). (lazerg) + . Fixed bug GH-24792 (Assignment-result writes compile but fail with invalid + opcode). (ChrisAbner) - CLI: . Fixed bug GH-24697 (PHP_HTTP_PARSER_STRICT build option doesn't work). diff --git a/Zend/tests/gh24792.phpt b/Zend/tests/gh24792.phpt new file mode 100644 index 000000000000..60019796d9eb --- /dev/null +++ b/Zend/tests/gh24792.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-24792: Assignment results can still be read and passed by reference +--FILE-- + 'v'])['k'], $v); +var_dump(isset(($w = [1])[0]), ($w = [1])[5] ?? 'default', empty(($w = [1])[0])); + +$o = new stdClass; +$o->p = [1]; +var_dump(($c = $o)->p[0]); +?> +--EXPECT-- +int(9) +int(9) +string(1) "v" +array(1) { + ["k"]=> + string(1) "v" +} +bool(true) +string(7) "default" +bool(false) +int(1) diff --git a/Zend/tests/gh24792_001.phpt b/Zend/tests/gh24792_001.phpt new file mode 100644 index 000000000000..b024bb1bd34f --- /dev/null +++ b/Zend/tests/gh24792_001.phpt @@ -0,0 +1,8 @@ +--TEST-- +GH-24792: Dimension write on an assignment result +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_002.phpt b/Zend/tests/gh24792_002.phpt new file mode 100644 index 000000000000..21eb2597d2bb --- /dev/null +++ b/Zend/tests/gh24792_002.phpt @@ -0,0 +1,8 @@ +--TEST-- +GH-24792: Property write on an assignment result +--FILE-- +p = 1; +?> +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_003.phpt b/Zend/tests/gh24792_003.phpt new file mode 100644 index 000000000000..28e2d7b05275 --- /dev/null +++ b/Zend/tests/gh24792_003.phpt @@ -0,0 +1,8 @@ +--TEST-- +GH-24792: Compound assignment on a dimension of an assignment result +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_004.phpt b/Zend/tests/gh24792_004.phpt new file mode 100644 index 000000000000..7ab7792aa991 --- /dev/null +++ b/Zend/tests/gh24792_004.phpt @@ -0,0 +1,8 @@ +--TEST-- +GH-24792: Unset of a dimension of an assignment result +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_005.phpt b/Zend/tests/gh24792_005.phpt new file mode 100644 index 000000000000..82b476498ea5 --- /dev/null +++ b/Zend/tests/gh24792_005.phpt @@ -0,0 +1,8 @@ +--TEST-- +GH-24792: Reference to a dimension of an assignment result +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_006.phpt b/Zend/tests/gh24792_006.phpt new file mode 100644 index 000000000000..d6d9773598a6 --- /dev/null +++ b/Zend/tests/gh24792_006.phpt @@ -0,0 +1,9 @@ +--TEST-- +GH-24792: Dimension of an assignment result passed by reference +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_007.phpt b/Zend/tests/gh24792_007.phpt new file mode 100644 index 000000000000..45f13732ceac --- /dev/null +++ b/Zend/tests/gh24792_007.phpt @@ -0,0 +1,9 @@ +--TEST-- +GH-24792: Dimension write on a by-reference assignment result +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/tests/gh24792_008.phpt b/Zend/tests/gh24792_008.phpt new file mode 100644 index 000000000000..1a26caf224c3 --- /dev/null +++ b/Zend/tests/gh24792_008.phpt @@ -0,0 +1,9 @@ +--TEST-- +GH-24792: Dimension write on the result of a destructuring assignment with references +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use temporary expression in write context in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8b5f0251701c..82b6114bffe8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -12797,11 +12797,17 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty *result = *zend_ast_get_znode(ast); return NULL; case ZEND_AST_ASSIGN_REF: - zend_compile_assign_ref(result, ast, type); - return NULL; case ZEND_AST_ASSIGN: - zend_compile_assign(result, ast, false, type); - return NULL; + /* Assignment results may only be passed by reference, not written to. */ + if (by_ref || (type != BP_VAR_W && type != BP_VAR_RW && type != BP_VAR_UNSET)) { + if (ast->kind == ZEND_AST_ASSIGN_REF) { + zend_compile_assign_ref(result, ast, type); + } else { + zend_compile_assign(result, ast, false, type); + } + return NULL; + } + ZEND_FALLTHROUGH; default: if (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) { zend_error_noreturn(E_COMPILE_ERROR,