I was halfway through a coffee yesterday evening when the PHP security advisory landed in my feed, and the coffee did not survive the next ten minutes. Two critical remote code execution bugs, unauthenticated, and the PHP team’s own wording was “update immediately”. No “consider upgrading when convenient”. That phrasing is rare enough that I stopped what I was doing.
Short version for the impatient: update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, restart your workers, and confirm with php -v. Everything below is the checklist I’m using, the stopgap settings if you can’t patch yet, and a few places where I think people will get tripped up.
I’m not a security researcher. I’m a developer who deploys PHP apps for clients and runs a few boxes of my own, so this is written from the “I have to patch this today and prove it” side of the fence. Read the primary sources too. I link them as I go.
What the advisory actually says
The official advisory on php.net names two bugs. The first is CVE-2026-109618, which the team describes as RCE via out-of-memory corruption. When PHP runs out of memory at a bad moment, it can be interrupted while internal request data is only half initialized, and the cleanup code then trusts that invalid data. The second is CVE-2026-109619, RCE via stale global state. Some request-owned internal state survives after its memory has been freed, and the next request on the same worker can pick it up before normal initialization resets it.
Both are unauthenticated. The team says the risk is highest for mod_php, where they have a demonstrated exploit that fires before any application code runs. That last part matters for how you think about this. Your framework’s middleware, your WAF rules written against your routes, your login page: none of it is in the path. The bug lives below your code.
The affected list is wide. PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed are all named. If your PHP serves web requests, you’re in scope. Laravel News covered the release with the platform-by-platform status, which is the quickest way to see where Forge, Vapor, Cloud and Envoyer stand.
I’m deliberately not going to describe exploitation details. I don’t know them beyond what the advisory says, and guessing would be worse than silence.
Which version you’re on decides the next hour
Here’s where I’d sort yourself, because the work is different for each bucket.
If you’re on 8.2 through 8.5, the fix is a normal package or image update. The 8.5.12 changelog lists the fix for both advisories under Core as “Avoid state corruption via bailout”, alongside a long pile of other fixes, including use-after-free bugs in DOM and BZ2 and a file descriptor leak in the built-in server. So even if the RCE bugs somehow didn’t apply to you, this is not a release to skip.
If you’re on 8.1 or older, the PHP project no longer ships updates for you. The security team published minimal source backports for 7.4, 8.0 and 8.1 for people building from source, which is generous, but I wouldn’t treat it as a plan. If you have a distribution or LTS vendor, ask them for their backport. If you don’t, this is the week the “we’ll upgrade PHP next quarter” ticket becomes this week’s ticket. I wrote about how I plan those upgrade windows in my Laravel 13 support window post, and the short version is that waiting for a security release to force the issue is the expensive way to do it.
If you use the volunteer-maintained Remi repo, read the notice carefully. Per Laravel News, it won’t have updates today, and the PHP team recommends distribution packages or building from source in the meantime. That one catches people because “apt upgrade says nothing to do” feels like good news.
The checklist I’m using
Order matters here, so I’m keeping it boring and sequential.
First, find out what you actually run. Not what you think you run. On each server:
php -v
php-fpm8.4 -v 2>/dev/null
apachectl -M 2>/dev/null | grep -i php
ps aux | grep -E 'php-fpm|php-cgi|frankenphp|lsphp' | grep -v grep
The CLI version and the FPM version can differ, and I’ve been bitten by that before. A box can show a patched php -v while the FPM pool still runs the old binary because nobody restarted it. Which brings me to the second step: patching the package does not patch the running process. Workers that were started before the update keep running the old code until they restart.
sudo apt update && sudo apt install --only-upgrade php8.4-fpm php8.4-cli
sudo systemctl restart php8.4-fpm
# containers: pull the new base image, rebuild, redeploy
docker compose pull && docker compose up -d --build
Third, verify against the running thing, not the package manager. For FPM, I check the master process’s binary and start time. For containers, I check the image digest of what’s actually up.
php-fpm8.4 -v
systemctl show php8.4-fpm --property=ActiveEnterTimestamp
docker compose exec app php -v
Fourth, remember the platforms that don’t patch you automatically. According to Laravel’s status notes, Cloud was patched in place, but Forge servers need you to click Update PHP on each one, and Vapor needs a redeploy (a new deployment, not just a redeploy, if you’re on a custom or Docker image runtime). Laravel also says Forge has no patch for PHP 8.0 or 8.1 and that Ubuntu 20.04 or older servers can’t get the update at all. For those you need a new server on a supported Ubuntu release. I’d power the old box down until you’ve moved.
Fifth, patch Node too if the same box runs it. The Laravel News piece mentions Node.js security releases shipped alongside, and Forge doesn’t update Node for you. I haven’t read the Node advisory closely enough to say more, so go read it yourself.
If you can’t patch today
The advisory has a section for this, and I’d read it on php.net rather than from my summary, because the details matter. The PHP team is blunt that these measures reduce exposure to specific attack paths and do not make an installation safe. Treat them as a seatbelt on the way to the update, not a substitute.
For the OOM bug, the advisory suggests setting memory_limit=-1 in a place applications can’t override it, and a low post_max_size. The counterintuitive part is the unlimited memory limit. I’d have bet money the safer move was a tighter limit. The reasoning is that a finite PHP memory limit is a controllable way to trigger the out-of-memory failure, so removing it takes away the lever. They also say an underlying allocation failure can still reach the vulnerable path, so this is partial at best. And they warn against using RLIMIT_AS or RLIMIT_DATA as a substitute, because those can themselves cause the vulnerable allocation failure.
Here’s what that looks like in an FPM pool, where php_admin_value can’t be overridden by app code:
; /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] = 6M
php_admin_value[session.upload_progress.enabled] = 0
The 8M figure is the advisory’s suggestion for apps without file uploads, and 32M for apps that need uploads, with upload_max_filesize kept below post_max_size. Adjust to your app.
For the stale global state bug, the advisory says to set session.upload_progress.enabled=0 and restart every worker, using php.ini, an FPM pool or server config that applies before request parsing. It specifically says not to rely on ini_set() or .user.ini, because they apply too late. There’s also a PHP 8.5 item about fatal_error_backtraces=0. Read the full text, since I’m only summarizing part of it.
One thing I’d add from my own habits, and this is opinion rather than advisory: if unlimited memory makes you nervous on a small VPS, pair it with a cgroup or container memory limit that kills only the worker. The advisory itself recommends a container or cgroup limit as an availability boundary. On my own boxes that’s a systemd drop-in with MemoryMax on the FPM unit, which I cover in my VPS hardening baseline.
What I’m still unsure about
I don’t know yet how many installs are being probed. I haven’t seen exploit traffic on my own servers, and absence of evidence on a handful of boxes means nothing. I also can’t tell you whether the interim settings hold up, because the advisory itself hedges on them.
The other open question is logs. If an attacker got in before you patched, a clean php -v tells you nothing about the past. I’m checking web server access logs for oddly large or oddly shaped requests since the 21st and looking for unexpected files and processes under the web user. If you run anything with client data on it, assume you may want a proper incident review rather than my grep one-liners. For client work I’d rather write that email now than later.
Do these four things before you close the laptop
Check the PHP version on every server and container you own, then restart FPM or Apache and recheck against the running process. Move anything on PHP 8.1 or older onto a supported release, or get a written backport commitment from your vendor. If a box can’t be patched tonight, apply the advisory’s interim settings and put a calendar block on the patch. And if you maintain client sites, send each client a two-line note saying what you patched and when, because they will hear about this from somewhere else and it’s better coming from you. If you want help with a patch sweep or the upgrade plan behind it, my work and contact details are at abrarqasim.com.