A 732-byte Python script that escalates to root on most Linux kernels built since 2017. No race condition, no per-distro offsets, no tuning. It works on the first try on Ubuntu, Debian, RHEL, Rocky, Alpine, and everything in between. If you run Docker containers on an unpatched Linux host, this vulnerability — CVE-2026-31431, nicknamed “Copy Fail” — is also a container escape.
An attacker with non-root access inside any container on your host can break out to root on the host kernel. That container could be a compromised web app, a CI runner executing untrusted code, or a multi-tenant service you exposed to the internet.
This guide covers how Copy Fail works, how to check if you’re exposed, and most importantly — how to mitigate it in a Docker homelab with kernel patching, seccomp profiles, and verification.
Understanding CVE-2026-31431 — Why Copy Fail Is Dangerous
The Linux kernel exposes cryptographic primitives to userspace through
the AF_ALG socket family. The algif_aead module handles
authenticated-encryption-with-associated-data (AEAD) requests. Back in
2017, a performance optimization changed AEAD to work in-place — the
destination scatterlist pointed at the same pages as the source. Faster
and less memory, until it collides with the page cache.
When a process calls splice() to feed file data through the crypto
pipeline, the kernel uses the actual page-cache page of that file
rather than a copy. With the in-place optimization, that same page
becomes the destination. The kernel writes the AEAD authentication tag
directly into the page-cache copy of a file the user only has read
access to.
The exploitation path is deterministic:
- Open an
AF_ALGsocket withauthencesn(hmac(sha256),cbc(aes)) splice()a setuid binary’s pages (like/usr/bin/su) into the crypto context- Craft a
sendmsg()with specific associated-data bytes that produce the write - Execute the now-corrupted binary → root shell
CVSS 7.8 High. No race, no spray, no offset hunting. A 732-byte Python script works identically across every major distro on any CPU architecture.
For Docker users, the impact is worse: it’s a container escape.
Unless you block AF_ALG at the runtime level, any non-root process
inside a container can reach through to the host kernel and gain root.
Checking Your Exposure
Before applying mitigations, verify your current state.
Check Your Kernel Version
|
|
Vulnerable range: All kernels built from roughly 2017 (4.14+) up to the fix landing in April 2026.
Fixed mainline versions: 7.0, 6.19.12, 6.18.22.
Check if algif_aead Is Loaded
|
|
If this returns the algif_aead module, the vulnerable code path is
available on your host. If disabled or blacklisted, you’ll see no
output.
Check Docker Engine Version
Docker Engine 29.4.3 includes seccomp updates that reduce exposure. Check your version:
|
|
If you’re running 29.4.3 or later, the default seccomp profile still
does not block AF_ALG — you need the custom profile below.
Quick Container Escape Test
Run this inside any container to check if AF_ALG is accessible:
|
|
Expected output on a vulnerable host:
AF_ALG available - VULNERABLE
Immediate Mitigation — Blacklist the Kernel Module
The fastest fix that doesn’t require a reboot is blacklisting
algif_aead. This has no impact on dm-crypt/LUKS, kTLS, IPsec/XFRM,
OpenSSL, GnuTLS, NSS, or SSH — none of them use algif_aead.
|
|
Verify the module is gone:
|
|
This is effective but only covers the host kernel. For Docker containers, you need the runtime mitigation below.
Docker Mitigation — Custom Seccomp Profile to Block AF_ALG
Docker’s built-in default seccomp profile does not block the
AF_ALG socket family. Pod Security Standards Restricted also allows
it through.
The fix: create a custom seccomp profile that blocks socket family 38
(AF_ALG) while preserving every other Docker default behavior.
Step 1: Extract Docker’s Current Seccomp Profile
|
|
The default profile is embedded in the Docker binary. The patch below defines the custom profile directly.
Step 2: Create the Custom Seccomp Profile
Create /etc/seccomp/docker-block-af-alg.json:
|
|
This allows the socket syscall for every address family except
AF_ALG (value 38). Every other Docker default syscall remains
permitted. If you prefer to keep the full default profile and patch the
socket entry, use the harden-docker-seccomp
script which extracts and patches Docker’s actual profile.
Step 3: Configure Docker Daemon
|
|
If your daemon.json already has content, merge carefully. Backup
first:
|
|
Step 4: Reload Docker
|
|
This sends SIGHUP — no container downtime, no restart.
Step 5: Verify the Block Works
|
|
You should see a permission error or socket failure. If it says “BLOCKED”, the seccomp filter needs adjustment.
AppArmor Alternative
If you use AppArmor instead of seccomp, create a profile that denies
af_alg:
profile docker-custom flags=(attach_disconnected,mediate_deleted) {
# Include Docker's default AppArmor profile
include <abstractions/docker>
# Deny AF_ALG socket creation
deny capability sys_admin,
deny @{PROC}/sys/net/ r,
}
Load it and apply to containers:
|
|
Kernel Patch Strategy
The seccomp profile is a runtime bandage. The real fix is patching the kernel.
Check Your Distro
| Distro | Advisory | Fixed Kernel |
|---|---|---|
| Ubuntu 26.04 | Ships unaffected | 7.0+ |
| Ubuntu 24.04 / 22.04 | kmod mitigation | Pending kernel update |
| Debian 12 / 11 | Backported | Check apt changelog linux-image-* |
| RHEL 9 / Rocky 9 / Alma 9 | RHSA advisory | Updated 5.14.0+ |
| Fedora 40+ | Updated kernel | 6.19.12+ |
Check for available kernel updates:
|
|
Apply and Reboot
|
|
Verify After Reboot
|
|
Both checks should show the new kernel and algif_aead either absent
or blacklisted.
Verification Checklist
After applying mitigations, confirm everything works:
|
|
Expected output:
=== Host kernel ===
6.19.12-amd64
=== algif_aead module ===
OK - not loaded
=== Docker seccomp profile ===
seccomp=/etc/seccomp/docker-block-af-alg.json
=== Container AF_ALG test ===
AF_ALG blocked - OK
Long-Term Hardening for Docker Homelabs
CVE-2026-31431 is a reminder that Docker’s default security posture leaves gaps. Use it as a catalyst to level up your container security:
- Update Docker Engine to 29.4.3+ and keep it current
- Run rootless Docker for an extra isolation layer around the daemon socket
- Avoid
--privilegedwherever possible — it bypasses seccomp, AppArmor, and capabilities - Drop capabilities at the container level — don’t run containers
with
SYS_ADMIN,NET_ADMIN, orSYS_PTRACEunless required - Use
--security-opt no-new-privileges:trueto prevent binary privilege escalation inside containers - Consider runtime detection with Falco or Tracee for behavioral monitoring of anomalous syscalls
- Keep your kernel updated — subscribe to your distro’s security feed and schedule quarterly maintenance windows
Conclusion
CVE-2026-31431 is not a theoretical vulnerability. The exploit is published, deterministic, and works across every major Linux distribution. For Docker users, it’s a container escape — the most serious class of vulnerability in containerized environments.
The fix is a two-layer approach:
- Patch the kernel to remove the vulnerable code path. This is the real fix.
- Deploy a custom seccomp profile blocking AF_ALG for all containers. This is the belt-and-suspenders that protects you until you reboot.
Both take about 15 minutes in a homelab. The custom seccomp profile takes five minutes to deploy with no container downtime. The kernel update takes another ten plus a reboot.
Your homelab’s containers share a kernel with the host. A vulnerability in that kernel is a vulnerability in every container. Mitigate Copy Fail today, and use it as a forcing function to review your broader Docker security posture.