Patching PHP’s Two Critical RCEs

On 21 October the PHP project released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two remote code execution vulnerabilities, CVE-2026-109618 and CVE-2026-109619. Both are rated 9.2 on CVSS v4, and neither needs authentication. Both advisories say updating is the only complete fix.

Fixed Versions

Branch Fixed in
8.5 8.5.12
8.4 8.4.27
8.3 8.3.36
8.2 8.2.35
8.1 and older No upstream release. The PHP security team published minimal source backports for 7.4, 8.0 and 8.1; Debian and Ubuntu users can get fixed builds from packages.sury.org; otherwise check your LTS vendor.

Linux distributions received the patches in advance, so most systems get the fix as a normal package update.

The Two Bugs

CVE-2026-109618 is in out-of-memory handling. PHP aborts on an out-of-memory error with longjmp(). If that happens while it’s still parsing the request body, request data is left half-initialized, and request shutdown later processes it as valid. Crafted multipart requests can trigger this while staying inside PHP’s input limits. The fix removes longjmp() from out-of-memory handling.

CVE-2026-109619 is a use-after-free. A request that’s interrupted by a fatal error, a timeout or running out of memory can leave freed state behind on the worker, and a later request on the same worker can read it before initialization resets it. Confirmed paths include session upload progress, which is on by default and doesn’t need session_start(), and PHP 8.5’s fatal_error_backtraces. The impact includes memory disclosure as well as code execution.

Exposure by Server API

SAPI CVE-2026-109618 CVE-2026-109619
Apache mod_php Code execution validated with default limits, before application code runs Code execution confirmed
PHP-FPM Exploitable by source analysis, not fully demonstrated Code execution validated through session upload progress; depends on the application
php-cgi, FrankenPHP, LiteSpeed Listed as at risk by php.net

Checking What You Run

The CLI and the web server often run different PHP packages, so check the one serving requests:

php -v                      # CLI only
php-fpm -v                  # often versioned: php-fpm8.3 -v
apachectl -M | grep -i php  # is mod_php loaded?
apt policy php8.3-fpm       # Debian/Ubuntu: installed version and its repo

On Debian and Ubuntu, fixed builds reached packages.sury.org before the ppa:ondrej/php Launchpad PPA, so confirm the installed version if you use the PPA. Ondřej Surý is tracking the rollout, including the end-of-life branches, in this issue.

After updating, restart PHP-FPM or Apache so no worker is still running the old build. The full details, including interim settings for systems that can’t update immediately, are in the php.net advisory.

Reading a Spamhaus Listing Without Panicking

The first reaction to a Spamhaus listing is usually to find the delisting form. That is almost always the wrong first move, because the return code already tells you which of several unrelated problems you have, and three of them cannot be fixed by delisting at all.

The Code Is the Diagnosis

A DNSBL answers with an address in 127.0.0.0/8, and the last octet identifies which dataset matched. Querying the combined zone gives you one lookup and a code that says which list you are on:

$ dig +short 2.0.113.203.zen.spamhaus.org A
127.0.0.4

Note the reversed octets. The address 203.0.113.2 becomes 2.0.113.203 in the query. The codes map like this:

127.0.0.2    SBL       manually researched spam source
127.0.0.3    CSS       automatically detected low-reputation sender
127.0.0.4    XBL       compromised or exploited machine
127.0.0.9    DROP      resource controlled by bad actors
127.0.0.10   PBL       dynamic space, declared by the ISP
127.0.0.11   PBL       dynamic space, inferred by Spamhaus
127.0.0.20   AuthBL    credential bruteforcing or hijacking
127.0.0.30   BCL       malware command and control

An empty answer means not listed. Always fetch the TXT record too, since it carries the reference URL for that specific listing.

What Each One Means for You

PBL, 127.0.0.10 or 127.0.0.11, is not a reputation problem and it is the most common cause of confusion. It says the address is in a range that should not be running a mail server, either because the ISP declared it dynamic or because Spamhaus inferred it. You have done nothing wrong. If you are sending from a residential or default cloud IP, the fix is to send through a proper relay or get static space with a matching declaration from the provider. Delisting an address whose range is still marked dynamic accomplishes nothing durable.

XBL, 127.0.0.4, means the address is emitting traffic characteristic of a compromised machine. Treat it as an incident. Something is infected, or something is being proxied through, or you have an open relay or a web form somebody is abusing. Delist after you find it and the listing returns within hours, which at least confirms the diagnosis.

