
Aarch64 proof-of-concept checker for CVE-2026-46242 'Bad Epoll', triggering the Linux kernel epoll close-vs-close use-after-free race to validate local privilege escalation.
This repo is a bad Epoll validator for aarch64 kernels based on exploit already exists for x86.
Linux Kernel Local Privilege Escalation via
epollUse-After-Free Race Condition
| Field | Detail |
|---|---|
| CVE | CVE-2026-46242 |
| Nickname | Bad Epoll |
| Type | Use-After-Free (UAF) via race condition |
| CVSS | 7.8 (High) |
| Impact | Local Privilege Escalation → root |
| Affected kernels | 5.10 – 6.11 (LTS & COS) |
| Subsystem | fs/eventpoll.c (ep_remove() / close path) |
A race condition exists in the Linux kernel's epoll subsystem during concurrent close() operations on monitored and monitoring file descriptors. When two close paths execute simultaneously:
eventpoll object via ep_remove().hlist_del_rcu() write lands on the already-freed memory.This produces a Use-After-Free that corrupts the refs list of the reclaimed eventpoll, leaving a dangling epi->ffd.file pointer in the surviving epoll waiter — the foundation for the full exploit chain.
┌─────────────────────────────────────────────────────────┐
│ 1. KASLR Leak (prefetch side-channel via libxdk) │
├─────────────────────────────────────────────────────────┤
│ 2. Race Trigger (close-vs-close UAF on eventpoll) │
│ ├─ Adaptive timer: stat/exec phases calibrate ahead │
│ ├─ False-sharing: dup()+close() widens race window │
│ └─ Oracle: EPOLL_CTL_ADD depth-3 → ELOOP? │
├─────────────────────────────────────────────────────────┤
│ 3. Reclaim (freed eventpoll → ep_uaf_target) │
│ └─ Dangling epi->ffd.file in ep_uaf_waiter │
├─────────────────────────────────────────────────────────┤
│ 4. Cross-Cache (filp slab → buddy → pipe_buffer) │
│ ├─ Drain per-CPU partial list │
│ ├─ Free enclosing objects → slab page freed │
│ └─ Reclaim as pipe pages (256 × 4 KiB) │
├─────────────────────────────────────────────────────────┤
│ 5. Constrained AAR (fdinfo ino:/sdev: as read oracle) │
│ ├─ Fake file spray tiles forged struct file in pipe │
│ ├─ 8B read: ino: ← i_ino at controlled offset │
│ ├─ 4B read: sdev: ← sigaltstack(sas_ss_sp) cursor │
│ └─ Walk: init_task → find exploit task → resolve │
│ files → fdt → fd[] → pipe_buffer → struct page │
├─────────────────────────────────────────────────────────┤
│ 6. RIP Control (f_op→poll hijack → JOP → ROP) │
│ ├─ Forge f_op = virt, f_count = virt - 1 │
│ ├─ JOP pivot chain lands rsp on controlled page │
│ └─ libxdk ROP: commit_creds(init_cred) + ns switch │
├─────────────────────────────────────────────────────────┤
│ 7. Ret2Usr execve("/bin/bash") as root │
└─────────────────────────────────────────────────────────┘
# aarch64 (cross-compile or native)
aarch64-linux-gnu-gcc -O2 -Wall -pthread -march=armv8-a \
race_check_aarch64.c -o race_check
./race_check [timeout_seconds] # default: 300s (5 min)
./race_check 120 # 2-minute timeout
| Code | Meaning |
|---|---|
0 | Race won — kernel is vulnerable |
1 | Timeout — race not detected (possible false negative, does not prove the kernel is patched) |
⚠️ Disclaimer: This repository is provided for educational and authorized security research purposes only. Unauthorized use of this exploit against systems you do not own or have explicit permission to test is illegal.