# How to check the PHP build your web server runs and fix CVE-2026-109618 and CVE-2026-109619

URL: https://systemdesignsimple.com/php-rce-php2shell-check-and-fix/
Published: 2026-11-07
Updated: 2026-11-06
Category: Security
Publisher: System Design Simple (https://systemdesignsimple.com)

## Short answer

PHP below 8.5.12, 8.4.27, 8.3.36 or 8.2.35 has two CVSS 9.2 remote code execution flaws, CVE-2026-109618 and CVE-2026-109619, in how long-lived web workers handle request startup and cleanup. Update PHP, restart every worker, then check the version the web server reports, because the CLI can be a different build. php.ini mitigations only reduce exposure.

## Key facts

- **CVEs:** CVE-2026-109618 and CVE-2026-109619, both CVSS v4.0 9.2
- **Fixed in:** PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 (php.net announcement 2026-10-21)
- **Affected SAPIs:** PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed; highest demonstrated risk on mod_php
- **Older branches:** backported patches for 7.4, 8.0, 8.1; get them from your distro or LTS vendor
- **Complete fix:** updating PHP; php.net says the mitigations do not make an install safe
- **Our lab:** CLI check on PHP 8.5.2: affected version plus 3 advisory settings found; 3 settings changed under -d overrides; no exploit tested

PHP below 8.5.12, 8.4.27, 8.3.36 or 8.2.35 has two remote code execution flaws, CVE-2026-109618 and CVE-2026-109619 (CVSS v4.0 9.2), in how long-lived web workers start and clean up requests, with Apache mod_php the most exposed. Update to the fixed release for your branch, restart every worker, and check the version the web server reports, which is not always the one `php -v` prints.

This guide is for protecting servers and applications you own or maintain.

## Am I affected?

| Branch | Affected | Fixed in |
|---|---|---|
| 8.5 | below 8.5.12 | 8.5.12 |
| 8.4 | below 8.4.27 | 8.4.27 |
| 8.3 | below 8.3.36 | 8.3.36 |
| 8.2 | below 8.2.35 | 8.2.35 (end of life 2026-12-31) |
| 8.1, 8.0, 7.4 | all upstream releases | no project release; backported patches through your distro or LTS vendor |

The [php.net advisory](https://www.php.net/archive/2026.php#2026-10-21-5) names five SAPIs (the server interface PHP runs under): PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. Check the binary that serves requests, not the one on your PATH:

```sh
# PHP-FPM (Debian/Ubuntu name the binary per branch)
php-fpm -v || php-fpm8.3 -v
# Apache: is mod_php loaded at all?
apachectl -M 2>/dev/null | grep -i php
# Homebrew
$(brew --prefix php)/sbin/php-fpm -v
# containers
docker exec <container> php -v
# distro builds keep the old upstream number: list installed revisions
dpkg-query -W 'php*'          # Debian/Ubuntu
rpm -qa 'php*'                # RHEL family
```

Upstream builds below the table are affected. For distro builds, check your vendor's security advisory for the fixed package revision on your release and compare it with every installed PHP package the web service loads. `apt changelog` or `rpm -q --changelog` searches for the CVE ids are useful supporting evidence, but `apt changelog` can describe a newer available package, so the installed revision is what counts.

## Fix it now

```sh
# Homebrew (example; Homebrew currently offers 8.5.12_1)
brew update && brew upgrade php && brew services restart php

# Debian/Ubuntu: take the security update for every php package
sudo apt update && sudo apt install --only-upgrade 'php*'
sudo systemctl restart php8.3-fpm     # or: sudo systemctl restart apache2 for mod_php

# RHEL, Alma, Rocky
sudo dnf upgrade 'php*' && sudo systemctl restart php-fpm
```

For containers, the Dockerfile of the PHP service must start from a fixed official tag, for example `FROM php:8.5.12-fpm`, `php:8.4.27-apache` or `php:8.3.36-fpm-alpine`. Then:

```sh
# Compose service with build:
docker compose build --pull <php-service>
docker compose up -d --force-recreate <php-service>
# Compose service with image: update the tag in compose.yaml first
docker compose pull <php-service> && docker compose up -d --force-recreate <php-service>
```

Then prove the running process is the fixed build. A reload that keeps old workers alive leaves old code in memory, and the CLI may read a different php.ini than FPM.

```sh
# every FPM process started after the upgrade?
ps -o pid,lstart,command -C php-fpm8.3   # macOS: ps -o pid,lstart,command -p $(pgrep -d, php-fpm)

# what the web SAPI reports: temporary file, request it once, delete it
echo '<?php echo PHP_VERSION, " ", PHP_SAPI, " ", php_ini_loaded_file();' > /var/www/html/v-check.php
curl -s https://your-site.example/v-check.php; echo
rm /var/www/html/v-check.php
```

Expect the fixed version and `fpm-fcgi` or `apache2handler`. For containers, also confirm the image digest of the running container matches the one you just built or pulled.

## Why it is dangerous

PHP apps assume shared-nothing requests: each one starts clean and everything it allocated is gone at the end. Under PHP-FPM and mod_php the worker process outlives the request, so that promise depends on startup and shutdown code doing their job every time.

```text
HTTP request -> web server -> PHP worker (reused)
   1. request startup   parse body, multipart, session upload progress   <- no app code yet
   2. your script
   3. request shutdown  free request memory, reset state
   4. same worker takes the next request
```

CVE-2026-109618 lives between steps 1 and 3. Multipart metadata can take far more memory than its size on the wire, so a request can hit `memory_limit` while startup is still building request structures. On affected builds the out-of-memory path uses `longjmp()`, which jumps past the code that would have finished that half-built state, and shutdown later treats it as valid. The patch defers a soft out-of-memory error to a safe point in the VM and exits with `_exit()` at a higher hard limit ([GHSA-w8hw-w9fx-j79w](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w)).

The SAPI decides what happens next. According to the advisories, mod_php can run shutdown after request startup fails, so the invalid state is touched before any application code or authentication runs. FPM terminates a child whose startup fails, which stops that exact path. FPM remains affected through other paths, including the validated session lifecycle case, where application code runs after a successful startup.

CVE-2026-109619 crosses steps 3 and 4: memory owned by one request is freed while a pointer to it survives, and a later request on the same worker uses it ([GHSA-6wmf-4rv5-p3rp](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)). The broken component is the isolation between requests. Either flaw ends, at worst, in code execution as the worker user, with that user's file, socket and credential access.

## Reproduce

Our lab, `labs/security/php2shell-check-lab/php2shell-check-lab.mjs`, is a read-only configuration check. It asks the local PHP for its version and for the settings php.net ties to each CVE. It sends no attack input and does not trigger either bug. We ran it on this Mac on 2026-11-06 at 11:01 UTC against Homebrew PHP 8.5.2 in the CLI SAPI with its installed php.ini.

The CLI check found an affected upstream version and three settings covered by the advisory: `session.upload_progress.enabled` On, `memory_limit` a finite 128M, and `fatal_error_backtraces` On (an 8.5 setting).

## Experiment

The second run used the same CLI binary with three `-d` overrides for the php.net mitigations, plus a plain `session_start()` in a temp directory as the normal-use check:

| Check | Installed php.ini | With `-d` overrides |
|---|---|---|
| PHP version 8.5.2 | affected | affected, update needed |
| `session.upload_progress.enabled` (109619) | On | Off |
| `memory_limit` (109618) | 128M | -1 |
| `fatal_error_backtraces`, 8.5 only (109619) | On | Off |
| normal use: `session_start()` in a temp dir | session ok | session ok |

The simple CLI `session_start()` check passed in both runs.

## Fix

The complete fix is the update and a restart of PHP-FPM or Apache. Until then, set the mitigations where they apply before request parsing:

```ini
; php.ini or the FPM pool (pool syntax: php_admin_value[...] / php_admin_flag[...])
; not ini_set(), not .user.ini: those run too late or can be overridden
session.upload_progress.enabled = 0
memory_limit = -1            ; pool: php_admin_value[memory_limit] = -1
fatal_error_backtraces = 0   ; PHP 8.5 only
post_max_size = 8M           ; 32M if you accept uploads, upload_max_filesize below it
```

## Report

[Image: Terminal output of our php2shell-check-lab run: node php2shell-check-lab.mjs]

| Count | Result |
|---|---|
| Configuration findings observed (version plus advisory settings) | 4/4 |
| Settings changed under `-d` overrides | 3/3 |
| PHP version changed | no |
| Normal `session_start()` unchanged | 1/1 |

The capture shows the lab's raw labels: its "REPRODUCED" means a finding was observed and "BLOCKED" means the setting changed. No exploit was tested. The script prints PARTIAL because the version row stays affected until PHP itself is updated.

What our lab does not prove: it never triggers either vulnerability, so it cannot show that 8.5.2 is exploitable or that 8.5.12 closes the flaws; that rests on the advisories. It ran in the CLI SAPI, which can load a different php.ini than PHP-FPM or mod_php. It does not cover proxy limits, cgroup limits or per-directory overrides, and we did not record an upgrade or a post-restart web SAPI check on this Mac.

## If you cannot update today

php.net says these reduce exposure and do not make an install safe. Restart every worker after each change, then verify through the web SAPI with the temporary file method above, printing `ini_get()` for each setting.

| Mitigation (php.net) | Verify it took effect |
|---|---|
| `session.upload_progress.enabled=0` in php.ini, pool or server config | `ini_get('session.upload_progress.enabled')` returns `0` or empty from the web SAPI |
| PHP 8.5: `fatal_error_backtraces=0` | `ini_get('fatal_error_backtraces')` returns `0` or empty |
| `memory_limit=-1` via `php_admin_value`, no pool or directory putting a limit back | `ini_get('memory_limit')` returns `-1`; `grep -rn memory_limit` across pools, vhosts and `.user.ini` |
| `post_max_size` 8M (32M with uploads), `upload_max_filesize` below it | `ini_get` on both through the web SAPI; PHP empties `$_POST` and `$_FILES` above the limit rather than rejecting ([manual](https://www.php.net/manual/en/ini.core.php#ini.post-max-size)) |
| Request size, field, part, header and nesting limits at the web server or proxy, also for requests without `Content-Length` | send an oversized request, including a chunked one, to your own staging host and expect HTTP 413 from the proxy |
| Container or cgroup memory limit, never `RLIMIT_AS` or `RLIMIT_DATA` | `docker inspect -f '{{.HostConfig.Memory}}' <container>` or `cat /sys/fs/cgroup/memory.max` |
| Remove invalid or deprecated per-request settings, fix reachable fatal errors and timeouts | the error log shows no startup warnings or fatal errors under normal traffic |

Turning off `display_errors` or adding `@` does not help, according to php.net. Our [PHP security checklist](/php-security-common-vulnerabilities/) covers the application side, and the [WordPress core upgrade check](/wordpress-core-upgrade-flaw-check-and-fix/) is the layer above PHP if you run WordPress.

## Timeline and sources

| Date | Event |
|---|---|
| 2026-10-20 | php.net announces a critical security release for the next day |
| 2026-10-21 | php.net security announcement: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, both GHSA entries, backported patches for 7.4, 8.0, 8.1 |
| 2026-10-22 | date on the [ChangeLog](https://www.php.net/ChangeLog-8.php#8.5.12) entries for those releases |
| 2026-10-23 | php.net [informational advisory](https://www.php.net/archive/2026.php#2026-10-23-1) on CVE-2026-115560, a non-default session serializer case; mitigation `session.upload_progress.enabled=0`; full advisory with the next release |
| 2026-10-23 | [BleepingComputer](https://www.bleepingcomputer.com/news/security/php-shares-mitigation-for-new-and-unpatched-rce-vulnerability/) reports exploitation attempts |
| 2026-12-31 | PHP 8.2 end of life |

My take: with long-lived workers, a flaw in request startup or cleanup is a flaw in the isolation every PHP app relies on, and no php.ini line restores that. Update, restart every worker, and read the version from the web SAPI. The same version-then-running-process habit applies to our [APT signature check](/apt-package-signature-flaw-check-and-fix/) and the [Node.js Brotli check](/nodejs-brotli-rce-check-and-fix/).

## Frequently asked questions

**php -v shows the fixed version. Am I done?**
Not yet. The CLI and PHP-FPM or mod_php can be different packages with different php.ini files. Check the version reported by the running web SAPI, and confirm every worker started after the upgrade.

**My distro package says 8.3.6, not 8.3.36. Is it fixed?**
Distributions backport fixes without changing the upstream version number. Find the fixed package revision in your vendor's security advisory for your release, compare it with the installed revision of every PHP package the web service loads (dpkg-query -W or rpm -q), then restart and check the running service. A CVE line in a changelog alone does not prove the installed package has it.

**After updating, should I keep session.upload_progress.enabled=0?**
Keep it off if your application switches session.serialize_handler to php with ini_set before session_start. That is the non-default case php.net describes for CVE-2026-115560, with the fix due in the next release. Otherwise you can turn it back on.

## Sources

- [php.net, 2026-10-21: Security advisory, critical remote code execution vulnerabilities, update now](https://www.php.net/archive/2026.php#2026-10-21-5)
- [GHSA-w8hw-w9fx-j79w: CVE-2026-109618, out-of-memory handling during request startup](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w)
- [GHSA-6wmf-4rv5-p3rp: CVE-2026-109619, request state used after free by a later request](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)
- [PHP 8 ChangeLog: 8.5.12, 8.4.27, 8.3.36, 8.2.35](https://www.php.net/ChangeLog-8.php#8.5.12)
- [php.net, 2026-10-23: Informational advisory, session upload progress (CVE-2026-115560)](https://www.php.net/archive/2026.php#2026-10-23-1)
- [BleepingComputer, 2026-10-23: PHP shares mitigation for new and unpatched RCE vulnerability](https://www.bleepingcomputer.com/news/security/php-shares-mitigation-for-new-and-unpatched-rce-vulnerability/)