CSS, 127.0.0.3, is automated and driven by sending behavior: poor list hygiene, high bounce rates, spam-trap hits, sudden volume changes from a cold address. This is the one that most often reflects marketing practice rather than a technical fault, and the technical team frequently cannot fix it alone.

SBL, 127.0.0.2, is manually researched by a human at Spamhaus. Something specific and documented happened, the TXT record will point to the evidence, and there is no automated path out. Read the reference before contacting anyone.

DROP and BCL are severe. DROP marks space believed to be under criminal control, and BCL marks malware command-and-control infrastructure. If either appears against space you own, the conversation is with your provider and probably with an incident responder.

Delisting Without Fixing the Cause

Every automated list re-lists. That is the point of automation. Requesting removal while the underlying condition persists buys hours, and it also trains you to treat the listing as the problem instead of the symptom.

The order that works is to identify the code, find and fix the cause, verify the cause is actually gone, and then request removal. For CSS in particular, verification means watching your own outbound metrics for long enough to know the behavior changed, since a quiet weekend proves nothing.

Check the Basics While You Are There

Listings frequently arrive alongside configuration problems that make delivery worse independently. Reverse DNS that does not exist or does not match the HELO name, SPF that does not cover the sending host, DMARC set to a policy nobody tested. None of those cause a Spamhaus listing directly, and all of them compound the damage while you are listed.

$ dig +short -x 203.0.113.2
mail.example.com.
$ dig +short mail.example.com A
203.0.113.2

Forward and reverse should agree. The email health check on mrdns.com runs that alongside SPF, DKIM, and DMARC in one pass, and the blacklist check covers the wider set of lists in case this is not the only one you are on.

One operational note if you are querying at any volume: the free DNS interface is intended for individual mail servers checking their own inbound traffic. Anything programmatic or repeated belongs on Spamhaus’s data query service, and running bulk lookups against the public zone will get you blocked, which then looks exactly like a clean result.

systemd Hardening for Long-Running Daemons

A long-running daemon that talks to the network and a database is the thing an attacker wants most, and it is usually the thing running with the fewest restrictions. systemd ships a large set of sandboxing directives that cost nothing at runtime and are almost never used, mostly because the documentation presents forty options with no indication of which ones matter.

Measure First

systemd will grade the unit for you, which is a better starting point than reading the manual page end to end:

systemd-analyze security tracker.service

You get a per-directive table and an overall exposure score from 0 to 10. A default unit with no hardening scores around 9.6, which the tool cheerfully labels UNSAFE. The score is a rough heuristic rather than a real measure of risk, and it is genuinely useful for ordering the work, since it sorts by exposure and shows what each setting would buy.

The Directives Worth Setting

These are the ones I put on every service, in rough order of value per unit of trouble:

[Service]
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
LockPersonality=yes
RemoveIPC=yes
MemoryDenyWriteExecute=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
CapabilityBoundingSet=
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

NoNewPrivileges=yes is the single highest-value line. It blocks setuid escalation for the process and everything it spawns, and almost nothing legitimately needs the alternative.

ProtectSystem=strict mounts the entire filesystem read-only apart from /dev, /proc, and /sys. Combined with ProtectHome=yes, a compromised process cannot write anywhere at all until you grant specific paths back:

ReadWritePaths=/var/lib/tracker /var/log/tracker
RuntimeDirectory=tracker
StateDirectory=tracker

The RuntimeDirectory and StateDirectory forms are better than listing paths by hand, because systemd creates them with the right ownership and cleans up runtime state on stop.

CapabilityBoundingSet= with an empty value drops every capability. Most daemons need none, and the common exception is binding a port below 1024, which needs CAP_NET_BIND_SERVICE added back. Better still, use a socket unit and let systemd hold the privileged socket so the service never needs the capability at all.

RestrictAddressFamilies is worth the thought. A daemon doing DNS and PostgreSQL over TCP needs AF_INET, AF_INET6, and AF_UNIX for the local socket. It has no business creating a raw packet socket or an AF_NETLINK socket, and denying those closes a real avenue.

The Ones That Will Break You

MemoryDenyWriteExecute=yes blocks pages that are both writable and executable. It is excellent against a large class of exploit, and it breaks anything with a JIT. For a C++ daemon it is usually free. For anything embedding a scripting runtime, test carefully.

SystemCallFilter=@system-service is the setting most likely to produce a mysterious failure. The allow list covers what normal services do, and a program using something unusual gets killed with SIGSYS and no useful message. When a service dies immediately after adding it, find the offending call rather than guessing:

