Commit ddbb581f1b2 for php

commit ddbb581f1b29d3bb14d4a437f1e75515b4de50c5
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date:   Thu Oct 1 22:44:57 2026 +0200

    Fix OSS-Fuzz #568005340: FETCH_DIM_FUNC_ARG partial conversion

    The new call is compiled to FETCH_DIM_FUNC_ARG going to
    FETCH_OBJ_FUNC_ARG. It tries to convert the FETCH_*_FUNC_ARG to FETCH_*_R, but
    it skips FETCH_DIM_FUNC_ARG with the UNUSED op2, and erroneously
    converts the next FETCH_OBJ_FUNC_ARG to FETCH_OBJ_R, which is an
    unexpected state for the optimizer.

    Interestingly, the reference path already had the appropriate check, so
    we move the check upwards and do the same thing as the reference path.

    Closes GH-24059.

diff --git a/NEWS b/NEWS
index fd17e6a83fe..f075f2ffbae 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,7 @@ PHP                                                                        NEWS
     "Too few arguments" errors and crashes). (RV7PR)
   . Fix type inference of ADD_ARRAY_UNPACK with integer keys. (ndossche)
   . Fix too wide type inference for ASSIGN_DIM_OP. (ndossche)
+  . Fix OSS-Fuzz #568005340 (FETCH_DIM_FUNC_ARG partial conversion). (ndossche)

 - SOAP:
   . Fixed use of uninitialized func in do_request() on OOM bailout.
diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c
index 5449535c560..03eb83cc46c 100644
--- a/Zend/Optimizer/optimize_func_calls.c
+++ b/Zend/Optimizer/optimize_func_calls.c
@@ -240,12 +240,13 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
 			case ZEND_FETCH_OBJ_FUNC_ARG:
 			case ZEND_FETCH_DIM_FUNC_ARG:
 				if (call_stack[call - 1].func_arg_num != (uint32_t)-1
+						&& call_stack[call - 1].last_check_func_arg_opline != NULL
 						&& has_known_send_mode(&call_stack[call - 1], call_stack[call - 1].func_arg_num)) {
 					if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
 						/* There's no TMP specialization for FETCH_OBJ_W/FETCH_DIM_W. Avoid
 						 * converting it and error at runtime in the FUNC_ARG variant. */
 						if ((opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG)
-						 && (opline->op1_type == IS_TMP_VAR || call_stack[call - 1].last_check_func_arg_opline == NULL)) {
+						 && opline->op1_type == IS_TMP_VAR) {
 							/* Don't remove the associated CHECK_FUNC_ARG opcode. */
 							call_stack[call - 1].last_check_func_arg_opline = NULL;
 							break;
@@ -261,6 +262,8 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
 							/* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
 							 * Performing the replacement would create an invalid opcode. */
 							call_stack[call - 1].try_inline = 0;
+							/* Don't remove the associated CHECK_FUNC_ARG opcode. */
+							call_stack[call - 1].last_check_func_arg_opline = NULL;
 							break;
 						}

diff --git a/ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt b/ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt
new file mode 100644
index 00000000000..4b2bcf01b49
--- /dev/null
+++ b/ext/opcache/tests/opt/fetch_dim_func_arg_unused_chain.phpt
@@ -0,0 +1,29 @@
+--TEST--
+FETCH_DIM_FUNC_ARG with UNUSED op2 followed by other FUNC_ARG fetches must not be partially converted
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function test() {
+    try {
+        new Node($a[]->b);
+    } catch (Error $e) {
+        echo $e->getMessage(), "\n";
+    }
+    try {
+        byVal($a[][0]);
+    } catch (Error $e) {
+        echo $e->getMessage(), "\n";
+    }
+}
+class Node { function __construct() {} }
+function byVal($x) {}
+test();
+?>
+--EXPECT--
+Cannot use [] for reading
+Cannot use [] for reading