mod_php has a demonstrated pre-application exploit. Here is the patch order, the php.ini and FPM settings that narrow each attack path, and the trade-offs those settings carry.
PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix two critical vulnerabilities, CVE-2026-109618 and CVE-2026-109619. Either one can allow unauthenticated remote code execution. The exposure is not limited to one SAPI. PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed are all in scope, so any PHP installation serving web requests should be treated as affected until it is patched.
The PHP project considers mod_php the highest-risk setup. It has a demonstrated exploit where an unauthenticated request triggers the attack before any application code runs. Your framework's middleware, WAF rules written in PHP and authentication layers never get a chance to intervene. If an Apache box is still loading libphp directly, patch it first.
There is also a third, lower-priority issue, CVE-2026-115560. It depends on an unusual combination of session configuration and runtime code. One configuration change covers it, and it overlaps with a mitigation for CVE-2026-109619. The details are below, along with what each workaround does and does not protect against.
What the two vulnerabilities actually are
CVE-2026-109618 (GHSA-w8hw-w9fx-j79w), RCE via OOM corruption. Out-of-memory handling can interrupt PHP while internal request data is only partially initialized. Request cleanup may then treat that half-built data as valid and act on it. The attack exploits the gap between 'allocation failed' and 'cleanup assumes everything was set up'. Any attacker who can push a request into a memory failure at the right moment has a path in.
CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp), RCE via stale global state. Request-owned internal state can survive after the memory backing it has been freed. A later request handled by the same worker may use that stale state before normal initialization resets it. This is a cross-request problem. It matters most in persistent-worker models, where one process serves request after request: FPM pools, mod_php children, FrankenPHP workers.
Both bugs sit in the engine's request lifecycle rather than in an extension you can disable. That is why the mitigations below narrow specific attack paths and none of them closes the bug. The full technical write-ups are in the advisories linked from the php.net release announcement.
Which versions to install, and where to get them
The fixed releases for each supported branch are:
- PHP 8.5: 8.5.12
- PHP 8.4: 8.4.27
- PHP 8.3: 8.3.36
- PHP 8.2: 8.2.35
Source tarballs, and Windows source and binaries, are on the php.net downloads page. Most production servers run distribution or vendor packages, though, and Linux distributions and LTS vendors received the patches in advance. Check your vendor's security tracker for the package version that carries the fix. A distro build often keeps an older upstream version number with a patch-level suffix, so php -v alone may not tell you whether you are covered. Compare against the vendor's advisory, not the upstream version string.
PHP 8.1 and older are end of life upstream. Because these bugs are severe, the PHP security team published minimal source backports for PHP 7.4, 8.0 and 8.1. They are aimed at people who build from source or have no LTS vendor. They are a stopgap, not a support commitment. Some distributions and LTS vendors also ship backported fixes for these branches, so ask your vendor before rebuilding PHP yourself.
If you are testing PHP 8.6, RC4 already contains both fixes, and anyone on an earlier 8.6.0 build should move to it. It remains a pre-release that the PHP team says must not run in production. The last release candidate, RC5, is planned for 5 November 2026.
Containing the OOM corruption path (CVE-2026-109618)
If you cannot deploy the update today, three measures reduce exposure to the OOM path. None of them makes the installation safe.
Remove the controllable memory limit. Set memory_limit=-1 where applications cannot override it. In an FPM pool, use php_admin_value[memory_limit] = -1. In Apache, use php_admin_value memory_limit -1. Then audit every pool file, vhost, .htaccess and application bootstrap for anything that puts a finite limit back. One forgotten ini_set('memory_limit', '256M') in a framework config undoes the mitigation for that code path. This removes the attacker-steerable 'PHP memory limit reached' failure. An underlying allocation failure from the OS can still reach the vulnerable path.
Shrink the request body. For applications that do not take file uploads, set post_max_size = 8M. If uploads are needed, use post_max_size = 32M, and keep upload_max_filesize below post_max_size. Go further at the web server or reverse proxy. Limit decoded request size, input field count, multipart part count, header count and nesting depth. Those limits must also apply to requests without a usable Content-Length, such as chunked bodies. Otherwise an attacker can stream past a check that only reads the header.
Cap memory outside PHP. Use a container or cgroup memory limit as an availability boundary. Prefer a policy that kills only the affected worker rather than the whole pool. Do not reach for RLIMIT_AS or RLIMIT_DATA (via ulimit -v, rlimit_core-style settings or systemd's equivalents) as a substitute. Those limits can cause the exact allocation failure that triggers the vulnerability.
Containing the stale global state path (CVE-2026-109619)
The stale-state bug has several confirmed trigger paths, and each mitigation closes one of them.
- Disable session upload progress. Set
session.upload_progress.enabled=0inphp.ini, an FPM pool (php_admin_flag[session.upload_progress.enabled] = off) or the server configuration, then restart every worker. The setting has to apply before request parsing, soini_set()and FPM.user.inifiles do not work here. This blocks the confirmed session paths but not other variants. - On PHP 8.5, set
fatal_error_backtraces=0and restart every worker. This blocks only the confirmed fatal-backtrace path. - Remove invalid or deprecated per-request settings that emit diagnostics before initialization. Suppressing the output with
error_reporting,display_errorsor the@operator does not help. The diagnostic is still generated, and generating it is what matters. - Fix attacker-reachable fatal errors and execution timeouts. Include failures inside callbacks, upload or import processing, and cleanup code.
The last item is the most expensive. Any endpoint where a crafted request can drive PHP into a fatal error or max_execution_time becomes a candidate trigger. Large CSV or XML imports, image processing and long report exports are typical. Finding those endpoints in a mature codebase takes days, which is a strong argument for shipping the patched build instead.
The third issue: CVE-2026-115560 and session serialize handlers
Separately, the PHP project recommends keeping session.upload_progress.enabled=0 until the next regular bugfix release, to close an RCE vector it describes as unlikely. A full advisory for CVE-2026-115560 is due with that release.
The preconditions are narrow, and all three must hold. The application configures session.serialize_handler as php_binary or php_serialize. At runtime, it switches the handler to php with ini_set() before calling session_start(). And session upload progress is still enabled. Applications that never change the serialize handler at runtime can keep upload progress on.
The same setting was already listed as a mitigation in the GHSA-6wmf-4rv5-p3rp advisory for CVE-2026-109619, published on 21 October 2026. If you applied it then, leave it in place and you need do nothing more. To check whether your code is affected, grep for runtime handler changes:
grep -rn "serialize_handler" --include=*.php /path/to/app
grep -rn "serialize_handler" /etc/php/A hit in application code that calls ini_set before session_start() is the pattern to look for.
A practical rollout order
Treat this as a priority patch with an ordering, not a single change window.
- Inventory SAPIs. List every host serving PHP and note whether it uses mod_php, FPM, php-cgi, FrankenPHP or LiteSpeed. Update mod_php hosts first.
- Apply the always-safe settings now. Set
session.upload_progress.enabled=0at the php.ini or pool level, andfatal_error_backtraces=0on 8.5. Few applications depend on upload progress, and both settings carry over cleanly after patching. - Install the vendor package or the upstream release, then restart every worker. A graceful FPM reload or
apachectl gracefulmust actually cycle the processes, because old workers keep running old code. - Verify from inside the web SAPI.
php -vandphp -ireport the CLI binary and CLI ini, which can differ from what FPM or Apache loaded. Check the running version and the effective values ofmemory_limit,post_max_sizeandsession.upload_progress.enabledthrough a request served by the real stack, behind authentication. - Container images: rebuild from an updated base image and redeploy. Patching a running container is lost on the next restart.
Where the workarounds do not fit, and what to watch for
The workarounds are exposure reduction, not a fix. The PHP project is explicit that only updating is a complete solution. A server with every mitigation applied still runs vulnerable code, and the stale-state bug has variants beyond the confirmed paths. Use the mitigations to cover the hours or days until packages land, not as the end state.
Some mitigations conflict with real workloads. memory_limit=-1 removes a guardrail many teams rely on to stop a runaway report or import from exhausting a host. Without a cgroup or container limit behind it, you trade an RCE risk for an availability risk. An 8M or 32M post_max_size breaks applications that accept large media or document uploads. Those need explicit per-vhost exceptions, or the upload endpoint has to move off the PHP path. Disabling session upload progress removes server-side progress bars for uploads that depend on it.
Watch for configuration that silently overrides your settings: per-directory php_value lines, a second FPM pool nobody remembered, framework bootstraps that call ini_set(). Also watch any host still on PHP 8.1 or older. The upstream backports exist, but running an end-of-life branch means the next issue may arrive without one. Finally, keep an eye on the next regular bugfix release and the CVE-2026-115560 advisory that comes with it. That is the point where re-enabling session upload progress can be reconsidered.
This article draws on Transport security has blocked a cleartext HTTP and answers to it (1, 2), on the Stack Exchange network, licensed under CC BY-SA 4.0.
Share this article
Comments