Verify Your Linux Kernel Is Actually Patched Against Copy Fail (CVE-2026-31431)
A hands-on, copy-paste tutorial: identify your running kernel, compare against your distro's fixed version, confirm the actual fix landed in the changelog, run the public PoC safely in a sandbox, and audit your whole fleet with one playbook.
In our previous breakdown of Copy Fail (CVE-2026-31431), we walked through what the vulnerability is and which servers are most exposed. The fix sounds simple — "update your kernel and reboot" — but in practice plenty of boxes get an updated package without ever booting into the patched kernel. This guide shows you, step by step, how to confirm a Linux machine is genuinely no longer vulnerable.
Every command below is safe to run on a production server. The exploitation step at the end runs only in a throwaway VM. Total time: about ten minutes per host, less than a minute once you script it.
Step 1 — Identify the kernel that is actually running
Patched kernel packages can sit installed for weeks behind a running unpatched kernel. The only version that matters is the one currently loaded in memory.
uname -r
# example output: 5.15.0-119-generic
uname -a
cat /proc/version
Write down the exact string from uname -r. That is what you will compare against your distribution's advisory.
Has the box been rebooted since the package landed?
If the running kernel is older than the newest installed kernel, you have a pending reboot — and you are still vulnerable.
# Debian / Ubuntu
ls -t /boot/vmlinuz-* | head -n 1
# If that filename does not match 'vmlinuz-' + $(uname -r), reboot is required.
# RHEL / Rocky / Alma / Amazon Linux
sudo dnf list installed 'kernel*' | tail -n 5
sudo needs-restarting -r # exit code 1 = reboot required
Step 2 — Look up your distribution's fixed version
Each vendor publishes a CVE tracking page that names the first patched kernel version for every supported release. Bookmark whichever applies to you:
- Ubuntu: ubuntu.com/security/CVE-2026-31431
- Red Hat: access.redhat.com/security/cve/CVE-2026-31431
- SUSE: suse.com/security/cve/CVE-2026-31431
- Debian: security-tracker.debian.org/tracker/CVE-2026-31431
- Amazon Linux: alas.aws.amazon.com/cve/html/CVE-2026-31431.html
- Arch: security.archlinux.org/CVE-2026-31431
Note the fixed version for your release — Ubuntu 22.04 and 24.04 will have different patched build numbers, and so will RHEL 9 vs RHEL 10.
Step 3 — Compare versions correctly
Naïve string comparison fails on kernel versions ("5.15.0-119" sorts before "5.15.0-9" alphabetically). Use your distro's package tooling, which understands the proper ordering.
# Debian / Ubuntu — uses dpkg's version comparator
dpkg --compare-versions "$(uname -r | sed 's/-generic//; s/-aws//; s/-azure//')" ge "5.15.0-XXX"
echo "patched: $?" # 0 = yes, 1 = no
# RHEL family — uses rpmdev-vercmp (yum-utils / rpmdevtools)
rpmdev-vercmp "$(uname -r)" "5.14.0-XXX.el9"
# Exit codes: 0 equal, 11 first newer, 12 first older
Replace XXX with the version your distribution lists as containing the Copy Fail fix. If your running kernel is greater than or equal to the fixed version, you are patched at the package level.
Step 4 — Confirm the actual fix is in your changelog
Versions are useful, but a backport can land in any minor revision. Verify the patch by name. The upstream commit that fixes Copy Fail is a664bf3d603d, in the file crypto/algif_aead.c.
# Debian / Ubuntu
zcat /usr/share/doc/linux-image-$(uname -r)/changelog.Debian.gz | grep -iE 'CVE-2026-31431|copy.?fail|algif_aead' | head
# RHEL family
rpm -q --changelog kernel-$(uname -r) | grep -iE 'CVE-2026-31431|copy.?fail|algif_aead' | head
A non-empty match confirms the fix is in the running kernel's source tree. An empty result means either the patch is not there, or your distro vendored it under a different label — fall back to Step 5.