journalctl -u tracker.service | grep -i seccomp
auditctl -a always,exit -F arch=b64 -S all -F key=seccomp-debug

ProtectSystem=strict breaks anything writing outside its declared paths, which in practice means log files in unexpected places and PID files in /var/run. Both are better fixed than exempted.

ProtectProc=invisible hides other users’ processes in /proc. Harmless for most daemons and immediately fatal for anything that inspects the process table, including some monitoring agents.

Do It Incrementally

Adding all twenty lines at once and finding the service dead tells you nothing about which line did it. Add the block above minus SystemCallFilter and MemoryDenyWriteExecute, confirm the service still works under real load rather than just starting, then add those two separately.

“Starts successfully” is a weak test. Sandboxing failures show up on code paths that only run occasionally, so the syscall your daemon makes once a day during log rotation is exactly the one the filter blocks. Give a hardened unit a full cycle of whatever periodic work it does before considering it verified.

None of this replaces running as an unprivileged user, keeping the software patched, or putting the database somewhere the internet cannot reach. It is the layer underneath those, and it is roughly twenty lines of configuration that turns a total compromise of the process into a considerably less useful foothold.

Why Your DNS Change Looks Different From Eight Places at Once

“Waiting for DNS to propagate” is the most common description of something that does not happen. There is no propagation in DNS. Nothing is pushed anywhere. Understanding what is actually going on turns a mysterious waiting period into an arithmetic problem you can predict and control.

Nothing Travels

When you change a record, exactly one thing happens: your authoritative nameservers start returning a new answer. That change is complete the moment the zone reloads, which is usually seconds. Any resolver that asks after that gets the new value immediately.

What varies is when each resolver next bothers to ask, and that is governed entirely by the TTL of the answer it cached before you made the change. A resolver holding a record with a 3600 second TTL will keep serving the old value for up to an hour, and no action on your end reaches inside its cache. You are not waiting for something to spread. You are waiting for copies to expire.

This is why checking from eight locations gives eight different answers during the window, and why the pattern looks random. Each of those resolvers cached at a different moment, so each expires at a different moment. The propagation checker on mrdns.com is really a cache-expiry checker, and reading it that way makes the staggered results obvious.

The TTL That Matters Is the Old One

Here is the part that trips people up. Lowering a record’s TTL right before you change it does nothing useful, because resolvers are still holding the copy they fetched under the previous, longer TTL.

; Monday: the value resolvers have cached
www.example.com.    86400    IN    A    203.0.113.10

; Friday: lowered TTL and changed the address at the same time
www.example.com.      300    IN    A    203.0.113.55

A resolver that cached on Monday holds the old address for a full day regardless of what Friday’s record says, because it will not re-ask until its copy expires. The 300 second TTL only starts applying to whatever it fetches next.

The sequence that works is to lower the TTL first, wait at least the old TTL so every cached copy expires and gets replaced by a short-lived one, and only then change the value. Now the maximum staleness is 300 seconds. Raise the TTL again once the change has settled.

For a planned migration that means starting a day or two ahead. Doing it the other way around and then waiting is the origin of most “DNS is still propagating” complaints.

Negative Answers Cache Too

If someone queries a name before it exists, the NXDOMAIN gets cached. The lifetime of that negative answer comes from the zone’s SOA record, specifically the minimum field, per RFC 2308:

example.com.  IN  SOA  ns1.example.com. hostmaster.example.com. (
                  2026081101   ; serial
                  7200         ; refresh
                  3600         ; retry
                  1209600      ; expire
                  3600 )       ; minimum, caps negative caching

A large minimum means that a name someone checked prematurely stays broken for them long after you create it. An hour is reasonable. The multi-day values that appear in copied-and-pasted zone files are not, and they produce the particularly confusing case where a brand new hostname works for most of the world and not for the one person who tried it early.

The Other Places Answers Hide

Delegation TTLs are longer than record TTLs and get overlooked. If you change nameservers, the parent zone’s NS records govern how long resolvers keep asking your old provider, and those are often set to a day or two by the registry. The records at your new provider can be perfect while resolvers continue to consult the old one.

CNAME chains expire independently. Each link has its own TTL, so the effective staleness is set by whichever link is longest, and updating the target does nothing for a resolver still holding the alias.

