diff --git a/NEWS b/NEWS index 0c80ffefb1b2..22c20881d6e5 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,8 @@ PHP NEWS - FPM: . Fixed bug GH-24229 (fcgi_close() passes shutdown() a literal 1, which is SHUT_RD in WASI). (Ryosuke Ishibashi) + . Fixed bug GH-21974 (Crash on worker shutdown in ZTS builds when + disable_functions is set in a pool configuration). (Sree24-ui) - Fileinfo: . Fixed bug GH-24289 (finfo::file() reports the wrong argument number for diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 732dc6dd79bd..407b80981724 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3660,6 +3660,26 @@ static void zend_disable_function(const char *function_name, size_t function_nam zend_error(E_WARNING, "Cannot disable function %s()", function_name); return; } +#ifdef ZTS + /* After zend_post_startup() the function table of a thread borrows the + * functions of the global function table, which destroys them on shutdown + * (see compiler_globals_ctor()). Remove them without destroying them. */ + HashTable *function_table = CG(function_table); + zval *zv = zend_hash_str_find(function_table, function_name, function_name_length); + if (zv && (uint32_t) ((Bucket *) zv - function_table->arData) < CG(copied_functions_count)) { + dtor_func_t dtor = function_table->pDestructor; + + function_table->pDestructor = NULL; + zend_hash_str_del(function_table, function_name, function_name_length); + function_table->pDestructor = dtor; + + /* Keep the remaining copied functions at the start of the table, + * as expected by compiler_globals_dtor(). */ + zend_hash_rehash(function_table); + CG(copied_functions_count)--; + return; + } +#endif zend_hash_str_del(CG(function_table), function_name, function_name_length); } diff --git a/sapi/fpm/tests/gh21974-disable-functions-worker-shutdown.phpt b/sapi/fpm/tests/gh21974-disable-functions-worker-shutdown.phpt new file mode 100644 index 000000000000..40ac13936d6c --- /dev/null +++ b/sapi/fpm/tests/gh21974-disable-functions-worker-shutdown.phpt @@ -0,0 +1,45 @@ +--TEST-- +FPM: GH-21974 - disable_functions set in a pool must not break shutdown of the worker +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->request()->expectBody('disabled'); +$tester->expectLogNotice('child \d+ exited with code 0 after \d+\.\d+ seconds from start', 'unconfined'); +$tester->expectLogNotice('child \d+ started', 'unconfined'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- +