
If you run PHP on anything that answers web requests, check your version before you read the rest of this. On October 21, the PHP project shipped emergency security releases for two critical vulnerabilities that can allow unauthenticated remote code execution. The fixed versions are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, and the project’s own advice is blunt: updating is the only complete fix.
This post covers what was fixed, how to patch each kind of setup, what to do if you can’t patch today, and how to check you weren’t hit in the meantime.
What actually happened
The PHP security team gave a day’s notice on October 20, saying both bugs were rated CVSS v4.0 9.2. The two issues:
CVE-2026-109618 (GHSA-w8hw-w9fx-j79w): remote code execution through out-of-memory corruption. If PHP runs out of memory at the wrong moment, it can be interrupted while internal request data is only partly initialized, and the cleanup that follows may treat that invalid data as valid.
CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp): remote code execution through stale global state. Some request-owned internal state can outlive the memory behind it, so a later request handled by the same worker may use that stale state before normal initialization resets it.
Who’s affected? Any PHP installation serving web requests: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The PHP team considers mod_php the highest risk, because they have a demonstrated exploit where an unauthenticated request triggers the attack before any application code runs. Which also means your own code quality isn’t what protects you here.
Step 1: Find every PHP you’re running
Most teams have more PHP than they think. Start with the obvious boxes, then go looking for the rest.
php -v # CLI only, may differ from what serves your site
php-fpm8.4 -v # or php-fpm -v, depending on your distro
apachectl -M 2>/dev/null | grep -i php # is mod_php loaded?Check web servers, container base images in your Dockerfiles, CI and staging images, queue-worker and cron machines, and any managed hosting panels. The CLI binary and the FPM or Apache one are often separate packages, so a patched php -v doesn’t prove the thing facing the internet is patched.
Step 2: Update
How you get there depends on the platform.
Debian and Ubuntu. Distributions received the patches in advance, but they usually backport fixes without changing the upstream version number, so don’t judge by the version string alone. Check the package changelog for the CVEs instead.
sudo apt update
sudo apt install --only-upgrade 'php8.4*'
apt changelog php8.4-fpm | grep -E 'CVE-2026-10961[89]'RHEL, Alma, Rocky, Fedora. Same idea:
sudo dnf upgrade 'php*'
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]'If you use a third-party repository such as Remi or Ondřej Surý’s PPA, confirm they’ve published the fixed build before assuming your package manager has it.
Docker. Rebuild from a fresh base and check the version inside the image rather than trusting the tag.
docker build --pull --no-cache -t myapp .
docker run --rm myapp php -vIf you pin a patch tag like php:8.4.26-fpm, bump it. If the official image for your branch hasn’t been rebuilt yet, that’s your signal to either wait a few hours or build from source.
Building from source. Download the release from the PHP downloads page, verify the checksum, and rebuild with the same configure flags you used before.
Managed hosting. Many platforms roll out PHP security releases for you, but confirm in the dashboard that the new patch version is actually live, especially if you pinned a version at some point.
Dev machines. Homebrew users can run brew update && brew upgrade php once the formula has been bumped; Windows builds are on php.net. Less urgent, but don’t leave a built-in dev server exposed on a shared network while you wait.
Step 3: Restart, then verify what’s running
Installing a package doesn’t touch processes that are already running. Restart them.
sudo systemctl restart php8.4-fpm # PHP-FPM
sudo systemctl restart apache2 # mod_phpDon’t forget long-running workers: queue consumers (for Laravel, php artisan queue:restart), Horizon, Octane, FrankenPHP worker mode, Swoole or RoadRunner, and anything cron launches as a daemon. CVE-2026-109619 is about state surviving between requests in the same worker, so stale workers are exactly what you want to flush.
Then confirm the web-facing process is the new one:
ps -o pid,lstart,cmd -C php-fpm8.4 # start time should be after your updateIf you can’t update today
The PHP team published stopgap settings, with a clear warning: they reduce exposure to specific attack paths but do not make an installation safe. Treat them as a bandage while you schedule the real fix.
For CVE-2026-109618 (out-of-memory corruption):
Set
memory_limit = -1, in a way applications can’t override (php_admin_valuein the FPM pool or Apache config), and check that no pool, directory or app setting puts a finite limit back. The catch: unlimited memory means a runaway script can eat the host, so pair it with a container or cgroup limit, ideally one that kills only the affected worker. Don’t substituteRLIMIT_ASorRLIMIT_DATA; they can cause the very allocation failure you’re trying to avoid.Lower
post_max_size: 8M if you don’t need uploads, 32M if you do, and keepupload_max_filesizebelow it. Where you can, enforce limits at the web server or proxy too (decoded request size, input field count, multipart part count, header count, nesting depth), including for requests without a usableContent-Length.
For CVE-2026-109619 (stale global state):
Set
session.upload_progress.enabled = 0inphp.ini, the FPM pool or server config, then restart every worker. Don’t rely onini_set()or.user.ini, because they apply too late. This blocks the confirmed session paths, not every possible variant.On PHP 8.5, set
fatal_error_backtraces = 0and restart. That closes only the confirmed fatal-backtrace path.Remove invalid or deprecated per-request settings that emit diagnostics before initialization. Hiding the messages with
error_reporting,display_errorsor@does not help.Fix attacker-reachable fatal errors and execution timeouts, including failures in callbacks, upload or import handling, and cleanup code.
An FPM pool example:
; /etc/php/8.4/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 2M
php_admin_value[session.upload_progress.enabled] = 0
; PHP 8.5 only:
; php_admin_value[fatal_error_backtraces] = 0Running something older than 8.2?
PHP 8.1 and older no longer get updates from the PHP project. In order of preference:
Upgrade to a supported branch. Aim for 8.4 or 8.5: 8.4 keeps getting security fixes through the end of 2028 and 8.5 through the end of 2029.
Use vendor backports. Some distributions and LTS vendors backport fixes for older versions. Ask yours which CVEs are covered.
Use the project’s emergency backports. The security team pushed minimal source backports for PHP 7.4, 8.0 and 8.1 for people building from source without a vendor. That’s a bridge, not a plan.
And if you’re on 8.2, note that it reaches end of life on December 31, 2026. 8.2.35 is a good moment to schedule the jump instead of patching and forgetting. One more thing: PHP 8.6.0 RC4 also contains the fixes, but it’s a test release, so keep it out of production.
Check you weren’t already hit
The advisory doesn’t mention confirmed attacks in the wild, but the gap between disclosure and exploitation is usually short for bugs like this. It also publishes no indicators of compromise, so this is general hygiene rather than a signature hunt. It’s worth twenty minutes:
Search PHP-FPM and Apache error logs from the last few weeks for repeated worker crashes (
exited on signal,zend_mm_heap corrupted) and out-of-memory spikes.Look for recently changed PHP files in your web roots and compare them with your deploy history:
find /var/www -name '*.php' -mtime -14 -lsReview cron jobs, user accounts and unexpected outbound connections from web hosts.
If anything looks wrong, treat the host as compromised: rebuild from a known-good image and rotate secrets (database credentials, API keys, app keys).
The rest of 2026’s PHP security fixes
If you’re jumping from an older patch level, you pick up everything in between too. The highlights:
Released | Versions | Notable security fixes |
|---|---|---|
Sep 24 | 8.5.11, 8.4.26, 8.3.35, 8.2.34 | SOAP server unbounded recursion (CVE-2026-91765) and an integer overflow in SOAP HTTP parsing (CVE-2025-14181); OpenSSL hostname verification falling back to CN after a SAN mismatch (CVE-2026-91769) and a heap overflow on crafted wildcard certificates (CVE-2026-91767); HTTP stream wrapper out-of-bounds read (CVE-2026-93682) and cross-origin credential leak on redirects (CVE-2026-91766); FPM IPv6 ACL bypass in |
Jul 30 | 8.5.9, 8.4.24, 8.3.33, 8.2.33 | CVE-2026-17543: SQL injection in the PostgreSQL extension ( |
Jul 2 | 8.5.8, 8.4.23, 8.3.32, 8.2.32 | CVE-2026-14355: memory corruption in |
May 7 | 8.5.6 (see the changelog for other branches) | SOAP use-after-free bugs (CVE-2026-6722, CVE-2026-7261, CVE-2026-7262); SQL injection via NUL bytes in PDO Firebird (CVE-2025-14179); XSS in the PHP-FPM status endpoint (CVE-2026-6735); a segfault in |
Two are worth a specific look in your own code. If you use ext-pgsql’s pg_* helper functions with user-supplied values, the July SQL injection fix matters a lot. And if you make outbound HTTP calls with the stream wrapper (file_get_contents(), fopen()) and send credentials, the redirect credential leak is relevant. For exact per-branch details, the PHP 8 changelog is the source of truth.
Make the next one less painful
Watch php.net news. Someone on your team should be reading it, because the heads-up for this release came only a day ahead.
Rebuild images on a schedule, with
--pull, and let Renovate or Dependabot bump pinned patch versions so a security release shows up as a pull request.Pair automatic OS updates with automatic restarts. On Debian and Ubuntu, tools like
needrestartexist for exactly this gap.Keep staging close to production so a same-day patch is a boring deploy.
Audit dependencies separately.
composer auditcovers your packages; it says nothing about the engine underneath them.
Checklist
Inventory every PHP runtime (web, CLI, workers, containers, CI)
Upgrade to a fixed version, or a confirmed vendor backport
Restart FPM, Apache and every long-running worker
Verify the web-facing process is the patched one
Apply the stopgap settings if you can’t update yet
Review logs and web roots for anything odd
Plan the move off 8.2 and anything older
Comments(0)
Join the discussion by logging into your account.
No comments yet. Be the first to comment!