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.

Verify Your Linux Kernel Is Actually Patched Against Copy Fail (CVE-2026-31431)

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:

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.

Isometric illustration of stacked Linux servers with version numbers and green and red status badges
Once you trust your single-host check, you can roll it out across an entire fleet with a one-liner.

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.

Translucent isolated sandbox cube glowing in a dark server room with warning lines around it
Always run the PoC inside a snapshot you can destroy afterwards.

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.