
Analysis and mitigation of Linux kernel Copy Fail (CVE-2026-31431) vulnerability exploiting AF_ALG/splice page cache mutation, with PoC checker, auditd detection rules, and kernel update verification.
This project analyzes the operational principle of the Linux Kernel Copy Fail (CVE-2026-31431) vulnerability and compares the pre-patch and post-patch state using the non-destructive checker from the public PoC repository.
It does not perform actual setuid binary modification or /etc/passwd modification exploits, but focuses on safely checking vulnerability based on a temporary testfile and analyzing detection/mitigation perspectives.
The goal of this project is not simply to run an exploit, but to understand the combination of internal structures that cause a Linux kernel vulnerability and to organize how to identify and respond from an operational perspective.
The scope of work is as follows:
Vulnerability principle analysis
↓
PoC code structure analysis
↓
Non-destructive checker-based practice
↓
Pre-patch vs. post-patch comparison
↓
Detection/mitigation measures summary
In this practice, only vulnerable.c from the copy-fail-c repository was executed.
| Category | Pre-patch | Post-patch |
|---|---|---|
| OS | Ubuntu 24.04.2 LTS | Ubuntu 24.04.4 LTS |
| Kernel | 6.8.0-53-generic | 6.8.0-134-generic |
| Account | Normal user client | Normal user client |
| checker | vulnerable | vulnerable |
| Test method | Non-destructive check based on temporary testfile | Same checker re-executed |
Pre-patch kernel info:
Linux ubuntu-server 6.8.0-53-generic #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Post-patch kernel info:
Linux ubuntu-server 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Kernel package change summary:
- linux-image-6.8.0-53-generic 6.8.0-53.55
- linux-image-generic 6.8.0-53.55+1
+ linux-image-6.8.0-134-generic 6.8.0-134.134
+ linux-image-generic 6.8.0-134.134
+ linux-generic 6.8.0-134.134
+ linux-headers-generic 6.8.0-134.134
Copy Fail is a vulnerability that occurs when the Linux kernel's AF_ALG AEAD processing path and splice() zero-copy behavior combine, causing the page cache of a read-only file to be used as an incorrect write target.
The core components are as follows:
| Element | Role |
|---|---|
| Linux page cache | Kernel mechanism that caches disk file contents in RAM |
splice() | A zero-copy syscall that connects data via references within the kernel without copying to user space |
AF_ALG | An interface that allows user space to use the Linux kernel crypto API like a socket |
| AEAD in-place processing | Optimization that processes input and output in the same buffer instead of separate buffers |
authencesn | Crypto template that generates a 4-byte scratch write during AEAD processing |
The core flow of the vulnerability is as follows:
Readable file
↓
Loaded into Linux page cache
↓
Page cache reference passed to AF_ALG crypto path via splice()
↓
Input and output scatterlists become entangled due to AEAD in-place processing
↓
4-byte scratch write occurs during authencesn processing
↓
Write occurs not to a separate output buffer but to the page cache
↓
Page cache mutation occurs
In other words, splice() passes a page cache reference, AEAD in-place processing ties input and output through the same path, and authencesn causes the actual 4-byte write.
The repository structure used in the practice is as follows:
copy-fail-c/
├── exploit.c
├── exploit-passwd.c
├── vulnerable.c
├── payload.c
├── utils.c
├── utils.h
├── Makefile
└── nolibc/
| File | Role | Usage in this practice |
|---|---|---|
utils.c, utils.h | Implementation of AF_ALG/splice based page cache mutation primitive | Used for analysis and executing vulnerable |
vulnerable.c | Non-destructive vulnerability check tool based on temporary testfile | Executed |
exploit.c | setuid root binary page cache modification variant | Not executed |
exploit-passwd.c | /etc/passwd page cache modification variant | Not executed |
payload.c | Payload to be executed with root privileges | Not executed |
Makefile | Build automation | Only vulnerable target used |
nolibc/ | Lightweight libc replacement for building small static ELF payloads | Only analyzed |
utils.cThe core of utils.c is the patch_chunk() family of page cache mutation primitives. This function uses AF_ALG and splice() to connect the page cache of the target file to the crypto processing path, and on vulnerable kernels checks whether part of the page cache gets overwritten during AEAD processing.
vulnerable.cvulnerable.c does not touch actual system files. It creates a temporary testfile in the current directory and checks whether its page cache is mutated.
In this project, only this file was executed.
Before performing the kernel update, the OS, kernel, and package states were recorded.
mkdir -p ~/copyfail-mini/{before,after,logs}
cd ~/copyfail-mini
uname -a | tee before/uname.txt
cat /etc/os-release | tee before/os-release.txt
dpkg -l | grep -E 'linux-image|linux-headers|linux-generic|linux-virtual' | tee before/kernel-package.txt
git clone https://github.com/jihwan77/copy-fail-c.git
cd copy-fail-c
make clean
make vulnerable
In this practice, the default make was not used to build exploit binaries together; only the vulnerable target was used.
./vulnerable > ../before/vulnerable-output.txt 2>&1
echo $? >> ../before/vulnerable-output.txt
cat ../before/vulnerable-output.txt
Pre-patch execution result:

Judgment:
exit code 100
→ page cache mutation confirmed
→ Copy Fail primitive confirmed in pre-patch kernel
After saving the pre-patch results, Ubuntu package update was performed.
sudo apt update
sudo apt full-upgrade -y
sudo reboot
After reboot, the kernel changed as follows:
Before: 6.8.0-53-generic
After : 6.8.0-134-generic
cd ~/copyfail-mini/copy-fail-c
make clean
make vulnerable
./vulnerable > ../after/vulnerable-output.txt 2>&1
echo "exit_code=$?" >> ../after/vulnerable-output.txt
cat ../after/vulnerable-output.txt
Post-patch execution result:

Result comparison:
| Item | Pre-patch | Post-patch |
|---|---|---|
| Kernel | 6.8.0-53-generic | 6.8.0-134-generic |
| Checker result | VULNERABLE | authencesn template not registered |
| Exit Code | 100 | 2 |
| Page cache mutation | Confirmed | Checker did not reach mutation step |
| Interpretation | Copy Fail primitive working | PoC required AEAD/authencesn path entry failed |
The post-patch exit_code=2 does not simply mean “not vulnerable.” Precisely, it means:
The authencesn(hmac(sha256),cbc(aes)) template of AF_ALG is not registered,
so the checker could not directly determine vulnerability.
Upon further checking, the algif_aead module load was blocked after the Ubuntu update.
lsmod | grep -E 'af_alg|algif_aead'
Result:
af_alg 32768 0
algif_aead was not loaded.
sudo modprobe algif_aead
Result: