{"id":798,"date":"2026-10-23T13:03:50","date_gmt":"2026-10-23T13:03:50","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/php-8-5-12-security-update-rce-patch-checklist-php-fpm-mod-php\/"},"modified":"2026-10-23T13:03:50","modified_gmt":"2026-10-23T13:03:50","slug":"php-8-5-12-security-update-rce-patch-checklist-php-fpm-mod-php","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/php-8-5-12-security-update-rce-patch-checklist-php-fpm-mod-php\/","title":{"rendered":"PHP 8.5.12 RCE Patch: The Checklist I Run on Every Server"},"content":{"rendered":"<p>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&rsquo;s own wording was &ldquo;update immediately&rdquo;. No &ldquo;consider upgrading when convenient&rdquo;. That phrasing is rare enough that I stopped what I was doing.<\/p>\n<p>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 <code>php -v<\/code>. Everything below is the checklist I&rsquo;m using, the stopgap settings if you can&rsquo;t patch yet, and a few places where I think people will get tripped up.<\/p>\n<p>I&rsquo;m not a security researcher. I&rsquo;m a developer who deploys PHP apps for clients and runs a few boxes of my own, so this is written from the &ldquo;I have to patch this today and prove it&rdquo; side of the fence. Read the primary sources too. I link them as I go.<\/p>\n<h2 id=\"what-the-advisory-actually-says\">What the advisory actually says<\/h2>\n<p>The <a href=\"https:\/\/www.php.net\/index.php#2026-10-21-5\" rel=\"nofollow noopener\" target=\"_blank\">official advisory on php.net<\/a> 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.<\/p>\n<p>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&rsquo;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.<\/p>\n<p>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&rsquo;re in scope. <a href=\"https:\/\/laravel-news.com\/php-critical-rce-security-releases\" rel=\"nofollow noopener\" target=\"_blank\">Laravel News covered the release<\/a> with the platform-by-platform status, which is the quickest way to see where Forge, Vapor, Cloud and Envoyer stand.<\/p>\n<p>I&rsquo;m deliberately not going to describe exploitation details. I don&rsquo;t know them beyond what the advisory says, and guessing would be worse than silence.<\/p>\n<h2 id=\"which-version-youre-on-decides-the-next-hour\">Which version you&rsquo;re on decides the next hour<\/h2>\n<p>Here&rsquo;s where I&rsquo;d sort yourself, because the work is different for each bucket.<\/p>\n<p>If you&rsquo;re on 8.2 through 8.5, the fix is a normal package or image update. The <a href=\"https:\/\/www.php.net\/ChangeLog-8.php\" rel=\"nofollow noopener\" target=\"_blank\">8.5.12 changelog<\/a> lists the fix for both advisories under Core as &ldquo;Avoid state corruption via bailout&rdquo;, 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&rsquo;t apply to you, this is not a release to skip.<\/p>\n<p>If you&rsquo;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&rsquo;t treat it as a plan. If you have a distribution or LTS vendor, ask them for their backport. If you don&rsquo;t, this is the week the &ldquo;we&rsquo;ll upgrade PHP next quarter&rdquo; ticket becomes this week&rsquo;s ticket. I wrote about how I plan those upgrade windows in <a href=\"https:\/\/abrarqasim.com\/blog\/laravel-13-release-date-support-window-i-plan-client-upgrades-around\" rel=\"noopener\">my Laravel 13 support window post<\/a>, and the short version is that waiting for a security release to force the issue is the expensive way to do it.<\/p>\n<p>If you use the volunteer-maintained Remi repo, read the notice carefully. Per Laravel News, it won&rsquo;t have updates today, and the PHP team recommends distribution packages or building from source in the meantime. That one catches people because &ldquo;apt upgrade says nothing to do&rdquo; feels like good news.<\/p>\n<h2 id=\"the-checklist-im-using\">The checklist I&rsquo;m using<\/h2>\n<p>Order matters here, so I&rsquo;m keeping it boring and sequential.<\/p>\n<p>First, find out what you actually run. Not what you think you run. On each server:<\/p>\n<pre><code class=\"language-bash\">php -v\nphp-fpm8.4 -v 2&gt;\/dev\/null\napachectl -M 2&gt;\/dev\/null | grep -i php\nps aux | grep -E 'php-fpm|php-cgi|frankenphp|lsphp' | grep -v grep\n<\/code><\/pre>\n<p>The CLI version and the FPM version can differ, and I&rsquo;ve been bitten by that before. A box can show a patched <code>php -v<\/code> 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.<\/p>\n<pre><code class=\"language-bash\">sudo apt update &amp;&amp; sudo apt install --only-upgrade php8.4-fpm php8.4-cli\nsudo systemctl restart php8.4-fpm\n# containers: pull the new base image, rebuild, redeploy\ndocker compose pull &amp;&amp; docker compose up -d --build\n<\/code><\/pre>\n<p>Third, verify against the running thing, not the package manager. For FPM, I check the master process&rsquo;s binary and start time. For containers, I check the image digest of what&rsquo;s actually up.<\/p>\n<pre><code class=\"language-bash\">php-fpm8.4 -v\nsystemctl show php8.4-fpm --property=ActiveEnterTimestamp\ndocker compose exec app php -v\n<\/code><\/pre>\n<p>Fourth, remember the platforms that don&rsquo;t patch you automatically. According to Laravel&rsquo;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&rsquo;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&rsquo;t get the update at all. For those you need a new server on a supported Ubuntu release. I&rsquo;d power the old box down until you&rsquo;ve moved.<\/p>\n<p>Fifth, patch Node too if the same box runs it. The Laravel News piece mentions Node.js security releases shipped alongside, and Forge doesn&rsquo;t update Node for you. I haven&rsquo;t read the Node advisory closely enough to say more, so go read it yourself.<\/p>\n<h2 id=\"if-you-cant-patch-today\">If you can&rsquo;t patch today<\/h2>\n<p>The advisory has a section for this, and I&rsquo;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.<\/p>\n<p>For the OOM bug, the advisory suggests setting <code>memory_limit=-1<\/code> in a place applications can&rsquo;t override it, and a low <code>post_max_size<\/code>. The counterintuitive part is the unlimited memory limit. I&rsquo;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 <code>RLIMIT_AS<\/code> or <code>RLIMIT_DATA<\/code> as a substitute, because those can themselves cause the vulnerable allocation failure.<\/p>\n<p>Here&rsquo;s what that looks like in an FPM pool, where <code>php_admin_value<\/code> can&rsquo;t be overridden by app code:<\/p>\n<pre><code class=\"language-ini\">; \/etc\/php\/8.4\/fpm\/pool.d\/www.conf\nphp_admin_value[memory_limit] = -1\nphp_admin_value[post_max_size] = 8M\nphp_admin_value[upload_max_filesize] = 6M\nphp_admin_value[session.upload_progress.enabled] = 0\n<\/code><\/pre>\n<p>The 8M figure is the advisory&rsquo;s suggestion for apps without file uploads, and 32M for apps that need uploads, with <code>upload_max_filesize<\/code> kept below <code>post_max_size<\/code>. Adjust to your app.<\/p>\n<p>For the stale global state bug, the advisory says to set <code>session.upload_progress.enabled=0<\/code> 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 <code>ini_set()<\/code> or <code>.user.ini<\/code>, because they apply too late. There&rsquo;s also a PHP 8.5 item about <code>fatal_error_backtraces=0<\/code>. Read the full text, since I&rsquo;m only summarizing part of it.<\/p>\n<p>One thing I&rsquo;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&rsquo;s a systemd drop-in with <code>MemoryMax<\/code> on the FPM unit, which I cover in my <a href=\"https:\/\/abrarqasim.com\/blog\/linux-server-hardening-small-vps-baseline-i-run-first\" rel=\"noopener\">VPS hardening baseline<\/a>.<\/p>\n<h2 id=\"what-im-still-unsure-about\">What I&rsquo;m still unsure about<\/h2>\n<p>I don&rsquo;t know yet how many installs are being probed. I haven&rsquo;t seen exploit traffic on my own servers, and absence of evidence on a handful of boxes means nothing. I also can&rsquo;t tell you whether the interim settings hold up, because the advisory itself hedges on them.<\/p>\n<p>The other open question is logs. If an attacker got in before you patched, a clean <code>php -v<\/code> tells you nothing about the past. I&rsquo;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&rsquo;d rather write that email now than later.<\/p>\n<h2 id=\"do-these-four-things-before-you-close-the-laptop\">Do these four things before you close the laptop<\/h2>\n<p>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&rsquo;t be patched tonight, apply the advisory&rsquo;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&rsquo;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 <a href=\"https:\/\/abrarqasim.com\" rel=\"noopener\">abrarqasim.com<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix two unauthenticated RCE bugs. My patch checklist, how to verify workers, and the interim settings if you can&#8217;t update today.<\/p>\n","protected":false},"author":2,"featured_media":797,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix two unauthenticated RCE bugs. My patch checklist, how to verify workers, and the interim settings if you can't update today.","rank_math_focus_keyword":"php security update","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[869],"tags":[80,56,53,874,154],"class_list":["post-798","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-php-laravel","tag-devops","tag-laravel","tag-php","tag-php-fpm","tag-security"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/798","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=798"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/798\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media\/797"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=798"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=798"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=798"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}