Then there is everything below the resolver: the stub resolver on the client, the browser’s own cache, and any corporate forwarder in between. Chrome caches independently of the operating system, which is why a page can stay stubbornly wrong on one machine after dig on the same machine returns the new value.

How to Check Properly

Query the authoritative server directly to confirm the change is live, then query a public resolver to see what the cached world still believes:

dig @ns1.example.com www.example.com A +norecurse
dig @8.8.8.8 www.example.com A

The TTL in the second answer counts down toward zero, and it tells you precisely how much longer that resolver will serve the old value. That number is the actual answer to “how long until this is done”, and it is available immediately rather than after waiting to find out. If you want the same view without a terminal, the lookup tool shows the returned TTL alongside the record.

Where AI Compute Goes When the Router Does the Work

My last post ended on a small, quiet machine on the shelf beside the Wi-Fi router. Six months earlier, the IEEE 802.11 working group, which writes the Wi-Fi standard, had voted to start letting the router be that machine, or at least find it for you.

What the Standard Actually Covers

Qualcomm brought the proposal, now called AI Offload, in March, co-signed by Cisco, Meta, MediaTek, Huawei, Sony, Nokia and others. A device that can’t run an AI model (the proposal’s example is “a non-AI Wi-Fi home security camera”) sends the job to a router that can and gets the answer back. It can even tell the router where to download the model, so a camera maker can ship its own AI without an AI chip in the camera.

Routers would announce the service the way they announce their network name, so a device can see what AI is on offer (text, voice, images, video) before it connects.

By July the scope had grown. The draft charter covers AI work done by the router, by another device on your Wi-Fi, or by a device plugged into your network behind the router: the box on the shelf. MediaTek has proposed how that would work: AI boxes check in with the router, and the router passes your request to whichever one can take it.

The standard covers finding the service, connecting to it, and securing the connection. The AI work itself is out of scope; Cisco’s review of the early proposals calls the model “a black box” as far as Wi-Fi is concerned. That’s the right call. A Wi-Fi standard that tried to pin down AI models would be obsolete before it shipped.

When What happened
January 2026 Broadcom sends router makers early samples of the BCM4918, a Wi-Fi 8 router chip with built-in AI hardware
March 2026 Qualcomm announces the Dragonwing NPro A8 Elite, a router chip with its own AI processor; the Wi-Fi working group votes to start on AI Offload
May 2026 The AI Offload group meets for the first time, in Antwerp
September 2026 The working group approves the charter; final sign-off expected in November
July 2029 Target for the first formal vote on a draft standard
July 2030 Target for the finished standard

Why Your Camera Shouldn’t Need Its Own Brain

Today a smart home device gets its AI from a chip inside it or from its maker’s cloud.

Eufy’s $299.99 E40 lock does the first: it stores up to 50 faces, and “all facial data is stored locally.” That works, but every smart device then needs its own AI chip, built from scratch by every company.

Ring does the second. Its Familiar Faces feature tags people at your door, and Amazon says it “processes and stores biometrics collected by Ring cameras on its own servers.” It isn’t offered in Illinois, Texas, or Portland, Oregon, all of which have strict biometric privacy laws.

The cloud can also disappear. When Belkin shut down Wemo’s cloud on January 31, 2026, the app, remote access, and the Alexa and Google integrations went with it. Only the Wemo devices already set up in Apple’s HomeKit kept working.

The router is a third option. A $30 camera with no AI hardware sends its frames to the router, the router runs the model, and the faces stay in the house. If the camera company goes under, the model’s still on your router. The models are small, too: Frigate, a free, open-source recorder for home security cameras, added face and license plate recognition last year, and its small face model “runs locally on the CPU” of an ordinary computer.

The obvious example is the front door: the doorbell camera sees you coming, the router matches your face, and the lock opens before you find your keys. I wouldn’t do it on the face alone. A camera can’t always tell a face from a good photo of one, and a cheap camera won’t have the 3D sensor your phone uses for face unlock. But the router knows your phone just joined the network. Face plus phone beats either one alone (e.g. MFA for your front door).

Two-factor front door: the doorbell camera sends a face and your phone joins the Wi-Fi; the router checks that the face matches someone who lives here and that their phone is on the network, and only then unlocks the door. A photo held up to the camera passes the face check but fails the phone check, so the door stays locked.

Once a cheap device can borrow the router’s model, the list of things worth building gets long. A few:

