From 875fb4a84d81819cf34197b3c743ac2fb1e06da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 2 Nov 2026 11:00:05 +0100 Subject: [PATCH] Run RSHUTDOWN for activated modules when a later RINIT bails out php_request_shutdown() only calls RSHUTDOWN once every RINIT returned, so the modules activated before a failing one never released their request state. With session.auto_start, the session stayed locked and every later request for it blocked forever in persistent SAPIs. --- Zend/zend_API.c | 31 ++++++--- .../tests/rinit_bailout_releases_lock.phpt | 63 +++++++++++++++++++ ext/zend_test/php_test.h | 1 + ext/zend_test/test.c | 7 +++ 4 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 ext/session/tests/rinit_bailout_releases_lock.phpt diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 8b7fa4c73b34..ba44074eb81a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3405,17 +3405,32 @@ void module_registry_unload(const zend_module_entry *module) ZEND_API void zend_activate_modules(void) /* {{{ */ { - zend_module_entry **p = module_request_startup_handlers; + zend_module_entry ** volatile p = module_request_startup_handlers; - while (*p) { - zend_module_entry *module = *p; + zend_try { + while (*p) { + zend_module_entry *module = *p; - if (module->request_startup_func(module->type, module->module_number)==FAILURE) { - zend_error(E_WARNING, "request_startup() for %s module failed", module->name); - exit(1); + if (module->request_startup_func(module->type, module->module_number)==FAILURE) { + zend_error(E_WARNING, "request_startup() for %s module failed", module->name); + exit(1); + } + p++; } - p++; - } + } zend_catch { + /* The request shutdown skips RSHUTDOWN unless every RINIT returned, + * release what the modules activated so far hold (e.g. session locks) */ + while (p > module_request_startup_handlers) { + zend_module_entry *module = *--p; + + if (module->request_shutdown_func) { + zend_try { + module->request_shutdown_func(module->type, module->module_number); + } zend_end_try(); + } + } + zend_bailout(); + } zend_end_try(); } /* }}} */ diff --git a/ext/session/tests/rinit_bailout_releases_lock.phpt b/ext/session/tests/rinit_bailout_releases_lock.phpt new file mode 100644 index 000000000000..7f6ba5cd029e --- /dev/null +++ b/ext/session/tests/rinit_bailout_releases_lock.phpt @@ -0,0 +1,63 @@ +--TEST-- +A request whose startup bails out after session.auto_start releases the session lock +--EXTENSIONS-- +session +zend_test +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +[] +[rinitbailout] diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index ee2e334fb55c..c75cfa8a29c7 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -58,6 +58,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) int register_passes; bool rearm_vm_interrupt; bool print_stderr_mshutdown; + zend_long bailout_in_rinit; zend_long limit_copy_file_range; int observe_opline_in_zendmm; zend_mm_heap* zend_orig_heap; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index a11fc54af465..409b0b7a6a00 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1374,6 +1374,7 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.rearm_vm_interrupt", "0", PHP_INI_SYSTEM, OnUpdateBool, rearm_vm_interrupt, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.print_stderr_mshutdown", "0", PHP_INI_SYSTEM, OnUpdateBool, print_stderr_mshutdown, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_ENTRY("zend_test.bailout_in_rinit", "0", PHP_INI_SYSTEM, OnUpdateLong, bailout_in_rinit, zend_zend_test_globals, zend_test_globals) #ifdef HAVE_COPY_FILE_RANGE STD_PHP_INI_ENTRY("zend_test.limit_copy_file_range", "-1", PHP_INI_ALL, OnUpdateLong, limit_copy_file_range, zend_zend_test_globals, zend_test_globals) #endif @@ -1625,6 +1626,12 @@ PHP_MSHUTDOWN_FUNCTION(zend_test) PHP_RINIT_FUNCTION(zend_test) { + /* number of requests whose startup fails */ + if (ZT_G(bailout_in_rinit) > 0) { + ZT_G(bailout_in_rinit)--; + zend_bailout(); + } + ALLOC_HASHTABLE(ZT_G(global_weakmap)); zend_hash_init(ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0); ZT_G(observer_nesting_depth) = 0;