The 21 October PHP Security Release
CVE-2026-109618 and CVE-2026-109619 are fixed in 8.2.35, 8.3.36, 8.4.27, and 8.5.12. The package on disk is a different thing from the worker still serving traffic.
What shipped
On 21 October 2026, PHP released 8.2.35, 8.3.36, 8.4.27, and 8.5.12. They fix CVE-2026-109618 and CVE-2026-109619. PHP scores both at CVSS 9.2 and describes the impact as unauthenticated remote code execution. The request can be handled before your script runs. PHP considers the risk highest on Apache with mod_php. The same bugs apply to PHP-FPM, php-cgi, FrankenPHP, and LiteSpeed.
PHP 8.1 and older sit outside the branches PHP still releases. The security team published minimal source backports for 7.4, 8.0, and 8.1 so people building from source have something to apply. A distribution package is the other way those versions get a fix.
Cloudflare had already reported attack traffic that morning, ahead of the scheduled 14:00 UTC publication. The packages are the fix. The settings further down close specific paths while you get there, and PHP's advisory is explicit that they leave an installation unsafe.
Two different failures
Each bug is a worker treating request state as valid when that state is unfinished or already gone. They fail at different moments, and the temporary setting for one does nothing about the other. The figure plays both.
PHP worker
1 / 3
- parse
- oom
- cleanup
worker
request data
The request is still starting. PHP is still building internal request data. No application code has run.
CVE-2026-109618 is the out-of-memory path. PHP can be interrupted while internal request data is only partly initialized. Cleanup then walks that data and treats it as valid.
memory_limit=-1 removes the controllable memory-limit failure when the application cannot put a finite limit back. Set it with php_admin_value in the FPM pool, or in the Apache configuration, and check that no pool, directory, or application setting restores a cap. An ordinary allocation failure can still reach the same path. A low post_max_size narrows it further: 8M when the app takes no uploads, 32M when it does, with upload_max_filesize kept below that. A cgroup limit can bound the worker. RLIMIT_AS and RLIMIT_DATA can cause the allocation failure this bug depends on, so leave those unset.
CVE-2026-109619 is the next request on the same worker. Fields that live for the whole process can still point at memory the previous request already freed. The following request may use them before normal initialization clears them.
One confirmed path is session.upload_progress. It has shipped on by default since PHP 5.4, and it runs while PHP is parsing a multipart/form-data body, including when the script never calls session_start(). On PHP 8.5, fatal-error backtraces are a second confirmed path. A larger worker pool makes it less likely that two requests land on the same process. The bug is still there.
Turn upload progress off in php.ini, an FPM pool, or other server configuration that applies before PHP parses the request, then restart every worker. ini_set() and a per-directory .user.ini run too late to matter. On 8.5, set fatal_error_backtraces=0 in that same early configuration. Hiding an early diagnostic with error_reporting, display_errors, or @ leaves the state that diagnostic created.
What to do
Install the release from the same place you already install PHP, then restart every process that still has the old binary mapped. Mattias Geniar's notes are the practical version: which distributions had packages on 22 October, and a command that lists processes still mapped to a deleted PHP file.
A few of those details are easy to miss:
- A PHP-FPM reload loads the new binary. Nothing else is required on nginx, Caddy, or Apache in front of FPM.
- Apache with mod_php needs a full stop and start.
apachectl gracefulkeeps the oldlibphpmapped. Mattias checked this by replacing the files on disk and reading what the process still had open. - LiteSpeed's
lsphpprocesses stay up across a LiteSpeed restart. They have to be stopped on their own after thelsphppackages are upgraded. - Queue workers, Supervisor jobs, and Laravel Octane keep the old interpreter until that process restarts.
php artisan queue:restartsignals queue workers.octane:reloadreloads your application code, so the Octane process itself has to be restarted for the new PHP. - On Ubuntu the package version can move while
php -vstays on the same upstream number, because the fix is backported. Read the package changelog, and read it from a worker, not from a shell that never goes through FPM. - Docker and FrankenPHP ship their own PHP.
aptanddnfon the host do not replace it. Pull the new image, or FrankenPHP 1.13.2 or later, and recreate the containers. The official Docker images were out on the 21st. - On the morning of the 21st, PHP said Remi's repository would not ship the fix that day. By that evening the supported branches were in Remi's own index, on x86_64 and aarch64. A stale mirror still offers the old build.
dnf clean metadataand another check is the way to tell.
Two stopgaps are in circulation until the workers are actually on the new binary. Set session.upload_progress.enabled=0 in the early configuration above, and set memory_limit=-1 where the application cannot restore a finite limit. If you can live without uploads for a few hours, reject multipart/form-data at the web server so PHP never parses that body. Sansec's note records the same boundary: either setting on its own is incomplete, and the multipart block breaks every real file upload. Take the block off once the new binary is what the workers are running, and put memory_limit back to a real value at the same time. The patched releases add a hard ceiling above memory_limit, and a worker that crosses it exits immediately. Left at -1, one request can use the whole machine.
Keep session.use_strict_mode=1. That is ordinary session advice, and it is the same advice it was before this release.
A site still on PHP 8.1 or older, with no backport from its distribution, keeps the stopgaps in place until it is on a build that contains the fix. A backport closes these two bugs. That old branch stays outside security support. PHP 8.2 itself reaches end of life on 31 December 2026.
8.4.27 and 8.5.12 also crash while garbage-collecting some SimpleXML objects. That comes from a normal bugfix in those two releases, and PHP fixed it in source on 22 October. 8.2.35 and 8.3.36 contain the security fix only, so they do not have the crash. The crash is a reason to watch 8.4 and 8.5 after you upgrade. The unpatched builds are the ones that are already being attacked.