[php-src] Issue #24661: FPM: pools with different disable_functions sharing one opcache run scripts wrongly (var_export() calls var_dump(), SIGSEGV in
the restricted pool) on 8.4/8.5/8.6

From: Date: Sat, 24 Oct 2026 18:58:39 +0000
Subject: [php-src] Issue #24661: FPM: pools with different disable_functions sharing one opcache run scripts wrongly (var_export() calls var_dump(), SIGSEGV in
the restricted pool) on 8.4/8.5/8.6
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253164@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24661 Author: Enfiladeguide ### Description Two pools in **one** FPM master, one with php_admin_value[disable_functions] and one without, share the opcache. Once a script has been compiled and cached through one pool, the other pool executes it wrongly: an internal function call is dispatched to a different internal function, and a worker of the restricted pool can crash. fpm.conf: ```ini [global] daemonize = no error_log = /dev/stderr [a] user = nobody listen = /tmp/a.sock listen.owner = nobody pm = static pm.max_children = 1 php_admin_value[disable_functions] = exec [b] user = nobody listen = /tmp/b.sock listen.owner = nobody pm = static pm.max_children = 1 ``` t.php: ```php <?php echo var_export([1, 'x' => true], true), "\n"; ``` Request t.php through pool a, then through pool b (a ~20-line FastCGI client, fcgi.php, is attached below so that no web server is needed). Resulting output, php:8.5-fpm (8.5.12), default settings (opcache is built in): ``` pool a: array ( 0 => 1, 'x' => true, ) pool b: array(2) { [0]=> int(1) ["x"]=> bool(true) } bool(true) ``` Pool b printed the output of var_dump() (and its return value) instead of var_export(). Expected output: the same text as pool a. With the order reversed (b first, then a) pool a, the one with exec disabled, prints debug_zval_dump() style output (array(2) interned {...}). On Ubuntu's 8.5.4 (8.5.4-0ubuntu1.5, -n with zend_extension=opcache) pool b fails with convert_uudecode() expects exactly 1 argument, 2 given for the same script, i.e. the call is dispatched to a third function. A script that calls the disabled function itself (t2.php: echo exec('echo hi');), requested through b first and then through a, kills the worker of the restricted pool: ``` WARNING: [pool a] child 10 exited on signal 11 (SIGSEGV) after 2.015193 seconds from start ``` What I checked: | | result | | -- | -- | | 8.5.12, 8.4.27 (official php:*-fpm images), 8.6.0RC4 (php:8.6-rc-fpm, ZTS), Ubuntu 8.5.4 | wrong function called | | 8.3.36 (php:8.3-fpm) | correct | | opcache.enable=0 | correct | | opcache.optimization_level=0 | correct | | 8.4 without zend_extension=opcache (-n) | correct | | default opcache.optimization_level with the bit 0x20 or the bit 0x40 cleared (one at a time; every other bit alone makes no difference) | correct | | two separate FPM masters, one pool each, same disable_functions split (8.5.12 and 8.4.27) | correct, no cross-effect | I did not trace what exactly gets cached or resolved by index or by pointer, so I cannot say more than that the optimizer passes 0x20 (DFA) and 0x40 (call graph) are both needed. I saw no case where a disabled function actually ran: the restricted pool crashed or printed the output of another function, but never executed exec. FPM accepts php_admin_value[disable_functions] per pool, so a setup like this is not exotic, and the symptom (wrong output rather than an error) is hard to diagnose; the workaround is opcache.enable=0, or one master per differing disable_functions set. I found no open or closed report about this. Related but different: #21974 (SIGSEGV with disable_functions per pool, ZTS builds only) and #24047 (system id and frameless functions). To reproduce (podman/docker; the directory holds fpm.conf, t.php, t2.php, fcgi.php, run.sh): ```sh docker run --rm -v "$PWD":/m docker.io/library/php:8.5-fpm sh /m/run.sh "a b a b" t.php docker run --rm -v "$PWD":/m docker.io/library/php:8.5-fpm sh /m/run.sh "b a" t2.php docker run --rm -v "$PWD":/m docker.io/library/php:8.5-fpm sh /m/run.sh "a b a b" t.php -d opcache.enable=0 # correct ``` <details><summary>control: one master per pool (correct on 8.5.12 and 8.4.27)</summary> ```sh sed -n '1,10p' /m/fpm.conf > /tmp/fpm-a.conf { sed -n '1,3p' /m/fpm.conf; sed -n '11,16p' /m/fpm.conf; } > /tmp/fpm-b.conf php-fpm -y /tmp/fpm-a.conf -F >/m/fpm-a.out 2>&1 & php-fpm -y /tmp/fpm-b.conf -F >/m/fpm-b.out 2>&1 & sleep 2 for p in a b a b b a; do printf -- "pool %s: " "$p"; php /m/fcgi.php /tmp/$p.sock /m/t.php 2>&1 | tr '\n' ' '; echo; done ``` </details> fcgi.php: ```php <?php // Minimal FastCGI GET client: php fcgi.php /tmp/a.sock /m/t.php [$sock, $script] = [$argv[1], $argv[2]]; $rec = fn(int $t, string $b) => pack('CCnnCx', 1, $t, 1, strlen($b), 0) . $b; $kv = function (string $k, string $v): string { $l = fn(int $n) => $n < 128 ? chr($n) : pack('N', $n | 0x80000000); return $l(strlen($k)) . $l(strlen($v)) . $k . $v; }; $p = $kv('SCRIPT_FILENAME', $script) . $kv('REQUEST_METHOD', 'GET') . $kv('SCRIPT_NAME', '/t.php') . $kv('SERVER_PROTOCOL', 'HTTP/1.1') . $kv('REQUEST_URI', '/t.php') . $kv('QUERY_STRING', ''); $s = stream_socket_client("unix://$sock", $en, $es, 10); fwrite($s, $rec(1, pack('nCx5', 1, 0)) . $rec(4, $p) . $rec(4, '') . $rec(5, '')); $out = ''; while (!feof($s)) { $h = stream_get_contents($s, 8); if (strlen($h) < 8) break; $d = unpack('Cv/Ct/nid/nlen/Cpad', $h); $body = $d['len'] + $d['pad'] ? stream_get_contents($s, $d['len'] + $d['pad']) : ''; if ($d['t'] === 6 || $d['t'] === 7) $out .= substr($body, 0, $d['len']); if ($d['t'] === 3) break; } echo preg_replace('~^.*?\r\n\r\n~s', '', $out) ?: "(empty response / connection reset)\n"; ``` run.sh: ```sh ord=$1; scr=$2; shift 2 php -v | head -1 php-fpm "$@" -y /m/fpm.conf -F >/m/fpm.out 2>&1 & sleep 2 for p in $ord; do printf -- "pool %s: " "$p"; php /m/fcgi.php /tmp/$p.sock /m/$scr 2>&1 | tr '\n' ' '; echo; done grep -E "SIGSEGV|signal" /m/fpm.out ``` ### PHP Version ``` PHP 8.5.12 (cli) (built: Oct 21 2026 15:49:51) (NTS) [php:8.5-fpm] PHP 8.4.27 (cli) (built: Oct 21 2026 15:53:31) (NTS) [php:8.4-fpm, opcache loaded] PHP 8.6.0RC4 (cli) (built: Oct 21 2026 22:05:36) (ZTS) [php:8.6-rc-fpm] PHP 8.5.4-0ubuntu1.5 (Ubuntu 26.04) ``` ### Operating System Debian (official Docker images), Ubuntu 26.04 --- *Disclosure: the investigation and this write-up were prepared with automated tooling; the reproduction was re-run on the official images (8.5.12) immediately before posting.*

« previous php.bugs (#253164) next »