From 78251bcb23128125f7f6a15bcf68eba1189e62d4 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 20 Jul 2026 21:23:43 +0100 Subject: [PATCH] ext/standard: stream_filter_register() orphaned user_filter_map on shutdown re-registration. Fix GH-22818 During request shutdown the user_filter_map is torn down by the user_filters RSHUTDOWN before the streams referencing it are flushed. A user filter whose filter() callback re-registers the filter recreated the now-NULL map, then, since the volatile factory was still present in FG(stream_filters), deleted the freshly added entry and left an empty orphaned map behind. The following stream_filter_append() located the factory but no matching fdat, tripping ZEND_ASSERT(fdat), and the recreated map leaked. Register the volatile factory first and only create and populate user_filter_map on success, so a re-registration during the shutdown window fails without recreating the map. The existing NULL-map guard in user_filter_factory_create() then handles the append gracefully. --- ext/standard/tests/gh22818.phpt | 28 ++++++++++++++++++++++++++++ ext/standard/user_filters.c | 20 +++++++++----------- 2 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 ext/standard/tests/gh22818.phpt diff --git a/ext/standard/tests/gh22818.phpt b/ext/standard/tests/gh22818.phpt new file mode 100644 index 000000000000..e6ebaeecce5d --- /dev/null +++ b/ext/standard/tests/gh22818.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug GH-22818: user_filter_factory_create assertion failure on shutdown re-registration +--FILE-- + +--EXPECTF-- +done + +Warning: stream_filter_append(): Unable to create or locate filter "rotator_notWorking" in %s on line %d diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 986bbbd5f4d3..9d249ac3abfb 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -618,6 +618,13 @@ PHP_FUNCTION(stream_filter_register) RETURN_THROWS(); } + + /* Register the factory first; if that fails, don't (re)create the map, + * which would leak during shutdown re-registration. */ + if (php_stream_filter_register_factory_volatile(filtername, &user_filter_factory) == FAILURE) { + RETURN_FALSE; + } + if (!BG(user_filter_map)) { BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable)); zend_hash_init(BG(user_filter_map), 8, NULL, (dtor_func_t) filter_item_dtor, 0); @@ -626,17 +633,8 @@ PHP_FUNCTION(stream_filter_register) fdat = ecalloc(1, sizeof(struct php_user_filter_data)); fdat->classname = zend_string_copy(classname); - if (zend_hash_add_ptr(BG(user_filter_map), filtername, fdat) != NULL) { - if (php_stream_filter_register_factory_volatile(filtername, &user_filter_factory) == SUCCESS) { - RETURN_TRUE; - } - - zend_hash_del(BG(user_filter_map), filtername); - } else { - zend_string_release_ex(classname, 0); - efree(fdat); - } + zend_hash_add_ptr(BG(user_filter_map), filtername, fdat); - RETURN_FALSE; + RETURN_TRUE; } /* }}} */