Devices What the router’s model does
Driveway camera, garage door or gate Reads the plate and opens for the family car. Nobody needs a giant cloud AI to read a license plate.
Porch camera, phone “The FedEx driver left a box” instead of “motion detected,” and another alert if someone else walks off with it.
Cheap smart speaker The speaker only listens for its wake word, like “Alexa”; the router does the rest.
Smoke and CO alarms, lights, heating and cooling Any microphone in the house hears the alarm; the router turns on the lights and shuts off the furnace fan so it stops pushing smoke through the ducts.
Wi-Fi motion sensing, family alerts Spots a fall in a parent’s house from the way a body disturbs the Wi-Fi signal, with no camera in the bathroom. Wi-Fi sensing got its own standard, 802.11bf, in 2025.

Most of these exist today, each locked to one vendor’s app and cloud. The standard would let a camera from one company use a router from another, the way any Wi-Fi phone works with any Wi-Fi router.

Your Laptop Uses the Same Box

Cameras make the easy case, but most of my AI questions come from my laptop. Lenovo’s scope proposal lists laptops as offload clients too, to save battery and run models too big for them. The announcement that lets a camera find the router lets a laptop find the shelf box, so your AI apps can use it by default and go to the cloud only when the policy allows. The software is mostly there: Ollama, a free tool for running models at home, accepts the same requests as OpenAI’s service, so many apps can switch to it by changing one address.

The bigger change is what that box can see. MCP, an open standard for AI apps, lets you plug your calendar or files into a model so it can answer questions about them. At home, you plug in the house: every camera and sensor, what they’ve recorded, and what they can do. Home Assistant, the free home automation platform, already offers its devices to Claude and ChatGPT this way, and the charter’s supporting document describes a router that keeps a directory of the AI agents on the network.

Then your laptop can ask about the house, using data that never left it:

  • “Who came to the door while I was out?”
  • “Was the garage door open overnight this week?”
  • “Are we using more water this month than last?”

A cloud model could answer those too, if you sent it your doorbell footage and your water readings.

Sending Work to the Big Cloud Models Isn’t in the Standard

Some work still wants a frontier model, one of the big cloud AIs like ChatGPT or Claude: summarizing a contract, or anything that needs real reasoning. The standard doesn’t cover sending it there, but it did come up.

Lenovo’s proposal listed handing work to a cloud provider as one possible design, and Huawei argued that the biggest models “have to be deployed on the cloud.” The draft charter went the other way. It only counts a device “capable of fulfilling an AI offload request without further assistance from another device,” which, as I read it, rules out a router that just forwards your request to a cloud service. Cisco’s review said where the AI runs is something ordinary networking already handles.

That’s right for a Wi-Fi standard, and it doesn’t get in the way. The box behind the router can answer what it can and send the rest to the cloud. That’s the idea from my first post, sending each request to the cheapest model that can handle it, moved inside the house.

Home Assistant already works this way by default: its built-in assistant handles commands first, and “only questions or commands it can’t understand will be sent to the AI you’ve set up.” Apple Intelligence runs many models on the device, sends harder requests to “larger, server-based models” on Apple’s servers, and for ChatGPT, “users are asked before any questions are sent.”

Tier Where it runs Typical work Leaves the house?
Device The camera, speaker, or glasses Wake word, motion trigger No
Router AI chip in the router Face match, plate read, voice commands No
Shelf box Plugged into your home network Your laptop’s questions, the family’s mail and photos No
Cloud Someone else’s data center Hard reasoning, long research Yes, when the house allows it

The last column is why I want this. Today each device maker decides what goes to its cloud. If requests leave through the router, the house decides, in one policy. Nothing defines that policy yet; here’s roughly what I’d want mine to say:

One policy for the whole house: faces and license plates go to the router and never leave the house; mail, calendar and photos go to the shelf box and stay home; everything else goes to the big AI models in the cloud, but only after asking first.

The Four Years Before 2030

The chips are in router makers’ hands now. The standard lands in 2030 if the schedule holds, and Wi-Fi schedules tend to slip. Until then, every router maker will ship its own version, every camera maker will pair with whoever pays them, and you’ll get a doorbell that only talks to one brand of router. Smart home hubs have worked that way for a decade.

Memory is still the hard part. An AI chip that reads license plates is cheap; a router with enough memory to run a large AI model costs what I complained about last time. That’s why the clause I care most about is the box plugged into your home network. The router only has to know where the big model is, and the box can sit on the shelf and get cheaper on its own schedule.

If the standard ships close to today’s charter, every device in the house will find the box from my last post the same way it finds the Wi-Fi.