[php-src] PR #24766: Fix GH-24661: FPM pools with different disable_functions share cached scripts

From: Date: Mon, 26 Oct 2026 23:11:16 +0000
Subject: [php-src] PR #24766: Fix GH-24661: FPM pools with different disable_functions share cached scripts
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-39944@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/24766 Author: Enfiladeguide Fixes GH-24661. *Quoted text written with an LLM and checked by a human developer.* > This follows the suggestion in GH-24661 to fix the FPM case with a mechanism similar to #24664. > > FPM applies a pool's disable_functions and PåÞ­=¶Ü<·ãÖ~oÐ > Ýextension in the worker processes (¼�–«ùF$¶ > ©F7žfpm_php_init_child()), after startup, while all pools of a master share one > opcache. A script compiled in one pool and run in another depends on the internal functions and > extensions it was compiled with: ZEND_INIT_FCALL offsets pick the > wrong function, a call to a function that the pool disables fails the assertion in > ZEND_INIT_FCALL (a NULL dereference in release builds), with > opcache.jit=function the disabled function is still called, and > constants of an extension loaded by one pool are substituted in scripts that the other pool runs. A > run-time check of the offsets would not be enough, since the optimizer also folds Æ|Ý6;òq > &ö'¾function_exists() and the JIT embeds the function. > > With this patch, opcache checks in RINIT whether the internal functions or extensions differ > from those at startup. If they do, the process keeps its scripts apart from processes that differ: > > - in shared memory, by mixing a hash of them into root_hash, as > for chroot; > - in the file cache, by using a system ID derived from > zend_system_id and that hash. > > After startup, functions are only removed (disable_functions) > or added with an extension, so the check in RINIT compares just the numbers of functions and > extensions. Processes that match the startup state use the cache as before, and pools with the same > functions and extensions share their entries. > > The file cache needs this too. Since the fix for GH-17733 (#24664), the > INIT_FCALL offsets are no longer zeroed there, and the system ID > covers the function table. But the system ID is finalized in > php_module_startup() of the master, before a pool applies its > disable_functions, so pools with > opcache.file_cache_only=1 read each other's offsets. With this > patch they use different directories. > > Limitations: > > - With opcache.preload, nothing changes. Preloaded scripts are > stored at startup, so a pool with a separate namespace would not find them, and including a > preloaded file would fail with "Cannot redeclare function". They are also compiled with > the functions of the master in any case. > - opcache_invalidate() only invalidates the entry of the > calling pool, as with chroot. opcache_reset() is not affected. > - Pools that differ each cache their own copy of a script. > > Tests: three FPM tests, for two pools with different > disable_functions (shared memory and file cache, both request > orders), disable_functions in > PHP_ADMIN_VALUE, and a pool that loads an extension and disables as > many functions as it adds. They fail on PHP-8.4 and pass with this patch, also on PHP-8.5, PHP-8.6 > and master. With opcache in shared memory and file cache modes, with JIT, with preload, and in a ZTS > build with ASAN, no other test fails that does not fail without the patch.

« previous php.git-pulls (#39944) next »