Step 5 — Check the runtime mitigation state
If you applied the interim mitigation from the previous post (blacklisting algif_aead), confirm that is actually in effect on the running system, not just written to a config file.
# Is the module currently loaded?
lsmod | grep algif_aead # empty = not loaded (good for mitigation)
# Is it blacklisted?
grep -r algif_aead /etc/modprobe.d/
# Will the kernel even allow loading it?
modprobe --dry-run algif_aead 2>&1
# 'install /bin/true' or 'blacklist' message = mitigation active
This is a defense-in-depth check, not a substitute for a real kernel update. A blacklisted module on an unpatched kernel still leaves the bug present if anything legitimate ever loads algif_aead.
Step 6 — Active verification with the public PoC (in a sandbox)
The cleanest way to be certain a kernel is fixed is to run the published proof-of-concept against it. Do this only on a disposable VM that is a clone of your production image — never on a live host, and never on a system you do not own.

Spin up a VM (Multipass, libvirt, Vagrant, an EC2 t3.micro from a snapshot of your AMI — anything you can throw away). Inside it, as a normal unprivileged user:
# 1. Confirm the running kernel matches the production image you want to verify
uname -r
# 2. Pull the public PoC released by Xint
git clone https://github.com/xint-code/copy-fail-poc.git
cd copy-fail-poc
# 3. Run it as the unprivileged user — NOT as root
python3 copyfail.py
# Patched kernel: script exits with 'EAGAIN' or 'verification failed: kernel patched'.
# Vulnerable kernel: script returns a root shell within ~1 second.
If the patched VM resists the exploit and the unpatched VM falls over instantly, you have empirical confirmation. Destroy both VMs when you are done.
Step 7 — Audit a whole fleet at once
Once you trust the single-host check, you can sweep an entire fleet with a one-liner. Two patterns work well:
Quick SSH loop
while read host; do
printf "%-30s " "$host"
ssh -o ConnectTimeout=5 "$host" '
running=$(uname -r);
if zcat /usr/share/doc/linux-image-$running/changelog.Debian.gz 2>/dev/null | grep -q CVE-2026-31431; then
echo "PATCHED ($running)";
elif rpm -q --changelog kernel-$running 2>/dev/null | grep -q CVE-2026-31431; then
echo "PATCHED ($running)";
else
echo "UNPATCHED ($running)";
fi'
done < hosts.txt
Ansible playbook
- name: Audit Copy Fail (CVE-2026-31431)
hosts: all
gather_facts: yes
tasks:
- name: Check kernel changelog for the fix
shell: |
if [ -f /usr/share/doc/linux-image-$(uname -r)/changelog.Debian.gz ]; then
zgrep -l CVE-2026-31431 /usr/share/doc/linux-image-$(uname -r)/changelog.Debian.gz
else
rpm -q --changelog kernel-$(uname -r) | grep -l CVE-2026-31431 || true
fi
register: cf_check
changed_when: false
failed_when: false
- name: Report status
debug:
msg: "{{ inventory_hostname }} kernel {{ ansible_kernel }} {{ 'PATCHED' if cf_check.stdout else 'UNPATCHED' }}"
Tag any "UNPATCHED" host into your patching queue and re-run the audit after the maintenance window. The job is not done until every line says PATCHED.
Livepatching caveat
Ubuntu Livepatch, Oracle Ksplice, kpatch, and SUSE kGraft can apply the Copy Fail fix to a running kernel without a reboot. That is great for uptime, but it has a quirk: uname -r still reports the old version because the on-disk image has not changed. To confirm a livepatch is active:
# Ubuntu Livepatch
canonical-livepatch status --verbose | grep -iE 'CVE-2026-31431|copy.?fail'
# kpatch (RHEL / CentOS Stream)
kpatch list
sudo kpatch-patch-{NAME} status
# Oracle Ksplice
sudo uptrack-show | grep -iE 'CVE-2026-31431|copy.?fail'
If the livepatch is loaded and the CVE is listed, you are protected. Still schedule a reboot at the next maintenance window so the patch survives a panic or unexpected restart.
Bottom line
"Patched" is a multi-step claim: the package is installed and the running kernel contains the fix and nothing in your supply chain has reverted the change. Treat each of those as something you actively confirm, not something you assume from a successful apt upgrade.
Once you have run the seven checks above on one host, scripting them across the fleet is trivial. Save your audit script — when the next nine-year-old logic bug surfaces (and it will), you will only need to swap in a new CVE number.
Stuck on a check or hitting an oddly-versioned kernel? Drop a comment with your distro and uname -r output and we will help you read the tea leaves.