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
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 48/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- php
- Bereich
- backend, performance
Rechercherichtung
Start by reproducing with the provided FPM configuration, scripts, and run.sh on PHP 8.5; compare pool request order and confirm that disabling OPcache or optimizer bits 0x20 and 0x40 avoids the issue. The report does not identify source files or tests, so trace the OPcache optimizer passes and how calls are resolved across pools. Done when shared OPcache preserves correct function dispatch for pools with different disable_functions settings, including the reported crash case.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Description
Quoted text written with an LLM and checked by a human developer.
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:
[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
echo var_export([1, 'x' => true], true), "\n";
Request
t.phpthrough poola, then through poolb(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
bprinted the output ofvar_dump()(and its return value) instead ofvar_export(). Expected output: the same text as poola. With the order reversed (bfirst, thena) poola, the one withexecdisabled, printsdebug_zval_dump()style output (array(2) interned {...}).
On Ubuntu's 8.5.4 (
8.5.4-0ubuntu1.5,-nwithzend_extension=opcache) poolbfails withconvert_uudecode() expects exactly 1 argument, 2 givenfor 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 throughbfirst and then througha, 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:*-fpmimages), 8.6.0RC4 (php:8.6-rc-fpm, ZTS), Ubuntu 8.5.4wrong function called 8.3.36 ( php:8.3-fpm)correct opcache.enable=0correct opcache.optimization_level=0correct 8.4 without zend_extension=opcache(-n)correct default opcache.optimization_levelwith the bit0x20or the bit0x40cleared (one at a time; every other bit alone makes no difference)correct two separate FPM masters, one pool each, same disable_functionssplit (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) and0x40(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 executedexec. FPM acceptsphp_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 isopcache.enable=0, or one master per differingdisable_functionsset.
I found no open or closed report about this.Same root cause as GH-17733. Related but different: #21974 (SIGSEGV withdisable_functionsper 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):
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
control: one master per pool (correct on 8.5.12 and 8.4.27)
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
fcgi.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:
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
- Vorherrschende Sprache
- C
- Sterne
- 41.8k
- Forks
- 8.5k
- Ø Merge
- 2 T. 13 Std.
- Gemergte PRs (30 T.)
- 145
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus php/php-src
-
ext/openssl: 8.2.31+/8.3.31+ fail to build against OpenSSL 1.0.2 (ASN1_STRING_get0_data in xp_ssl.c)Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 79/100
Maintainer antworten meist innerhalb von 1 Tag
-
Bug Status: Needs Triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
php/php-src#25397 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
SNMP accepts malformed remote portsEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offen
Schwierigkeit 2/5 Ein halber Tag Anfängerfreundlichkeit 62/100
php/php-src#25137 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Bug Status: Needs Triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
Maintainer antworten meist innerhalb von 1 Tag
-
quoted_printable_encode() produces lines longer than 76 characters for bytes 0xF5 to 0xFFEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
Maintainer antworten meist innerhalb von 1 Tag
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
-
not for AI lovers
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
OSGeo/gdal#15515 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Template: Bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 83/100
betaflight/betaflight#16014 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 75/100
ly05010419/MacNativeGames#2119 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100