From 92417ca116dbca7e00475a459e5a02da1e48e9d1 Mon Sep 17 00:00:00 2001 From: lazerg Date: Mon, 12 Oct 2026 08:57:23 +0500 Subject: [PATCH 1/4] Fix GH-24264: Unwind/GracefulExit discarded in spl_iterators.c --- NEWS | 4 ++++ ext/spl/spl_iterators.c | 23 ++++++++++++++--------- ext/spl/tests/gh24264_1.phpt | 24 ++++++++++++++++++++++++ ext/spl/tests/gh24264_2.phpt | 23 +++++++++++++++++++++++ 4 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 ext/spl/tests/gh24264_1.phpt create mode 100644 ext/spl/tests/gh24264_2.phpt diff --git a/NEWS b/NEWS index aaed43baa2c8..1ae5447c9ad9 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,10 @@ PHP NEWS . Fixed use of uninitialized func in do_request() on OOM bailout. (David Carlier) +- SPL: + . Fixed bug GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c). + (Lazizbek Ergashev) + - Standard: . Fixed chown() and lchown() failing to resolve user names in ZTS builds when getpwnam_r() needs a larger buffer. (Ilia Alshanetsky) diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 425bd99f37c0..5bd52d4a4cb9 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -251,6 +251,11 @@ static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *k } } +static bool spl_can_catch_exception(void) +{ + return !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; @@ -271,7 +276,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv case RS_NEXT: iterator->funcs->move_forward(iterator); if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD)) { + if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { return; } else { zend_clear_exception(); @@ -308,7 +313,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv zend_call_method_with_0_params(obj, ce, cache, "haschildren", &retval); } if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD)) { + if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { object->iterators[object->level].state = RS_NEXT; return; } else { @@ -344,7 +349,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv } object->iterators[object->level].state = RS_NEXT; if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD)) { + if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { return; } else { zend_clear_exception(); @@ -373,7 +378,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv } if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD)) { + if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { return; } else { zend_clear_exception(); @@ -417,7 +422,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv if (object->beginChildren) { zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->beginChildren, "beginchildren", NULL); if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD)) { + if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { return; } else { zend_clear_exception(); @@ -431,7 +436,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv if (object->endChildren) { zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endChildren, "endchildren", NULL); if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD)) { + if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { return; } else { zend_clear_exception(); @@ -2300,7 +2305,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 ((intern->u.caching.flags & CIT_CATCH_GET_CHILD) && spl_can_catch_exception()) { zend_clear_exception(); } else { return; @@ -2316,7 +2321,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 ((intern->u.caching.flags & CIT_CATCH_GET_CHILD) && spl_can_catch_exception()) { zend_clear_exception(); } else { return; @@ -2333,7 +2338,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 ((intern->u.caching.flags & CIT_CATCH_GET_CHILD) && spl_can_catch_exception()) { zend_clear_exception(); } else { return; diff --git a/ext/spl/tests/gh24264_1.phpt b/ext/spl/tests/gh24264_1.phpt new file mode 100644 index 000000000000..cfa00cbe4261 --- /dev/null +++ b/ext/spl/tests/gh24264_1.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveIteratorIterator +--FILE-- + +--EXPECT-- +exit diff --git a/ext/spl/tests/gh24264_2.phpt b/ext/spl/tests/gh24264_2.phpt new file mode 100644 index 000000000000..5a4bbdeb6b80 --- /dev/null +++ b/ext/spl/tests/gh24264_2.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveCachingIterator +--FILE-- + +--EXPECT-- +exit From cc75dea2a7c74cc62ae09b0ec11fa88ad3c68eb3 Mon Sep 17 00:00:00 2001 From: lazerg Date: Sat, 24 Oct 2026 19:07:41 +0500 Subject: [PATCH 2/4] Add getChildren() tests for GH-24264 --- ext/spl/tests/gh24264_1.phpt | 2 +- ext/spl/tests/gh24264_2.phpt | 2 +- ext/spl/tests/gh24264_3.phpt | 24 ++++++++++++++++++++++++ ext/spl/tests/gh24264_4.phpt | 23 +++++++++++++++++++++++ ext/spl/tests/gh24264_5.phpt | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 ext/spl/tests/gh24264_3.phpt create mode 100644 ext/spl/tests/gh24264_4.phpt create mode 100644 ext/spl/tests/gh24264_5.phpt diff --git a/ext/spl/tests/gh24264_1.phpt b/ext/spl/tests/gh24264_1.phpt index cfa00cbe4261..dd0005a81589 100644 --- a/ext/spl/tests/gh24264_1.phpt +++ b/ext/spl/tests/gh24264_1.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveIteratorIterator +GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveIteratorIterator hasChildren() --FILE-- +--EXPECT-- +exit diff --git a/ext/spl/tests/gh24264_4.phpt b/ext/spl/tests/gh24264_4.phpt new file mode 100644 index 000000000000..b59950bc2a54 --- /dev/null +++ b/ext/spl/tests/gh24264_4.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): RecursiveCachingIterator getChildren() +--FILE-- + +--EXPECT-- +exit diff --git a/ext/spl/tests/gh24264_5.phpt b/ext/spl/tests/gh24264_5.phpt new file mode 100644 index 000000000000..620ee4a43403 --- /dev/null +++ b/ext/spl/tests/gh24264_5.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c): destroyed fiber suspended in getChildren() +--FILE-- +start(); +unset($fiber); + +echo "Done\n"; +?> +--EXPECT-- +finally +Done From a2e52ddb60e61a39483aa767b58a2dfa0f8824d7 Mon Sep 17 00:00:00 2001 From: lazerg Date: Sat, 24 Oct 2026 22:35:56 +0500 Subject: [PATCH 3/4] Drop NEWS entry --- NEWS | 4 ---- 1 file changed, 4 deletions(-) diff --git a/NEWS b/NEWS index 1ae5447c9ad9..aaed43baa2c8 100644 --- a/NEWS +++ b/NEWS @@ -48,10 +48,6 @@ PHP NEWS . Fixed use of uninitialized func in do_request() on OOM bailout. (David Carlier) -- SPL: - . Fixed bug GH-24264 (Unwind/GracefulExit discarded in spl_iterators.c). - (Lazizbek Ergashev) - - Standard: . Fixed chown() and lchown() failing to resolve user names in ZTS builds when getpwnam_r() needs a larger buffer. (Ilia Alshanetsky) From ed3c6f0bc349efcb0f07f7d3ab7b46b8d2d40a69 Mon Sep 17 00:00:00 2001 From: lazerg Date: Sun, 25 Oct 2026 06:47:40 +0500 Subject: [PATCH 4/4] Move CATCH_GET_CHILD check into spl_can_catch_exception() --- ext/spl/spl_iterators.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 5bd52d4a4cb9..05bdfdb34ba3 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -251,9 +251,11 @@ static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *k } } -static bool spl_can_catch_exception(void) +static bool spl_can_catch_exception(zend_long flags) { - return !zend_is_unwind_exit(EG(exception)) && !zend_is_graceful_exit(EG(exception)); + 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) @@ -276,7 +278,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv case RS_NEXT: iterator->funcs->move_forward(iterator); if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { + if (!spl_can_catch_exception(object->flags)) { return; } else { zend_clear_exception(); @@ -313,7 +315,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv zend_call_method_with_0_params(obj, ce, cache, "haschildren", &retval); } if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { + if (!spl_can_catch_exception(object->flags)) { object->iterators[object->level].state = RS_NEXT; return; } else { @@ -349,7 +351,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv } object->iterators[object->level].state = RS_NEXT; if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { + if (!spl_can_catch_exception(object->flags)) { return; } else { zend_clear_exception(); @@ -378,7 +380,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv } if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { + if (!spl_can_catch_exception(object->flags)) { return; } else { zend_clear_exception(); @@ -422,7 +424,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv if (object->beginChildren) { zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->beginChildren, "beginchildren", NULL); if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { + if (!spl_can_catch_exception(object->flags)) { return; } else { zend_clear_exception(); @@ -436,7 +438,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv if (object->endChildren) { zend_call_method_with_0_params(Z_OBJ_P(zthis), object->ce, &object->endChildren, "endchildren", NULL); if (EG(exception)) { - if (!(object->flags & RIT_CATCH_GET_CHILD) || !spl_can_catch_exception()) { + if (!spl_can_catch_exception(object->flags)) { return; } else { zend_clear_exception(); @@ -2305,7 +2307,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) && spl_can_catch_exception()) { + if (spl_can_catch_exception(intern->u.caching.flags)) { zend_clear_exception(); } else { return; @@ -2321,7 +2323,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) && spl_can_catch_exception()) { + if (spl_can_catch_exception(intern->u.caching.flags)) { zend_clear_exception(); } else { return; @@ -2338,7 +2340,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) && spl_can_catch_exception()) { + if (spl_can_catch_exception(intern->u.caching.flags)) { zend_clear_exception(); } else { return;