From 165bff1e0949f2dcfd93ef5081c8f64ee723d887 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 15 Sep 2025 19:03:30 +0200 Subject: [PATCH] Fix ht corruption during shutdown for bailing user stream Fixes GH-19844 Closes GH-19849 --- Zend/tests/gh19844.phpt | 46 +++++++++++++++++++++++++++++++++++++++++ Zend/zend_execute_API.c | 4 +--- Zend/zend_list.c | 24 ++++++++++++++------- 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/gh19844.phpt diff --git a/Zend/tests/gh19844.phpt b/Zend/tests/gh19844.phpt new file mode 100644 index 0000000000000..2978640735357 --- /dev/null +++ b/Zend/tests/gh19844.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-19844: Bail from stream_close() in zend_shutdown_executor_values() +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Bail in %s on line %d + +Fatal error: Bail in %s on line %d + +Fatal error: Bail in %s on line %d diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index d4a373616fe92..e2503d6d3045d 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -274,9 +274,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) zval *zv; EG(flags) |= EG_FLAGS_IN_RESOURCE_SHUTDOWN; - zend_try { - zend_close_rsrc_list(&EG(regular_list)); - } zend_end_try(); + zend_close_rsrc_list(&EG(regular_list)); /* No PHP callback functions should be called after this point. */ EG(active) = 0; diff --git a/Zend/zend_list.c b/Zend/zend_list.c index bf599a2efca9b..5add19256a691 100644 --- a/Zend/zend_list.c +++ b/Zend/zend_list.c @@ -217,15 +217,25 @@ void zend_close_rsrc_list(HashTable *ht) /* Reload ht->arData on each iteration, as it may be reallocated. */ uint32_t i = ht->nNumUsed; - while (i-- > 0) { - zval *p = ZEND_HASH_ELEMENT(ht, i); - if (Z_TYPE_P(p) != IS_UNDEF) { - zend_resource *res = Z_PTR_P(p); - if (res->type >= 0) { - zend_resource_dtor(res); +retry: + zend_try { + while (i-- > 0) { + zval *p = ZEND_HASH_ELEMENT(ht, i); + if (Z_TYPE_P(p) != IS_UNDEF) { + zend_resource *res = Z_PTR_P(p); + if (res->type >= 0) { + zend_resource_dtor(res); + } } } - } + } zend_catch { + /* If we have bailed, we probably executed user code (e.g. user stream + * API). Keep closing resources so they don't leak. User handlers must be + * called now so they aren't called in zend_deactivate() on + * zend_destroy_rsrc_list(&EG(regular_list)). At that point, the executor + * has already shut down and the process would crash. */ + goto retry; + } zend_end_try(); }