95589e26cb
release / build (arm64) (push) Waiting to run
release / build (x86_64) (push) Waiting to run
release / build (x86_64-static / musl) (push) Waiting to run
release / build (arm64-static / musl) (push) Waiting to run
release / release (push) Blocked by required conditions
Jaeyoung Chung's kernelCTF "Bad Epoll": a race-condition use-after-free in fs/eventpoll.c. ep_remove() clears file->f_ep under f_lock but keeps using the file (hlist_del_rcu + unlock) while a concurrent __fput() frees the still-referenced struct eventpoll. Reachable by any unprivileged user with no userns / CONFIG / capability; weaponised via cross-cache to a struct file, /proc/self/fdinfo arb-read, and ROP. Introduced 58c9b016e128 (6.4), fixed a6dc643c6931 (7.1-rc1), stable backport 7.0.13. CWE-416; not in KEV. The corpus's first epoll / VFS-teardown module. Shipped as a reconstructed, deliberately under-driven trigger on the stackrot / nft_catchall contract: detect() is a pure version gate (>= 6.4 and below the on-branch fix; no userns precondition -- epoll needs none); exploit() forks a CPU-pinned child that exercises the ep_remove-vs-__fput close window a hard-bounded 48 attempts / 2s and returns EXPLOIT_FAIL. It does not grind the race to a win, does not do the cross-cache reclaim, and does not bundle the fdinfo R/W + ROP (a won race frees a live struct file and rarely trips KASAN -> silent-corruption risk). Wired: registry, Makefile, safety rank (12 -- lowest in the corpus; a kernel race is the least predictable class), 5 detect() test rows (version gating), CVE metadata (sorted insert, CWE-416 / T1068 / not-KEV), README + CVES.md + website counts (44/39), RELEASE_NOTES v0.9.12, and a verify-vm target (sweep pending). Detection rules are intentionally weak/structural (epoll ubiquitous, rarely KASAN) -- post-exploitation euid-0 transition, no yara. Also corrects pre-existing README drift in the "not yet verified" count (8 -> 11). Not VM-verified, so the verified count stays 28 of 39. Version 0.9.12. Credit: Jaeyoung Chung (J-jaeyoung). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KUq4DGXSPBmPkAyJ9WnM9n
71 lines
3.6 KiB
Markdown
71 lines
3.6 KiB
Markdown
# NOTICE — bad_epoll (CVE-2026-46242)
|
|
|
|
## Vulnerability
|
|
|
|
**CVE-2026-46242** — "Bad Epoll", a **race-condition use-after-free** in
|
|
the Linux kernel epoll subsystem (`fs/eventpoll.c`). On the file-teardown
|
|
path, `ep_remove()` clears `file->f_ep` under `file->f_lock` but continues
|
|
to use the file inside the critical section (`hlist_del_rcu()` over the
|
|
eventpoll `refs` list + `spin_unlock()`). A concurrent `__fput()` of a
|
|
linked epoll file observes the transient `NULL` `f_ep`, skips
|
|
`eventpoll_release_file()`, and proceeds to `f_op->release`, freeing a
|
|
`struct eventpoll` still in use → UAF.
|
|
|
|
The bug is reachable by **any unprivileged local user** — `epoll_create1`,
|
|
`epoll_ctl`, and `close` require no capability, no user namespace, and no
|
|
special kernel config. Exploitation converts the 8-byte UAF write into
|
|
control of a `struct file` via a cross-cache attack, gains arbitrary
|
|
kernel read through `/proc/self/fdinfo`, and ROPs to a root shell —
|
|
roughly 99% reliable despite a ~6-instruction race window. It also affects
|
|
Android. NVD class: **CWE-416** (Use After Free), with a **CWE-362** race
|
|
root cause. **Not** in CISA KEV (brand new).
|
|
|
|
## Research credit
|
|
|
|
- **Discovery, exploitation, and public PoC** by **Jaeyoung Chung**
|
|
(GitHub `J-jaeyoung`), submitted as a zero-day to **Google's kernelCTF**
|
|
program. Repository: <https://github.com/J-jaeyoung/bad-epoll> and the
|
|
kernelCTF submission under
|
|
`J-jaeyoung/security-research` (`CVE-2026-46242_lts_cos`, target
|
|
`lts-6.12.67`). SKELETONKEY's trigger reconstruction is informed by that
|
|
public PoC (the epoll object graph and the `ep_remove`-vs-`__fput`
|
|
close-race shape only — no offsets or ROP are reused).
|
|
- **Introduced** by commit `58c9b016e128` (Linux 6.4, 2023-04-08).
|
|
- **Fixed upstream** by commit
|
|
`a6dc643c69311677c574a0f17a3f4d66a5f3744b`, merged for **7.1-rc1**
|
|
(2026-04-24); stable backport **7.0.13**.
|
|
- Debian security tracker (authoritative backport versions):
|
|
<https://security-tracker.debian.org/tracker/CVE-2026-46242> — forky
|
|
`7.0.13-1` / sid `7.0.14-1` fixed; trixie 6.12.x still vulnerable at time
|
|
of writing; bookworm 6.1 and bullseye 5.10 "not affected — vulnerable
|
|
code not present".
|
|
|
|
All credit for finding, analysing, and exploiting this bug belongs to
|
|
Jaeyoung Chung and to the upstream maintainers who fixed it. SKELETONKEY
|
|
is the bundling and bookkeeping layer only.
|
|
|
|
## SKELETONKEY role
|
|
|
|
🟡 **Trigger (reconstructed) — primitive-only, not VM-verified.** This is
|
|
the corpus's first epoll / VFS-file-teardown module and its cleanest
|
|
example of an SMP kernel race, shipped on the same "fire the bug class and
|
|
stop" contract as `stackrot` (CVE-2023-3269) and `nft_catchall`
|
|
(CVE-2026-23111).
|
|
|
|
`detect()` is a pure kernel-version gate (vulnerable iff `>= 6.4` and below
|
|
the fix on-branch; stable backport 7.0.13, 7.1+ inherits; 6.1/5.10 not
|
|
affected) — no userns or CONFIG precondition, because none is required.
|
|
`exploit()` forks a CPU-pinned child that builds the epoll race pair and
|
|
exercises the `ep_remove`-vs-`__fput` concurrent-close window a
|
|
hard-bounded number of times (48 attempts / 2 s), widening it with
|
|
`close(dup())` false-sharing storms, snapshots the eventpoll slab, and
|
|
returns `EXPLOIT_FAIL`.
|
|
|
|
It is **deliberately under-driven**: a won race frees a live
|
|
`struct eventpoll` (real corruption that rarely trips KASAN), so the module
|
|
does not grind the race to a win, does not perform the cross-cache reclaim,
|
|
and does not bundle the `/proc/self/fdinfo` arbitrary-read + ROP root-pop
|
|
(per-build offsets refused). The trigger is reconstructed from the public
|
|
kernelCTF PoC, not VM-verified — it never claims root it did not get. It
|
|
carries the lowest `--auto` safety rank in the corpus.
|