Summary
A newly disclosed Linux kernel flaw tracked as CVE-2026-31431 — dubbed Copy Fail — lets any unprivileged local user gain root on nearly every mainstream Linux distribution released since 2017. Found by Xint Code Research together with Theori researcher Taeyang Lee, the bug is exploitable with a single 732-byte Python script that needs no compiled payload, no race condition, and no per-distribution tuning. Because the corruption happens entirely in the shared kernel page cache, it also reaches across container boundaries, turning it into a Kubernetes node-compromise primitive. CISA has already added the issue to its Known Exploited Vulnerabilities catalog on evidence of active exploitation.
Why Copy Fail is different from earlier Linux LPEs
Most headline Linux privilege-escalation bugs demand careful technique. Dirty Cow (CVE-2016-5195) hinged on winning a race in the virtual-memory copy-on-write path, often taking several tries and occasionally crashing the machine. Dirty Pipe (CVE-2022-0847) was tied to specific kernel versions and required deliberate pipe-buffer setup.
Copy Fail is none of that. It is a plain, straight-line logic error — no timing windows, no retries, no recompilation. The malicious write is deterministic, so the script runs once and works. The researchers summarize its properties as:
- Portable — no per-distribution offsets, version gates, or rebuilds; the same script runs across every tested distribution and architecture.
- Tiny — 732 bytes of Python using only the standard library (
os,socket,zlib). The sole requirement is Python 3.10+, needed foros.splice. - Stealthy — the kernel never flags the tampered page as dirty, so no writeback occurs and the on-disk file is untouched. Integrity scanners that checksum files on disk see nothing.
- Cross-container — because the page cache is shared by every process on the host, the corruption escapes container isolation and works as a Kubernetes escape primitive.
Root cause: page-cache pages inside a writable scatterlist
AF_ALG is a kernel socket family that hands the cryptographic subsystem to unprivileged userspace. Any user can open an AF_ALG socket, bind it to an AEAD (Authenticated Encryption with Associated Data) template, and run encryption or decryption over arbitrary data without privileges.
The second ingredient is splice(), which moves data between file descriptors and pipes without copying, handing page-cache pages along by reference. When an attacker splices a target file into a pipe and on into an AF_ALG socket, the socket's input scatterlist holds direct references to the kernel's cached pages for that file. These are not duplicates — the scatterlist entries point at the very physical pages that back every read(), mmap(), and execve() against that file across the whole system.
For AEAD decryption, algif_aead.c performs the operation in-place: a single scatterlist acts as both input and output. The AAD and ciphertext are byte-copied from the input into the output buffer through memcpy_sglist — a benign copy that only reads the page-cache pages. The authentication tag, however — the trailing authsize bytes of the input — is not copied. Instead the kernel chains those scatterlist entries straight onto the tail of the output scatterlist with sg_chain():
The output scatterlist ends up with two regions: the user's recvmsg buffer holding copied AAD and ciphertext, followed by the chained tag pages that still reference the original page cache pages of the target file. Both
req->srcandreq->dstpoint at the head of this combined chain.
This in-place design seats live page-cache pages inside a writable scatterlist. Nothing in the API guarantees that every AEAD algorithm will keep its writes inside the intended destination, and nothing documents that the caller must. One algorithm shatters that unspoken invariant.
The trigger: authencesn's scratch write
The component that breaks the rule is the authencesn AEAD template. While decrypting, authencesn writes intermediate cryptographic state — a scratch value — into the scatterlist at a computed offset. Since the output scatterlist now contains the chained page-cache pages, that scratch write lands directly in the kernel's in-memory copy of the target file.
The net effect is a controlled 4-byte write into the page cache of any file the attacker can read. Setuid binaries are world-readable, so the attacker picks one, patches 4 bytes in its in-memory image — enough to neutralize a permission check or redirect control flow — and runs it. The binary executes as root, while the file on disk stays byte-for-byte identical to the original.
From a 4-byte write to root
Turning a 4-byte arbitrary page-cache write into privilege escalation is direct when the target is a setuid binary. The attacker's sequence is:
- Choose a setuid binary and find the exact file offset of a branch or permission check to overwrite.
- Splice that file into a pipe to populate the
AF_ALGinput scatterlist with the target's page-cache pages. - Issue an AEAD decryption request shaped so that
authencesn's scratch write lands at the chosen 4-byte offset inside the target page. - Execute the now-corrupted setuid binary. The page cache serves the modified in-memory version, which runs with root privileges.
Because the page cache is global and shared across namespace boundaries, the same write is instantly visible to every process on the host, including those inside containers. The researchers say Part 2 of their series will detail the full Kubernetes node-compromise path this primitive opens.
Why traditional integrity tooling misses it
Copy Fail's stealth warrants a closer look for defenders. File-integrity monitors such as AIDE, Tripwire, and their peers hash on-disk files and compare them to a trusted baseline. Since the kernel never marks the corrupted page dirty, no writeback happens and the disk image is unchanged — so a filesystem hash comparison always returns clean.
Detecting this requires inspecting the page cache itself, comparing in-memory file content against trusted on-disk copies, or spotting the anomalous AF_ALG + splice() pattern at the syscall level. eBPF-based runtime tools that trace splice() calls into AF_ALG sockets are a far better detection surface for this bug class than conventional host IDS.
Affected systems and remediation
CVE-2026-31431 lives in the kernel's authencesn template and is exploitable on kernels shipped since 2017. The researchers confirmed root-level exploitation on:
- Ubuntu (multiple LTS releases)
- Amazon Linux
- Red Hat Enterprise Linux (RHEL)
- SUSE Linux Enterprise
Per the NVD entry, the upstream fix reverses the problematic in-place behavior: "crypto: algif_aead - Revert to operating out-of-place. This mostly reverts commit 72548b093ee3 except for the copying of the associated data." In other words, the patch stops page-cache pages from ever appearing in the writable output scatterlist in algif_aead.c. Distribution-specific advisories are being coordinated through the standard Linux security disclosure process.
Where immediate patching is not possible, exposure can be reduced — but not eliminated — by blocking unprivileged AF_ALG socket access via a seccomp policy, or by restricting splice() into AF_ALG sockets at the LSM layer. Neither is a complete fix.
Theori has published reference material and the disclosure write-up at theori-io/copy-fail-CVE-2026-31431.
How it was found: AI-assisted research
Copy Fail's discovery reflects an emerging workflow. The original insight came from Theori's Taeyang Lee, who was manually studying how the Linux crypto subsystem touches page-cache-backed data. Rather than hand-auditing every AEAD template against that hypothesis, Lee used the Xint Code AI security research platform to scale the analysis across the whole crypto subsystem, and Copy Fail surfaced as the most critical result. This pairing — a human picking a promising attack surface and AI tooling exhaustively enumerating instances — is becoming common in high-quality vulnerability research, particularly for large, complex kernel subsystems that resist manual review at scale.
Bottom line
CVE-2026-31431 is a clean example of a design-level assumption hardening into an exploitable invariant violation. The authencesn scratch write has presumably sat in the kernel for years; it turned critical only once researchers realized that AF_ALG + splice() can fill a writable scatterlist with the page-cache pages backing world-readable setuid binaries. Its mix of determinism, portability, stealth, and cross-container reach makes it one of the more significant Linux local privilege-escalation bugs in recent memory. Organizations running Linux workloads — especially multi-tenant Kubernetes clusters — should treat it as a top-priority patch and confirm their runtime detection can spot page-cache-level tampering, not just on-disk changes.