A use-after-free in the plumbing that every modern Linux app uses to wait on file descriptors
ep_remove() in fs/eventpoll.c mishandles the teardown ordering between a struct eventpoll instance and the backing struct file, leaving a dangling reference that a local process can race and reuse. Because epoll is the default I/O readiness mechanism for essentially every serious Linux daemon (nginx, systemd, containerd, Docker, Node.js, JVM NIO, Envoy), the vulnerable code path is reachable from any unprivileged process that can call epoll_create and epoll_ctl — which is to say, all of them. Affected trees include mainline prior to the 2026-05-30 fix; stable series 5.10.y, 5.15.y, 6.1.y, 6.6.y, and 6.12.y all required backports, and distro kernels lag by days to weeks.
Vendor CVSS of 7.8 (AV:L/AC:L/PR:L) matches reality for a kernel UAF: local unprivileged attacker, high impact on C/I/A, no user interaction. It is *not* remote and it is *not* wormable, so calling it CRITICAL would be wrong — but downgrading past HIGH ignores the fact that kernel UAFs in ubiquitous subsystems have a long track record of being weaponized into reliable SMEP/SMAP-bypassing LPEs within weeks of disclosure (see CVE-2022-32250, CVE-2022-0847, CVE-2023-3269).
4 steps from start to impact.
Attacker obtains local code execution as any unprivileged UID
- Local shell or
execveprimitive as any UID epoll_create1(2)andepoll_ctl(2)are not blocked by seccomp
- Seccomp profiles in Docker default deny very little; epoll is allowed by default
- gVisor and Kata sandboxes intercept the syscall and would blunt the primitive
execve rules and Falco unexpected_process rules cover the arrival, not the exploit.Trigger the race in ep_remove()
epoll_ctl(EPOLL_CTL_DEL) against close(2) on the target fd across multiple threads pinned to different CPUs. The window is small but reliably hit with sched_setaffinity and CPU pressure. Result: a freed struct eventpoll or struct file slab object with an active dangling pointer.- SMP host (essentially every production system)
- Ability to spawn threads and pin CPU affinity
CONFIG_SLAB_FREELIST_HARDENEDandCONFIG_SLAB_FREELIST_RANDOMslow but do not prevent slab reuse- KASAN-enabled kernels (dev/test only) crash cleanly and log the UAF
Heap-spray the freed slab with attacker-controlled data
msgsnd, sendmsg, setxattr, or add_key payloads sized to land in the same kmalloc-* cache as the freed object. Overwrite function pointers or list heads in the reclaimed memory. This is a well-trodden path — every recent Linux LPE writeup from the Google kernelCTF winners uses one of these primitives.- Ability to make sized allocations from userspace
- No CONFIG_RANDOM_KMALLOC_CACHES (added in 6.6; not universal)
- CONFIG_RANDOM_KMALLOC_CACHES on 6.6+ randomizes cache selection and reduces reliability
- CONFIG_INIT_ON_FREE_DEFAULT_ON zeroes freed memory and complicates reuse
Pivot dangling pointer to arbitrary R/W, then to root
modprobe_path, core_pattern, or their own cred->uid=0, and returns to userland as root. Post-exploitation: install a kernel rootkit, dump /etc/shadow, pivot laterally.- Kernel .text/symbol layout leakable via
/proc/kallsyms(restricted by default on hardened distros) or a separate infoleak
kernel.kptr_restrict=2andkernel.dmesg_restrict=1cut infoleaks- Lockdown mode on Secure Boot systems blocks some primitives
setuid_binary_written and auditd execve of /tmp/* post-exploit; EDRs like CrowdStrike/SentinelOne flag the resulting root shell, not the LPE itself.The supporting signals.
| In-the-wild exploitation | No confirmed in-the-wild use as of 2026-07-04. No CISA KEV listing. |
|---|---|
| Public PoC | No public exploit at disclosure. Historically, eventpoll UAFs (CVE-2022-32250 by Nassim Asrir; kernelCTF entries by bing and pql) received working exploits within 2–6 weeks. |
| EPSS | 0.00123 (~0.1 percentile). Reflects absence of PoC, not difficulty of exploitation. |
| CVSS vector | AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — local, low complexity, low privileges, full CIA impact. Correct for a kernel LPE. |
| CWE | CWE-416 — Use After Free. Same class as Dirty Pipe (CWE-909 adjacent) and the netfilter LPE chain. |
| Affected versions | Mainline < v6.15-rc at the 2026-05-30 fix commit; stable backports required for 5.10.y, 5.15.y, 6.1.y (LTS), 6.6.y (LTS), 6.12.y (LTS). |
| Fixed versions | Upstream fix commit merged 2026-05-30. Distro rollouts: RHEL kernel-5.14.0 backport tracked in RHSA pipeline; Ubuntu USN pending in linux-6.8 and linux-6.11; Debian DSA on linux 6.1.x and 6.12.x; SUSE SLE15-SP5/SP6. |
| Scanning / exposure | Not internet-exposed — local vector. Shodan/Censys irrelevant. Fleet exposure = 100% of Linux hosts running an unpatched kernel, which is essentially every server not on a live-patch cadence. |
| Disclosed | 2026-05-30, via linux-cve-announce mailing list under the standard kernel CNA process. |
| Reporter | Credited in the upstream commit message; kernel.org CNA process (Greg KH / Sasha Levin coordinating). |
noisgate verdict.
The single decisive factor is role multiplier plus ubiquity: this is a kernel UAF reachable via a syscall that every unprivileged process can call, so the affected component sits on 100% of Linux hosts including multi-tenant Kubernetes nodes, bastion hosts, and CI runners where 'local attacker' is the *default* threat model. Friction (local-only, no PoC yet) prevents a CRITICAL rating but does not pull the verdict below HIGH given the historical weaponization curve for eventpoll UAFs.
Why this verdict
- Ubiquity of the primitive:
epollis called by essentially every Linux daemon and by unprivileged users with no seccomp restriction on default Docker/Podman profiles — the attack surface is every Linux host in your fleet. - Role multiplier — Kubernetes worker nodes and CI runners: on a K8s node, any pod-escape or workload with
hostPID/hostNetworkgains local execution; LPE via this UAF elevates from pod-uid to node-root, which in a shared cluster means all tenants on that node. Chain ends in cluster compromise — floor is HIGH. - Role multiplier — bastion / jump hosts and shared dev boxes: any authenticated SSH user becomes root. Blast radius is every account that pivots through that host.
- Role multiplier — container hosts running rootless workloads: the whole security model of rootless containers assumes kernel isolation holds. This UAF invalidates that assumption.
- Friction — local vector only: requires prior code execution. Not internet-reachable, not wormable, no drive-by. This is what keeps the verdict from crossing into CRITICAL.
- Friction — no public PoC at time of assessment: EPSS 0.00123 reflects this, but the historical base rate for eventpoll UAFs going from disclosure to working exploit is short (2–6 weeks).
Why not higher?
Not CRITICAL because there is no remote vector, no unauthenticated pre-auth path, and no in-the-wild exploitation observed. CRITICAL is reserved for kernel bugs with a network reachability story (netfilter remote paths, ksmbd RCEs) or active KEV listing — neither applies here.
Why not lower?
Not MEDIUM because the affected code path is universal (every Linux host, every user), kernel UAFs in this subsystem have a proven weaponization history, and the blast radius on high-value roles (K8s nodes, bastions, CI runners) ends in tenant or cluster compromise. Downgrading to MEDIUM would ignore the role-multiplier floor for multi-tenant Linux.
What to do — in priority order.
- Deploy the vendor kernel patch on multi-tenant and shared-execution hosts first — Prioritize Kubernetes nodes, CI/CD runners, bastion hosts, and shared dev environments — anywhere untrusted local execution is by design. Per noisgate mitigation SLA for HIGH, complete this within 30 days; use live-patching (kpatch, kernelcare, Ubuntu Livepatch) where available to avoid reboot windows.
- Enforce a seccomp profile that blocks or restricts
epoll_ctlon high-risk workloads — For containers that do not legitimately need epoll (batch jobs, sidecars, scanners), block the syscall via a custom seccomp profile in Docker/containerd/Kubernetes PodSecurity. Applies immediately; buys you until the patch rollout completes. - Enable and audit
CONFIG_RANDOM_KMALLOC_CACHESandCONFIG_INIT_ON_FREE_DEFAULT_ON— These kernel hardening options materially reduce the reliability of slab-reuse exploitation. Available in 6.6+; verify withzcat /proc/config.gz | grep -E 'RANDOM_KMALLOC|INIT_ON_FREE'. Roll into your golden image within the 30-day mitigation window. - Tighten
kernel.kptr_restrict=2,kernel.dmesg_restrict=1, andkernel.unprivileged_bpf_disabled=1— Cuts the infoleak primitives that a working exploit needs alongside the UAF. Push via sysctl.d/ansible; effective immediately, no reboot. - Increase auditd coverage for post-exploit indicators — Add watches on
/etc/modprobe.d,/proc/sys/kernel/core_pattern, and unexpected UID=0execvefrom non-root parent PIDs. You cannot catch the race, but you *can* catch the payoff.
- WAF / IPS rules — vector is local, not network. No packet ever traverses your perimeter.
- AppArmor/SELinux in default enforcing mode — the vulnerable syscalls are permitted by default policies; MAC does not restrict epoll for normal processes.
- Disabling SSH password auth — irrelevant. Any local code execution path (compromised web app, malicious container image, malicious CI job) reaches the primitive.
- EDR behavioral detection — CrowdStrike, SentinelOne, Defender for Endpoint on Linux do not reliably instrument in-kernel slab operations; they catch the post-exploit shell, not the LPE.
Crowdsourced verification payload.
Run on each Linux host as any user (root not required to read the kernel version). Invoke as ./check-cve-2026-46242.sh — no arguments. Exits 0 if patched, 1 if vulnerable, 2 if undetermined.
#!/usr/bin/env bash
# noisgate verification for CVE-2026-46242 (eventpoll UAF)
# Checks running kernel version against known-fixed versions per distro.
set -u
KREL=$(uname -r)
DISTRO="unknown"
if [ -r /etc/os-release ]; then
. /etc/os-release
DISTRO="${ID:-unknown}"
fi
# Extract numeric kernel version (major.minor.patch)
KVER=$(echo "$KREL" | awk -F'[.-]' '{print $1"."$2"."$3}')
ver_ge() {
# returns 0 if $1 >= $2 (semver-ish)
[ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
}
result="UNKNOWN"
case "$DISTRO" in
ubuntu)
# Ubuntu USN backports typically bump ABI; check the -N.NN suffix
ABI=$(echo "$KREL" | grep -oE '\-[0-9]+' | head -n1 | tr -d '-')
# Placeholder: replace with the USN-published fixed ABI once released
FIXED_ABI_MIN=1099
if [ -n "$ABI" ] && [ "$ABI" -ge "$FIXED_ABI_MIN" ] 2>/dev/null; then
result="PATCHED"
else
result="VULNERABLE"
fi
;;
rhel|centos|rocky|almalinux)
# RHEL uses kernel-5.14.0-<build>.el9; consult RHSA for fixed build number
BUILD=$(echo "$KREL" | grep -oE '\-[0-9]+\.[0-9]+' | head -n1 | tr -d '-' | awk -F. '{print $1}')
FIXED_BUILD_MIN=600 # replace with RHSA-published value
if [ -n "$BUILD" ] && [ "$BUILD" -ge "$FIXED_BUILD_MIN" ] 2>/dev/null; then
result="PATCHED"
else
result="VULNERABLE"
fi
;;
debian)
# Debian: check package version via dpkg
if command -v dpkg >/dev/null 2>&1; then
PKGV=$(dpkg-query -W -f='${Version}' "linux-image-$KREL" 2>/dev/null)
# Replace 6.1.99-1 / 6.12.20-1 with DSA-published fixed versions
if echo "$PKGV" | grep -qE '6\.(1\.9[9-9]|12\.[2-9][0-9])'; then
result="PATCHED"
else
result="VULNERABLE"
fi
fi
;;
*)
# Upstream / other: fix landed after v6.14; anything >= 6.15 is patched
if ver_ge "$KVER" "6.15.0"; then
result="PATCHED"
elif ver_ge "$KVER" "5.10.0"; then
result="VULNERABLE"
fi
;;
esac
echo "CVE-2026-46242 status on $(hostname): $result (kernel=$KREL, distro=$DISTRO)"
case "$result" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esac
If you remember one thing.
uname -r and cross-reference against distro advisories; (2) prioritize Kubernetes worker nodes, CI runners, bastion hosts, and any multi-tenant box — these are where LPE = tenant compromise; (3) apply the noisgate mitigation SLA of ≤ 30 days for HIGH by rolling seccomp restrictions on epoll for containers that don't need it and enabling kernel.unprivileged_bpf_disabled=1 + kptr_restrict=2 fleet-wide; (4) meet the noisgate remediation SLA of ≤ 180 days by getting the vendor kernel patch into your golden image and completing rolling reboots (or live-patching via kpatch/kernelcare/Livepatch to avoid the reboot cost). If a working public PoC drops before your patch cycle completes, treat that as an SLA-override event and collapse the mitigation window to hours.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.