Merge branch 'PHP-8.6'

* PHP-8.6:
  Fix GH-24264: Unwind/GracefulExit discarded in spl_iterators.c
This commit is contained in:
ndossche committed 2026-10-25 12:52:34 +01:00
commit 60e012b901
6 files changed
+145 -9

No files matched your search

+16 -9
View File
@@ -244,6 +244,13 @@ static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *k
}
}
static bool spl_can_catch_exception(zend_long flags)
{
return (flags & CIT_CATCH_GET_CHILD)
&& !zend_is_unwind_exit(EG(exception))
&& !zend_is_graceful_exit(EG(exception));
}
static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zval *zthis)
{
zend_object_iterator *iterator;
@@ -264,7 +271,7 @@ next_step:
case RS_NEXT:
iterator->funcs->move_forward(iterator);
if (EG(exception)) {
if (!(object->flags & RIT_CATCH_GET_CHILD)) {
if (!spl_can_catch_exception(object->flags)) {
return;
} else {
zend_clear_exception();
@@ -301,7 +308,7 @@ next_step:
zend_call_method_with_0_params(obj, ce, cache, "haschildren", &retval);
}
if (EG(exception)) {
if (!(object->flags & RIT_CATCH_GET_CHILD)) {
if (!spl_can_catch_exception(object->flags)) {
object->iterators[object->level].state = RS_NEXT;
return;
} else {
@@ -337,7 +344,7 @@ next_step:
}
object->iterators[object->level].state = RS_NEXT;
if (EG(exception)) {
if (!(object->flags & RIT_CATCH_GET_CHILD)) {
if (!spl_can_catch_exception(object->flags)) {
return;
} else {
zend_clear_exception();
@@ -366,7 +373,7 @@ next_step:
}
if (EG(exception)) {
if (!(object->flags & RIT_CATCH_GET_CHILD)) {
if (!spl_can_catch_exception(object->flags)) {
return;
} else {
zend_clear_exception();
@@ -410,7 +417,7 @@ next_step:
if (object->beginChildren) {
zend_call_known_function(object->beginChildren, Z_OBJ_P(zthis), object->ce, NULL, 0, NULL, NULL);
if (EG(exception)) {
if (!(object->flags & RIT_CATCH_GET_CHILD)) {
if (!spl_can_catch_exception(object->flags)) {
return;
} else {
zend_clear_exception();
@@ -424,7 +431,7 @@ next_step:
if (object->endChildren) {
zend_call_known_function(object->endChildren, Z_OBJ_P(zthis), object->ce, NULL, 0, NULL, NULL);
if (EG(exception)) {
if (!(object->flags & RIT_CATCH_GET_CHILD)) {
if (!spl_can_catch_exception(object->flags)) {
return;
} else {
zend_clear_exception();
@@ -2275,7 +2282,7 @@ static inline void spl_caching_it_next(spl_dual_it_object *intern)
zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "haschildren", &retval);
if (EG(exception)) {
zval_ptr_dtor(&retval);
if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
if (spl_can_catch_exception(intern->u.caching.flags)) {
zend_clear_exception();
} else {
return;
@@ -2291,7 +2298,7 @@ static inline void spl_caching_it_next(spl_dual_it_object *intern)
zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &args[0]);
if (EG(exception)) {
zval_ptr_dtor(&args[0]);
if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
if (spl_can_catch_exception(intern->u.caching.flags)) {
zend_clear_exception();
} else {
return;
@@ -2308,7 +2315,7 @@ static inline void spl_caching_it_next(spl_dual_it_object *intern)
);
zval_ptr_dtor(&args[0]);
if (is_initialized == FAILURE) {
if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
if (spl_can_catch_exception(intern->u.caching.flags)) {
zend_clear_exception();
} else {
return;
+24
View File
@@ -0,0 +1,24 @@
--TEST--
GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveIteratorIterator hasChildren()
--FILE--
<?php
class ReproIterator extends RecursiveArrayIterator {
public function hasChildren(): bool {
echo "exit\n";
exit(1);
}
}
$it = new RecursiveIteratorIterator(
new ReproIterator([1]),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD,
);
foreach ($it as $_) {}
echo "Not reached\n";
?>
--EXPECT--
exit
+23
View File
@@ -0,0 +1,23 @@
--TEST--
GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveCachingIterator hasChildren()
--FILE--
<?php
class ReproIterator extends RecursiveArrayIterator {
public function hasChildren(): bool {
echo "exit\n";
exit(1);
}
}
$it = new RecursiveCachingIterator(
new ReproIterator([1]),
CachingIterator::CATCH_GET_CHILD,
);
foreach ($it as $_) {}
echo "Not reached\n";
?>
--EXPECT--
exit
+24
View File
@@ -0,0 +1,24 @@
--TEST--
GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveIteratorIterator getChildren()
--FILE--
<?php
class ReproIterator extends RecursiveArrayIterator {
public function getChildren(): ?RecursiveArrayIterator {
echo "exit\n";
exit(1);
}
}
$it = new RecursiveIteratorIterator(
new ReproIterator([[1]]),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD,
);
foreach ($it as $_) {}
echo "Not reached\n";
?>
--EXPECT--
exit
+23
View File
@@ -0,0 +1,23 @@
--TEST--
GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveCachingIterator getChildren()
--FILE--
<?php
class ReproIterator extends RecursiveArrayIterator {
public function getChildren(): ?RecursiveArrayIterator {
echo "exit\n";
exit(1);
}
}
$it = new RecursiveCachingIterator(
new ReproIterator([[1]]),
CachingIterator::CATCH_GET_CHILD,
);
foreach ($it as $_) {}
echo "Not reached\n";
?>
--EXPECT--
exit
+35
View File
@@ -0,0 +1,35 @@
--TEST--
GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): destroyed fiber suspended in getChildren()
--FILE--
<?php
class ReproIterator extends RecursiveArrayIterator {
public function getChildren(): ?RecursiveArrayIterator {
try {
Fiber::suspend();
} finally {
echo "finally\n";
}
}
}
$fiber = new Fiber(function () {
$it = new RecursiveIteratorIterator(
new ReproIterator([[1], [2]]),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD,
);
foreach ($it as $_) {}
echo "Not reached\n";
});
$fiber->start();
unset($fiber);
echo "Done\n";
?>
--EXPECT--
finally
Done