Linux Kernel Copy Fail (CVE-2026-31431) Rewrites the Page Cache to Get Root
Contents
TL;DR
Affected Linux kernels carrying the 2017 algif_aead in-place optimization. CVSS 7.8 HIGH
Fix Update to your distribution’s patched kernel (upstream fix is a664bf3d603d, which reverts algif_aead to out-of-place operation)
Mitigation On shared servers, CI runners, Kubernetes nodes, and sandbox infrastructure, disable algif_aead and restrict AF_ALG socket creation with seccomp
On April 29, 2026, Theori’s Xint Code team disclosed a Linux kernel local privilege escalation, Copy Fail.
The CVE is CVE-2026-31431.
A combination of authencesn, AF_ALG, and splice() lets an unprivileged user write 4 controllable bytes into the page cache of any readable file.
The nasty part is that it does not modify the file on disk.
The page cache is where Linux holds file contents in RAM, and read(), mmap(), and execve() all read from it.
Dirty just the cached pages of a setuid-root binary like /usr/bin/su, and the on-disk hash stays correct while the runtime sees the modified content.
In an earlier post about Claude Code finding an NFS bug in the Linux kernel, I covered an AI-assisted discovery of a 23-year-old remote vulnerability.
This is also AI-assisted, but the nature is quite different.
It is not a remotely reachable bug; it is a bug for getting from a local user or a container process to host root.
What was broken
AF_ALG is the interface that exposes the Linux kernel crypto API to userspace as a socket.
On the path here, the user opens an AEAD (Authenticated Encryption with Associated Data, a mode that combines encryption with integrity) cipher named authencesn(hmac(sha256),cbc(aes)).
splice() enters the picture next.
splice() is a system call that moves data between a file and a pipe (and similar) without copying, passing file contents as page cache references.
According to Xint’s writeup, when you splice() from a file to an AF_ALG socket, the input scatterlist for the crypto op points at the actual page cache pages.
Originally, page cache pages on the input side should have been treated as read-only and left alone.
Then in 2017, an in-place optimization landed in algif_aead that started reusing the same scatterlist for input and output.
As a result, page-cache-backed pages also became visible as a “write target.”
graph TD
A["splice() from a readable file"] --> B["Hand page cache pages<br/>to AF_ALG input"]
B --> C["algif_aead in-place processing"]
C --> D["Input and output scatterlists are mixed"]
D --> E["authencesn writes 4 bytes<br/>past the boundary as scratch"]
E --> F["The file body is fine,<br/>but the page cache is dirty"]
The final trigger is authencesn.
This is an AEAD wrapper for IPsec ESN (Extended Sequence Number, a 64-bit extended sequence number), and it uses the caller’s output buffer as scratch during processing.
Per Xint’s explanation, authencesn writes 4 bytes at dst[assoclen + cryptlen].
Normally that is just a transient write to an output buffer, but on this in-place path the page cache pages were attached on the other side.
After the write, authentication fails and recvmsg() returns an error.
But the 4-byte write does not get rolled back.
The attacker controls the target file, the offset, and the 4 bytes that get written.
Why nothing shows up on disk
A striking property of Copy Fail is that it does not persistently tamper with files.
The on-disk /usr/bin/su is untouched, so package manager verification or file hash comparison shows nothing wrong.
On the other hand, what the kernel reads at execution time is the content in the page cache.
If a modification is sitting on a cached page, execve() will use that modified page.
Xint’s PoC leans on this property, layering small changes on the in-memory copy of a setuid-root binary to reach a root shell.
These modifications evaporate on reboot or under memory pressure.
Once the page cache is dropped and the file is re-read from disk, things are back to normal.
That does not mean it is safe because it does not persist.
On the contrary, it is more annoying because it is easy to miss in disk forensics during incident response.
Crossing the container boundary
The page cache is shared across the entire host.
Containers do not get a fully isolated copy in memory.
That means an unprivileged process inside a container that can hit this bug becomes a path to dirtying the page cache of the host’s setuid binaries.
The blast radius is heaviest in environments that run user or tenant code on the same kernel.
| Environment | What happens |
|---|---|
| Shared dev servers, jump hosts | Regular users can climb to root |
| Kubernetes nodes | A compromised pod gets a path to host root |
| CI runners | A malicious pull request job can take over the runner |
| Notebooks, agent sandboxes | Tenant code reaches the underlying host |
In that sense, this carries a similar operational dread to the Docker Engine AuthZ bypass.
Don’t view it as a single-server bug; the priority shifts based on how much untrusted code you are loading onto the same host kernel.
The fix is reverting the in-place optimization
The upstream fix is the Linux commit a664bf3d603d.
The commit message reads crypto: algif_aead - Revert to operating out-of-place.
It almost reverts the in-place behavior introduced in 72548b093ee3 back in 2017, splitting the send-side and receive-side scatterlists again.
Before the fix, page-cache-backed pages handed in via splice() could end up linked on the output side too.
After the fix, the input goes to a TX scatterlist and the output to an RX scatterlist, so the path that lets authencesn’s scratch write leak into the page cache is gone.
NVD’s CVE page reads “Awaiting Enrichment” as of April 30, 2026, but the kernel.org CNA’s CVSS 3.1 is 7.8 HIGH with vector AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H.
I wrote in a separate article about NVD stepping back from full enrichment and shifting to priority triage; for kernel CVEs like this one, NVD-side detail is not lined up at disclosure either.
You have to look at vendor advisories, kernel.org, and researcher primary sources directly.
Stopgaps while you can’t update
The top priority is moving to your distribution’s patched kernel.
Theori suggests disabling the algif_aead module as a stopgap if you can’t apply patches right away.
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null || true
That alone may not be enough.
Some embedded configurations or distribution setups make the module impossible to unload.
Also, on hosts that run untrusted code, even after patching, blocking AF_ALG socket creation with seccomp is the cleaner approach.
OpenSSL, SSH, LUKS, kTLS, and IPsec/XFRM under typical configurations either use the kernel crypto API directly or use userspace libraries, so they often don’t use AF_ALG.
Theori also notes that disabling algif_aead has limited impact in typical environments.
For verification, check whether your kernel package contains the equivalent of the fix commit, or follow your distribution’s security advisories.
Don’t make safety calls based on uname -r alone.
Vendors sometimes backport fixes without bumping the upstream version number.
The “AI found it” angle
Copy Fail is making the rounds with the headline “AI discovered a Linux kernel zero-day.”
By Xint’s account, a human researcher provided the observation that AF_ALG + splice() exposes page cache references, and Xint Code scanned the Linux crypto/ subsystem in roughly an hour and surfaced this as the top finding.
This isn’t fully automated magic.
But the shape where humans narrow the attack surface and a model reads code paths broadly is well into a practical stage now.
In the post on the Linux kernel adopting an AI coding assistant policy, the focus was transparency on the contributor side; on the defender side, vulnerability discovery has taken another step forward.
Even from public information alone, Copy Fail isn’t the kind of bug to process in CVE-number order.
If you run other people’s code on a shared kernel, treat it with higher priority than a typical local privilege escalation.