[php-src] master: Merge branch 'PHP-8.5' into PHP-8.6
| From: | ndossche | Date: | Sat, 10 Oct 2026 09:20:30 +0000 |
| Subject: | [php-src] master: Merge branch 'PHP-8.5' into PHP-8.6 | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-142148@lists.php.net to get a copy of this message | ||
Author: ndossche (ndossche)
Date: 2026-10-10T11:20:11+02:00
Commit: https://github.com/php/php-src/commit/99c35ed90d3851d03ee34bd12d97d84b513f4ad9
Raw diff: https://github.com/php/php-src/commit/99c35ed90d3851d03ee34bd12d97d84b513f4ad9.diff
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
Fix throwing behaviour with recursive arrays in VM
Changed paths:
A ext/opcache/tests/is_identical_nothrow_recursive_array.phpt
M NEWS
M Zend/zend_vm_def.h
M Zend/zend_vm_execute.h
Diff:
diff --git a/NEWS b/NEWS
index 747f07197ef1..a4b3d143c37c 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,7 @@ PHP NEWS
. Fixed memory manager keeping a block reallocated to exactly the size of
the next smaller bin in its larger bin, which efree_size() then freed
into the wrong one. (Marc Bennewitz)
+ . Fix throwing behaviour with recursive arrays in VM. (ndossche)
- FPM:
. Fixed bug GH-24229 (fcgi_close() passes shutdown() a literal 1, which is
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index cb00d358684b..eb0507da830e 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -10321,11 +10321,11 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_IDENTICAL, op->op2_type ==
IS_CONST && (Z_
ZEND_VM_SMART_BRANCH(result, 0);
}
-ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_IDENTICAL, op->op1_type == IS_CV && (op->op2_type
& (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info
& (MAY_BE_UNDEF|MAY_BE_REF)), ZEND_IS_IDENTICAL_NOTHROW, CV, CONST|CV, SPEC(COMMUTATIVE))
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_IDENTICAL, op->op1_type == IS_CV && (op->op2_type
& (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info
& (MAY_BE_UNDEF|MAY_BE_REF)) && !((op1_info & op2_info) &
MAY_BE_ARRAY_OF_ARRAY), ZEND_IS_IDENTICAL_NOTHROW, CV, CONST|CV, SPEC(COMMUTATIVE))
{
/* This is declared below the specializations for MAY_BE_LONG/MAY_BE_DOUBLE so those will be used
instead if possible. */
/* This optimizes $x === SOME_CONST_EXPR and $x === $y for non-refs and non-undef, which
can't throw. */
- /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
+ /* Comparing two arrays that may contain arrays is excluded, as recursion throws an Error. */
USE_OPLINE
zval *op1, *op2;
bool result;
@@ -10337,7 +10337,7 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_IDENTICAL, op->op1_type == IS_CV
&& (op->op2_t
ZEND_VM_SMART_BRANCH(result, 0);
}
-ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_IDENTICAL, op->op1_type == IS_CV &&
(op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF))
&& !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF)), ZEND_IS_NOT_IDENTICAL_NOTHROW, CV, CONST|CV,
SPEC(COMMUTATIVE))
+ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_IDENTICAL, op->op1_type == IS_CV &&
(op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF))
&& !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !((op1_info & op2_info) &
MAY_BE_ARRAY_OF_ARRAY), ZEND_IS_NOT_IDENTICAL_NOTHROW, CV, CONST|CV, SPEC(COMMUTATIVE))
{
USE_OPLINE
zval *op1, *op2;
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 9be9b8441c87..6ffb43f57cab 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -45021,7 +45021,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV
ZEND_IS_IDENTICAL_
{
/* This is declared below the specializations for MAY_BE_LONG/MAY_BE_DOUBLE so those will be used
instead if possible. */
/* This optimizes $x === SOME_CONST_EXPR and $x === $y for non-refs and non-undef, which
can't throw. */
- /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
+ /* Comparing two arrays that may contain arrays is excluded, as recursion throws an Error. */
USE_OPLINE
zval *op1, *op2;
bool result;
@@ -53653,7 +53653,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV
ZEND_IS_IDENTICAL_
{
/* This is declared below the specializations for MAY_BE_LONG/MAY_BE_DOUBLE so those will be used
instead if possible. */
/* This optimizes $x === SOME_CONST_EXPR and $x === $y for non-refs and non-undef, which
can't throw. */
- /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
+ /* Comparing two arrays that may contain arrays is excluded, as recursion throws an Error. */
USE_OPLINE
zval *op1, *op2;
bool result;
@@ -97682,7 +97682,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV
ZEND_IS_IDENTICAL_NOTHR
{
/* This is declared below the specializations for MAY_BE_LONG/MAY_BE_DOUBLE so those will be used
instead if possible. */
/* This optimizes $x === SOME_CONST_EXPR and $x === $y for non-refs and non-undef, which
can't throw. */
- /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
+ /* Comparing two arrays that may contain arrays is excluded, as recursion throws an Error. */
USE_OPLINE
zval *op1, *op2;
bool result;
@@ -106212,7 +106212,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV
ZEND_IS_IDENTICAL_NOTHR
{
/* This is declared below the specializations for MAY_BE_LONG/MAY_BE_DOUBLE so those will be used
instead if possible. */
/* This optimizes $x === SOME_CONST_EXPR and $x === $y for non-refs and non-undef, which
can't throw. */
- /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */
+ /* Comparing two arrays that may contain arrays is excluded, as recursion throws an Error. */
USE_OPLINE
zval *op1, *op2;
bool result;
@@ -123537,7 +123537,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op,
uint32_t
break;
}
spec = 3102 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
- } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV))
&& !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info &
(MAY_BE_UNDEF|MAY_BE_REF))) {
+ } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV))
&& !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info &
(MAY_BE_UNDEF|MAY_BE_REF)) && !((op1_info & op2_info) & MAY_BE_ARRAY_OF_ARRAY)) {
spec = 3108 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
@@ -123560,7 +123560,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op,
uint32_t
break;
}
spec = 3105 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
- } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV))
&& !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info &
(MAY_BE_UNDEF|MAY_BE_REF))) {
+ } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV))
&& !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info &
(MAY_BE_UNDEF|MAY_BE_REF)) && !((op1_info & op2_info) & MAY_BE_ARRAY_OF_ARRAY)) {
spec = 3113 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
diff --git a/ext/opcache/tests/is_identical_nothrow_recursive_array.phpt
b/ext/opcache/tests/is_identical_nothrow_recursive_array.phpt
new file mode 100644
index 000000000000..a9dcd5b057a9
--- /dev/null
+++ b/ext/opcache/tests/is_identical_nothrow_recursive_array.phpt
@@ -0,0 +1,39 @@
+--TEST--
+IS_IDENTICAL/IS_NOT_IDENTICAL must not use the NOTHROW handler for arrays that may contain arrays
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+function identical(array $a, array $b) {
+ $r = $a === $b;
+ echo "not reached\n";
+ return $r;
+}
+
+function not_identical(array $a, array $b) {
+ $r = $a !== $b;
+ echo "not reached\n";
+ return $r;
+}
+
+$x = [&$x];
+
+try {
+ identical($x, [[]]);
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), " on line ", $e->getLine(),
"\n";
+}
+
+try {
+ not_identical($x, [[]]);
+} catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), " on line ", $e->getLine(),
"\n";
+}
+?>
+--EXPECT--
+Error: Nesting level too deep - recursive dependency? on line 3
+Error: Nesting level too deep - recursive dependency? on line 9