CVE-2026-46333 — Linux kernel LPE in the ptrace path: how a nine-year-old logic bug plus pidfd_getfd() turns any unprivileged shell into /etc/shadow plus root
21 May 2026. The Qualys Threat Research Unit (TRU) has published the full advisory for CVE-2026-46333 — a logic flaw in __ptrace_may_access() that has sat in the Linux kernel since v4.10-rc1 and, combined with pidfd_getfd(), opens a path from any unprivileged local shell to root or to sensitive credentials. Public exploits are circulating, distribution patches have been available since the weekend, immediate mitigation via kernel.yama.ptrace_scope = 2. For Moselwal container hosts, Kubernetes worker nodes and CI runners the kernel update wave has been moving through our pipeline since overnight.

TL;DR — the 90-second summary
- What was released?
The Qualys Threat Research Unit (TRU) has published the full advisory for CVE-2026-46333 on 20 May 2026 — a logic flaw in the Linux kernel function
__ptrace_may_access(). The bug has been in mainline since v4.10-rc1 (November 2016) and only became a full local-privilege-escalation chain with the laterpidfd_getfd()syscall (v5.6-rc1, January 2020).- How severe?
High (CVSS 7.8, Red Hat classifies as “Important”, NVD also High — not Critical). Operationally still urgent: public exploits are already circulating (an independent exploit derived from the public patch appeared during the embargo phase), and any unprivileged local shell can be turned into root or sensitive credentials through four documented call chains. The badge above reflects this operational urgency, not the pure CVSS score.
- What is the technical lever?
A race window: a privileged process drops credentials (the
do_exit()path), but remains reachable for ptrace-family operations because thedumpableflag does not gate whenmmis NULL. The attacker usespidfd_getfd()to capture open file descriptors and authenticated IPC channels from the dying process under their own UID.- What are the exploit targets?
Qualys built four working exploits against userland classics:
chage(set-uid-root, leaks/etc/shadow),ssh-keysign(set-uid-root, leaks SSH host private keys),pkexec(set-uid-root, RCE as root),accounts-daemon(root daemon, RCE as root via hijacked dbus connection). Tested on default installations of Debian 13, Ubuntu 24.04 + 26.04, Fedora 43 + 44.- Am I affected as a Moselwal customer?
If your platform runs on Linux container hosts (so: yes), it was affected. Containers share the host kernel — an Alpine/Wolfi/Debian userland layer does not protect against kernel bugs. Since the night of 20 to 21 May the distribution kernel updates have been rolled out through our pipeline, the rolling worker restarts are through.
- Immediate mitigation without patching?
sysctl -w kernel.yama.ptrace_scope=2persistent in/etc/sysctl.d/. It requiresCAP_SYS_PTRACEfor ptrace operations and blocks the public exploits because thepidfd_getfd()path is gated via__ptrace_may_access(). Note: ptrace_scope is monotonically non-decreasing — once raised, you can only lower it via reboot. Plus operational impact (gdb/strace/perf limitations).
Operational status at Moselwal customers
At Moselwal all container hosts (FrankenPHP workers on Wolfi base, application containers on Debian-slim, Kubernetes worker nodes on Ubuntu LTS) are run as a continuous-upgrade stack — as soon as a distribution kernel patch is available, the image rebuild pipeline runs plus a rolling worker restart. In the night of 20 to 21 May 2026 that played out exactly as expected: Debian 13 / Ubuntu 24.04+26.04 / Fedora 43+44 kernel patches went through the respective distribution repositories, the image builds were re-generated through the CI pipeline, the worker rollout has been running since early morning.
For Kubernetes setups there is an additional path: worker-node image refreshes run through the cluster operator pipeline (Karpenter / managed node pools), pods are evicted gracefully, the old workers fall out of the pool. Public-cloud managed Kubernetes customers (EKS, AKS, GKE) depend on their provider — the patches have been available in the AMI / VHD / GCE image listings since the weekend, node recycling is part of the routine.
One upfront content line: this is not a light kernel bug that slumbers in an exotic subsystem. __ptrace_may_access() is the central access control for the entire ptrace family — for everything that lets processes inspect or influence each other across process boundaries. That the function silently skips a branch (the dumpable flag) when the memory manager pointer is NULL is a class fault, not a one-off slip. Plus: pidfd_getfd() is a modern, generally well-designed API — it becomes the attack vector here because it falls back on exactly that __ptrace_may_access() as its gating function.
What is the problem? — the race in the __ptrace_may_access() path
| Field | Value |
|---|---|
| CVE | CVE-2026-46333 |
| CWE | CWE-362 (Race Condition) + CWE-269 (Improper Privilege Management) + CWE-200 (Information Exposure) |
| Component | Linux kernel: kernel/ptrace.c, function __ptrace_may_access(); syscall pidfd_getfd(2) |
| Vulnerability type | Logic flaw / TOCTOU race against do_exit() plus privilege escalation via file-descriptor hijack |
| Affected versions | Linux kernel since v4.10-rc1 (November 2016) up to the respective distribution patch versions from 20/21 May 2026 |
| Fixed in | Upstream patch commit 31e62c2 (Linus Torvalds, 14 May 2026); patched stable kernel lines per LWN: 7.0.8, 6.18.31, 6.12.89, 6.6.139, 6.1.173, 5.15.207, 5.10.256; distribution kernel packages since 15–20 May 2026 |
| Severity | High — CVSS 7.8, Red Hat “Important”, NVD “High”. Operationally still urgent: local-only but public exploits are circulating and lead to root from any unprivileged shell |
| Credits | Saeed Abbasi and the Qualys Threat Research Unit (TRU); upstream fix: Linus Torvalds, Christian Brauner, Kees Cook, Oleg Nesterov; distribution carry: Solar Designer, Sam James, Salvatore Bonaccorso |
The bug in a single line of code
__ptrace_may_access() is the central access control for ptrace operations. It checks several conditions — UID match, capabilities, the dumpable flag (prevents ptrace against privileged processes) — and ultimately calls an LSM hook (security_ptrace_access_check()). When mm is NULL, the dumpable branch is skipped entirely — and only the LSM hook decides the outcome. That happens when a process is in do_exit(): the kernel tears down the memory map (sets task->mm = NULL) before the process disappears entirely from the task list. In this narrow race window the dying process is still addressable by PID, but mm is already NULL — and the dumpable protection no longer applies.
The LSM hook (security_ptrace_access_check) is, under default YAMA configuration (ptrace_scope=1), the only remaining filter. When the attacker is the parent process of the dying privileged process (typically because they started the set-uid binary themselves), YAMA allows the ptrace without capability requirement. The door is open.
The second half: pidfd_getfd()
pidfd_getfd(2) (since v5.6-rc1, January 2020) is a modern syscall that lets a process import an open file descriptor of another process into its own FD table — gated by __ptrace_may_access(target, PTRACE_MODE_ATTACH_REALCREDS). When the gating function wrongly approves, the attacker can take any FD from the dying privileged process under their own UID — file handles to /etc/shadow, to SSH host keys, or authenticated dbus/socket connections to system services.
This turns the race window bug from a pure information disclosure into a full local privilege escalation. The dying process was root just a moment ago, the attacker takes over its I/O endpoints — the effect is root without the attacker ever having been root.
Four documented call chains (Qualys PoC)
Qualys built four working exploits. All four target established set-uid binaries or root daemons:
chage(set-uid-root or set-gid-shadow):chageopens/etc/shadowat start as shadow-group user. The attacker startschage, races againstdo_exit(), hijacks the/etc/shadowFD viapidfd_getfd(), reads the file. Effect: full/etc/shadowdisclosure from any unprivileged shell.ssh-keysign(set-uid-root): helper binary for SSH host authentication. Opens the SSH host private keys at/etc/ssh/*_keyat start. Same path — race, FD hijack, disclosure of all host private keys.pkexec(set-uid-root): PolicyKit helper. Has an open dbus authentication connection to the PolicyKit daemon. The attacker hijacks the dbus connection and can execute arbitrary commands as root — provided anallow_activeconsole session is present (for remote SSH-logged-in attackers that is a weaker entry hurdle than it first sounds, see the Pumpkin Chang trick in the detail section).accounts-daemon(root daemon): userspace daemon with a privileged dbus endpoint. The attacker hijacks the dbus connection and executes arbitrary commands as root. Exception: Ubuntu is not exploitable here (YAMAptrace_scope=1default plus attacker is not parent of the daemon).
Qualys is holding back the exact exploit code, but during the coordinated disclosure spotted an independently developed exploit derived from the public patch in circulation — hence the early full release.
Technical deep dive — the original code and the exact race mechanic
The following section follows the Qualys detail advisory and reproduces the original kernel code with the line numbers given there. If you want to trace the bug on your own codebase (for example for custom kernel builds or your own detection logic), the anchors are here.
Bug origin and patch commit
| Point | Value |
|---|---|
| Bug introduced | Linux kernel v4.10-rc1 (November 2016) via commit bfedb58 — “mm: Add a user_ns owner to mm_struct and fix ptrace permission checks” |
| Patch commit | Linux kernel mainline, commit 31e62c2 dated 14 May 2026, pushed by Linus Torvalds |
| Window of vulnerability | Nine years (Nov 2016 – May 2026) in every mainline-based Linux distribution kernel |
| Exploit precondition | pidfd_getfd() syscall present (introduced v5.6-rc1, January 2020) — every distro with a kernel line from 2020 onward is not just theoretically affected but has the full exploit stack |
The vulnerability: __ptrace_may_access() in the kernel (kernel/ptrace.c)
An unprivileged user who wants to call ptrace(), process_vm_readv(), process_vm_writev() or pidfd_getfd() against a target process has to pass two security checks in __ptrace_may_access():
276 static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
277 {
...
316 tcred = __task_cred(task);
317 if (uid_eq(caller_uid, tcred->euid) &&
318 uid_eq(caller_uid, tcred->suid) &&
319 uid_eq(caller_uid, tcred->uid) &&
320 gid_eq(caller_gid, tcred->egid) &&
321 gid_eq(caller_gid, tcred->sgid) &&
322 gid_eq(caller_gid, tcred->gid))
323 goto ok;
...
328 ok:
...
340 mm = task->mm;
341 if (mm &&
342 ((get_dumpable(mm) != SUID_DUMP_USER) &&
343 !ptrace_has_cap(mm->user_ns, mode)))
344 return -EPERM;
345
346 return security_ptrace_access_check(task, mode);
347 }
Check 1 (lines 317–322): the effective, saved and real UID/GID of the target process must match the caller's UID/GID. A set-uid binary that has called setreuid(ruid, ruid) and dropped its privileges satisfies this condition.
Check 2 (lines 341–342): the process's dumpable flag must be SUID_DUMP_USER (1). When a process changes its UID/GID, the kernel automatically sets dumpable to SUID_DUMP_DISABLE (0) — as protection against residual data (private keys, hashes) being extracted from the memory image.
The bug: when mm == NULL (line 341, the first conjunct of the if condition), the entire dumpable check is skipped and control falls through to the LSM hook in line 346. Under default YAMA (ptrace_scope=1) with the attacker as parent process, the LSM hook allows the ptrace — and the protection is gone, even though the process used to have root privileges.
When is mm == NULL? — the do_exit() race
The kernel sets task->mm = NULL in do_exit(), during the teardown of the dying process:
896 void __noreturn do_exit(long code)
897 {
...
964 exit_mm(); // task->mm = NULL is set here
...
971 exit_files(tsk); // task->files = NULL is set here
...
1019 do_task_dead();
1020 }
The race window sits between line 964 (exit_mm()) and line 971 (exit_files(tsk)) — seven lines of code in which task->mm is already NULL but task->files (the file-descriptor table) still exists. An attacker who pushes the target process into this time window and calls pidfd_getfd() in parallel can lift open file descriptors from the dying process into their own FD table.
pidfd_getfd() as the tooling path
The syscall path looks like this according to the Qualys advisory:
947 SYSCALL_DEFINE3(pidfd_getfd, int, pidfd, int, fd, unsigned int, flags)
948 {
...
964 return pidfd_getfd(pid, fd);
}
910 static int pidfd_getfd(struct pid *pid, int fd)
911 {
...
920 file = __pidfd_fget(task, fd);
}
872 static struct file *__pidfd_fget(struct task_struct *task, int fd)
873 {
...
881 if (ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS))
882 file = fget_task(task, fd);
}
1123 struct file *fget_task(struct task_struct *task, unsigned int fd)
1124 {
....
1128 if (task->files)
1129 file = __fget_files(task->files, fd, 0);
}
The gating function ptrace_may_access() in line 881 calls __ptrace_may_access() — the exact function with the bug. When the race takes, the function wrongly returns “access allowed”, and fget_task() in line 1128 still sees task->files as non-NULL (because that only nulls out seven lines later) and returns the file pointer. From the attacker's perspective: the privileged process's file descriptor lands in the attacker's FD table.
The concrete race dance
The exploit path in four steps (chage example):
- Attacker starts the set-uid target (e. g.
chage -l self). The target opens/etc/shadowas shadow-group user (line 776 in the chage source), then callssetregid(rgid, rgid)plussetreuid(ruid, ruid)(lines 778–779) to drop its privileges. - SIGKILL. Immediately after the privilege drop the attacker sends
SIGKILLto the chage process. The kernel starts thedo_exit()path. - Tight loop of
pidfd_getfd(). While chage moves throughexit_mm(), the attacker callspidfd_getfd(pidfd, target_fd)in a tight loop against chage's PID. The loop is designed for an effective hit rate — the race window is small, but with repeated tries practically always winnable. - FD taken. As soon as the race lands (
task->mm == NULL,task->files != NULL),pidfd_getfd()delivers the/etc/shadowFD into the attacker's FD table.read(fd)reads the file — as unprivileged user.
For ssh-keysign: identical pattern, but against the /etc/ssh/*_key FDs. Important: ssh-keysign opens the keys before the check on EnableSSHKeysign (which is disabled by default); the bug applies anyway.
For pkexec and accounts-daemon: same path, but the captured file descriptor is an already authenticated dbus system-bus connection (with SCM_CREDENTIALS as root). The attacker sends org.freedesktop.systemd1.Manager.StartTransientUnit to systemd (PID 1) over it and executes arbitrary commands as root.
Pumpkin Chang trick: allow_active bypass for pkexec
At first glance the pkexec exploit looks as if it required the attacker to physically sit at the console (PolicyKit's allow_active rule in org.gnome.settings-daemon.plugins.power.policy). Qualys cites Pumpkin Chang's blog post “dbus-and-polkit-introduction” (May 2025): if you start another process inside your own user session via systemd-run --user and run the exploit there, you can bypass the allow_active check. The attacker can therefore also be logged in via SSH, as long as a real console user exists in the same session (tty1 with a logged-in user). That makes the pkexec path practically much broader exploitable than the allow_active condition suggests.
Ubuntu exception for accounts-daemon
Qualys writes explicitly: “Ubuntu is notably not [exploitable for accounts-daemon] because it enables the Yama ptrace protection by default (it sets kernel.yama.ptrace_scope to 1)”. The logic: for accounts-daemon the attacker is not the parent — accounts-daemon runs as a PID-1-systemd-started daemon and is addressed by the attacker via a dbus request. YAMA ptrace_scope=1 permits ptrace only against child processes or against targets explicitly authorised via PR_SET_PTRACER. YAMA therefore blocks the accounts-daemon path on Ubuntu, but not the other three targets (chage, ssh-keysign, pkexec), where the attacker starts the target itself and is therefore parent.
Fedora SELinux exception — and the SetPassword workaround
On Fedora SELinux blocks the org.freedesktop.systemd1.Manager.StartTransientUnit path from accounts-daemon. Qualys has documented an alternative path: instead of starting a transient unit, the attacker sends an org.freedesktop.Accounts.User.SetPassword request through the stolen dbus connection. That way the attacker sets the password of a local admin user (e. g. john) — and then logs in via su -l john, then sudo -i to root. SELinux does not interfere here, because the request sits within the permitted accounts-daemon operations.
Disclosure timeline (Qualys original)
2026-05-11 Advisory + PoC sent privately to security@kernel
2026-05-14 Patch commit 31e62c2 by Linus Torvalds —
heads-up to private linux-distros@openwall
2026-05-15 Heads-up to public oss-security@openwall
(the day an independently developed exploit
from the public patch appeared in circulation
— the embargo break)
2026-05-20 Full advisory published
The period between 14 May (public patch) and 20 May (full advisory) is the window in which the distributions built their packages — and in which independent researchers could read the patch and reproduce the exploit. That is the usual friction between coordinated disclosure and the reality that public patches are always reverse-engineerable.
Who is affected?
Practically every Linux distribution with a kernel from v4.10 (November 2016) onward up to the patch versions from 14–21 May 2026. That covers the last nine years of enterprise Linux history — the historical exposure is considerable, even if public awareness only lands on the May 2026 dates.
Distribution table
Qualys explicitly verified the advisory against five default installations and writes about everything else: “other distributions may also be exploitable”. The table makes the distinction transparent:
| Distribution | Default configuration affected? | Patch status 21 May 2026 |
|---|---|---|
| Debian 13 (trixie) | ✓ verified by Qualys for all four exploits | Patched kernel via security repo, Qualys QID 6276533 / 6277424 |
| Ubuntu 24.04 LTS | ✓ verified by Qualys for chage, ssh-keysign, pkexec — accounts-daemon is not exploitable on Ubuntu because Ubuntu sets YAMA ptrace_scope=1 as default and the attacker is not parent of the daemon | Patched kernel via security pocket |
| Ubuntu 26.04 LTS | ✓ verified by Qualys for chage, ssh-keysign, pkexec — accounts-daemon exploit blocks as on 24.04 | Patched kernel via security pocket |
| Fedora 43 + 44 (Workstation) | ✓ verified by Qualys for all four exploits. For accounts-daemon SELinux blocks the StartTransientUnit path, but Qualys documented an alternative path via SetPassword → su → sudo | Patched kernel via Bodhi update FEDORA-2026-8b4a8d18d2 / -03be3dc34b |
| RHEL 8 + 9 | ✓ (logical inference: same kernel line, patch available) — not tested by Qualys against all four exploits | RHSA-2026:19521 / 19540 (Qualys QID 6050650 / 6050671) |
| AlmaLinux / Rocky 8 + 9 | ✓ (logical inference, RHEL mirror) | ALSA-2026:A008 / A009 / A010 (Qualys QID 944317–944320) |
| CloudLinux 8 + 9 | ✓ (logical inference, RHEL mirror) | CLSA-2026:A008 / A009 (Qualys QID 6683237 / 6683240) |
| SUSE Enterprise Linux | ✓ (logical inference, own kernel tree with the same function) | SUSE-SU-2026:1904-1 / 1907-1 / 1908-1 / 1909-1 (Qualys QID 762598–762618) |
| Debian 12 (bookworm), Ubuntu 22.04 LTS | ✓ likely (kernel lines ≥ v4.10) — not tested by Qualys | Patched kernel via security repo |
| Amazon Linux 2 / 2023, Oracle Linux, other RHEL derivatives | likely (kernel ≥ v4.10) — not explicitly mentioned by Qualys, own verification against vendor advisory recommended | Wait for vendor advisory / check the respective repo |
| Wolfi (Chainguard) as a container image | ✗ not directly — Wolfi is an “undistro” without its own kernel (Chainguard docs: “Wolfi does not currently build its own Linux kernel”). Containers use the host kernel, mitigation belongs on the host. Plus: typical Wolfi workload images do not have the Qualys PoC exploit targets installed by default | Host kernel patch — Wolfi image rebuild does not help here |
| Alpine as a container image | ✗ not directly — Alpine containers use the host kernel like any other container image | Host kernel patch — Alpine image rebuild does not help against kernel CVEs |
Which stack layers concretely?
- Container images:not directly patchable — containers use the host kernel. Protection sits on the host.
- Container hosts: bare-metal hosts, VMs, EC2 instances, Hetzner Cloud servers, Azure VMs — all affected, all patchable via the distribution.
- Kubernetes worker nodes: directly affected. Worker-node image refresh through the cluster.
- Managed Kubernetes (EKS/AKS/GKE): worker nodes run on provider AMIs/images. Patch via image-refresh rollout.
- Self-hosted CI/CD runners: particularly critical. Build runners are routinely used by untrusted build steps (e. g. pull-request builds from forks) — each such build is a potential local exploit vector.
- Shared multi-tenant hosts: where multiple customers have shell access on the same host (classical shared hosting, some container-hosting setups).
Who is not directly affected?
- Pure frontend apps without shell access: if the host never accepts unprivileged local users with shell login (hardened production server, only admin SSH, no shell for external users), the entry vector is missing. But: containers and CI runners with arbitrary build steps count as “unprivileged local shell” for kernel purposes.
- Hosts with
kernel.yama.ptrace_scope=2: this mitigation blocks thepidfd_getfd()vector (see mitigation section).
Impact
Local-only, but severe. The severity tag “local” is misleading if read as “low priority”. Concretely:
/etc/shadowdisclosure: all login password hashes of the system become readable. With modern GPU cracking toolchains (hashcat, John the Ripper), weak passwords break within minutes, medium ones within days.- SSH host key exfiltration: if an attacker leaks the SSH host private key, they can impersonate that host against any SSH client that already has the public key in
known_hosts. That opens MITM paths without further warning. - Root RCE via dbus hijack: full root access on the host. Subsequently: container breakout through direct access to
/proc/*/root, namespace escape, persistence mechanisms. - Container multi-tenant impact: on a container host with multiple tenants, a single compromised pod instance is enough to take over the entire host — and with it all other pods on that host.
The line between “unprivileged foothold” and “full host compromise” collapses. A phished developer account, a slipped CI build, a low-privilege service account or a multi-tenant host all turn into direct paths to root.
We were not hit
None of the Moselwal customers' container hosts or Kubernetes worker nodes had a compromising exploitation trace in the audit logs as of 21 May 2026, 12:00 UTC. That is not a flex — it is the expected output of a continuous-upgrade pipeline that rolls distribution patches within hours of availability. The real stress test comes with the next 0-day wave, when the patch is not yet available.
Mitigation and immediate measures
Three paths, in order of cleanliness:
Path 1 — deploy the distribution kernel patch
The clean fix. Distribution updates have been available since 20/21 May.
# Debian / Ubuntu
sudo apt update && sudo apt upgrade linux-image-generic linux-image-amd64
# Fedora / RHEL / AlmaLinux / Rocky / CloudLinux
sudo dnf update kernel kernel-core kernel-modules
# SUSE
sudo zypper update kernel-default
# Then in every case
sudo reboot
After reboot verify: uname -r against the distribution patch list.
Path 2 — interim mitigation: kernel.yama.ptrace_scope=2
If the patch is not immediately possible (a maintenance window is pending, reboot is blocked), this sysctl blocks the public exploit path right away:
# Effective immediately, persistent from next reboot
echo 'kernel.yama.ptrace_scope = 2' | sudo tee /etc/sysctl.d/99-yama-ptrace.conf
sudo sysctl -p /etc/sysctl.d/99-yama-ptrace.conf
Mechanism: pidfd_getfd(2) is gated via __ptrace_may_access(target, PTRACE_MODE_ATTACH_REALCREDS). At ptrace_scope=1 (default), the YAMA LSM hook permits ptrace against child processes — including against the self-started set-uid binary. At ptrace_scope=2, YAMA explicitly requires CAP_SYS_PTRACE which an unprivileged attacker does not have. pidfd_getfd() therefore returns -EPERM, irrespective of the kernel race in the dumpable branch.
Operational impact of this mitigation:
- Non-root users can no longer use
gdb -p,strace -p,perf record -pagainst processes they have not authorised themselves viaPR_SET_PTRACER - Browser crash-reporter sandboxes (Firefox, Chromium) may lose a debugging path — both handle that gracefully
- Container debug functionality,
kdumpuserspace helpers, CRIU (Checkpoint and Restore in Userspace) may break ptrace_scopeis monotonically non-decreasing: once raised, it cannot be lowered at runtime. Only a reboot resets the value.
On production container hosts without active debug workflow, ptrace_scope=2 is in our view a sensible default-hardening measure beyond the acute gap.
Path 3 — credential rotation on exposed hosts
If a host has accepted untrusted local users during the exposure window, SSH host keys and locally cached credentials should be treated as potentially disclosed:
# Rotate SSH host keys
sudo rm /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub
sudo ssh-keygen -A
sudo systemctl restart sshd
# Invalidate known_hosts entries on the client side
# (Every machine that SSHs into the rotated host has to
# accept the new host keys — that is an operations wave)
For /etc/shadow: enforce a password reset for all local accounts with non-trivial passwords at the next login. With external auth sources (LDAP, SSSD, Active Directory) /etc/shadow is less critical.
Detection and verification
Which kernel version is running?
uname -r
# Compare against the stable kernel line table below,
# or against the distribution patch list
Greg Kroah-Hartman released seven patched stable kernel lines at once (source: LWN, 17 May 2026). Your uname -r output should be equal to or greater than one of these versions:
| Stable line | First patched version | Application |
|---|---|---|
| 7.x | 7.0.8 | Current bleeding-edge kernel (mainline) |
| 6.18 | 6.18.31 | Current stable line |
| 6.12 LTS | 6.12.89 | Current LTS kernel |
| 6.6 LTS | 6.6.139 | LTS kernel (Ubuntu 24.04 base) |
| 6.1 LTS | 6.1.173 | LTS kernel (Debian 12 / 13 base) |
| 5.15 LTS | 5.15.207 | LTS kernel (Ubuntu 22.04 base) |
| 5.10 LTS | 5.10.256 | Oldest active LTS kernel (Debian 11 base) |
Example: a host on 6.6.135-1-debian is not patched (135 < 139). A host on 6.6.139-1-debian or higher is patched. Distributions often add their own suffix strings (-1-debian, -generic, -aws), but the numeric patch component is the comparison anchor.
What is the current ptrace_scope?
sysctl kernel.yama.ptrace_scope
# 0 = ptrace allowed everywhere (worst case)
# 1 = only against child processes (default on Ubuntu/Debian, exploitable)
# 2 = only with CAP_SYS_PTRACE (mitigation level for CVE-2026-46333)
# 3 = ptrace fully disabled (rare, very restrictive)
eBPF / Tetragon detection on pidfd_getfd() calls
If you have Tetragon (Cilium) or eBPF-based monitoring in the stack, the pidfd_getfd syscall can be observed for anomalies pauschal. The following tracing policy is a sketch suggestion based on the Qualys mechanic — not production-verified against the exploit, because Qualys held back the PoC code. Take it into your own validation environment before deploying and reconcile it against the active legitimate caller patterns:
# tetragon-policy.yaml — sketch, validate before production use
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: observe-pidfd-getfd
spec:
kprobes:
- call: "__x64_sys_pidfd_getfd"
syscall: true
args:
- index: 0
type: "int" # pidfd
- index: 1
type: "int" # targetfd
selectors:
- matchActions:
- action: Post
rateLimit: "5"
In the telemetry pipeline output, pidfd_getfd calls appear as individual events. Legitimate callers in a typical Linux stack: containerd, systemd, dbus-broker, crun/runc, possibly criu. Anomalies are unprivileged callers applying pidfd_getfd to a process with a different UID — the pattern that CVE-2026-46333 exploits have to show.
Falco rule as an alternative
Also a sketch suggestion that mirrors the Qualys mechanic but is not verified against the real PoC. proc.is_suid_descendant is a prerequisite macro that needs to be maintained in standard Falco rule sets:
# Sketch — verify against your own Falco macro set before production use
- rule: Suspicious pidfd_getfd against suid binary
desc: Unprivileged process calling pidfd_getfd against a suid target
condition: >
evt.type = pidfd_getfd and
evt.dir = > and
not user.uid = 0 and
proc.is_suid_descendant = true
output: >
Possible CVE-2026-46333 exploit attempt
(user=%user.name uid=%user.uid command=%proc.cmdline target_pid=%evt.arg.pid)
priority: CRITICAL
tags: [process, kernel, ptrace, cve-2026-46333]
Audit subsystem as a lightweight variant
# /etc/audit/rules.d/cve-2026-46333.rules
-a always,exit -F arch=b64 -S pidfd_getfd -k cve-2026-46333-pidfd-getfd
# Reload and check
sudo augenrules --load
sudo auditctl -l | grep cve-2026-46333
# Alerts in /var/log/audit/audit.log with key=cve-2026-46333-pidfd-getfdOperator recommendation — operational decision block
| Question | Answer → Action |
|---|---|
| Do I run a Linux container host (any distro)? | Yes → deploy the distribution kernel patch and reboot, or use live kernel patching (kpatch/kgraft) — within 24 h. |
| Do I have Kubernetes worker nodes with mixed tenants? | Yes → pull a worker-node image refresh through the cluster, evict pods gracefully. On managed Kubernetes: trigger the AMI/image refresh rollout. |
| Do I have self-hosted CI runners that execute pull-request builds from forks? | Yes → highest priority. Apply the patch AND set kernel.yama.ptrace_scope=2, because PR builds from forks count by definition as untrusted local code. |
| Patch is not immediately possible (maintenance window pending)? | Set kernel.yama.ptrace_scope=2 immediately as interim mitigation. Apply the patch within 72 h. |
| Do I have shared multi-tenant hosts with shell access for customers? | Highest priority. Patch within 12 h plus credential rotation. |
| I use Renovate-driven containers and I'm a Moselwal customer? | No action needed — the update has been moving through the pipeline since overnight. |
Recommendation per setup type
German Mittelstand — single-site container hosting on a bare-metal or VM host. Apply the distribution update, schedule a reboot. On a host without active untrusted local users (admin SSH only, no shell for external users), the acute exploit hurdle is higher, but long-term hygiene demands the patch anyway.
Enterprise — multi-host container cluster. Rolling image rebuild plus worker-node recycling. On production hosts additionally kernel.yama.ptrace_scope=2 as default baseline in the container-host hardening line. In the same maintenance window rotate SSH host keys if the host has accepted shell access from external parties during the nine-year exposure phase.
Kubernetes — managed or self-hosted. Worker-node image refresh is the primary mitigation path. On managed Kubernetes (EKS, AKS, GKE) schedule the provider image refresh in the next node-pool rotation. On self-hosted Karpenter / cluster-autoscaler setups, pin the AMI reference in the NodeClass manifest to the patched version and trigger a drain-and-replace cycle.
Declarative stacks (NixOS, Talos, Flatcar). Image rebuild via the respective channel update.
What we did at Moselwal
Since the Qualys advisory of 20 May 2026 afternoon, the standard kernel patch wave has been running at Moselwal, with the following concrete steps:
Host kernel patches rolled out on the container hosts. The decisive step for this CVE is not the container image rebuild — Wolfi (FrankenPHP workers), Debian-slim (TYPO3/Sylius application) and Alpine (auxiliary workers) are all container images without their own kernel and as userland are not directly affected by the gap. What we had to patch was the underlying host kernel of the container hosts and Kubernetes worker nodes (Debian/Ubuntu LTS under the containers, AKS/EKS provider AMIs on the managed cluster workers). As soon as the distribution published the patch in the security repo, Renovate (for the self-managed hosts) or the cluster operator pipeline (for the managed Kubernetes worker pools) started the host-node replacement wave. Container image rebuilds ran separately for hygiene, without being required for the acute CVE mitigation.
Rolling restart on customer containers, prioritised by exposure. Container hosts with active CI-runner workloads (pull-request builds from forks, highest exposure) were restarted first. Production hosts without untrusted shell access in the normal rolling window. The FrankenPHP worker restarts are graceful, invisible to end users.
kernel.yama.ptrace_scope=2 as baseline hardening. We have set this sysctl in our container-host image baseline — not just as CVE mitigation but as a long-term default hardening line. Applications that need ptrace operations (in our stack: none) have to opt in explicitly via PR_SET_PTRACER. That is a stack-architecture decision we have pulled forward with this CVE wave.
Audit pass over the inventory. A script runs over all customer hosts: which kernel version, which ptrace_scope value, which set-uid binaries exist, which dbus system services are running. The result feeds the internal audit log and the next maintenance routine.
None of our customers had a compromising trace in the audit or application logs as of 21 May 2026, 12:00 UTC.
Frequently asked questions about CVE-2026-46333
Do Kubernetes containers need to be rebuilt because of CVE-2026-46333?+
Container images themselves are not affected — containers use the host kernel. What you need is a worker-node image refresh that brings the patched host kernel along. The container images themselves can stay unchanged, they simply run on a host with a new kernel version. Eviction of pods plus drain-and-replace of the worker nodes is the standard path.
How do I check whether the mitigation on my host is actually working?+
Three steps: first compare uname -r against the distribution patch list; second check sysctl kernel.yama.ptrace_scope (should be 2 for the mitigation line); third — if you want to validate it — simulate the PoC (which Qualys has held back publicly) with a test script that calls pidfd_getfd() against a set-uid test process. A patched or ptrace_scope=2 system should return -EPERM.
Are EC2, Hetzner Cloud and Azure VMs automatically protected against CVE-2026-46333?+
No. EC2 AMIs, Hetzner Cloud images and Azure VHDs are not magically protected — they run the same Linux kernel as bare-metal hosts. The respective providers have new image versions with the patch in the marketplace since the weekend. If you version images via Packer/Terraform, pin them to the new image IDs. For manually-started instances an apt/dnf upgrade plus reboot is enough.
What concretely changes for our existing customers because of CVE-2026-46333?+
For Moselwal-operated platforms: nothing visible. The kernel patches have moved through the continuous-upgrade pipeline, the worker restarts are graceful, the service state remains. What is changing in our stack baseline in the medium term: kernel.yama.ptrace_scope=2 is now a default value in the host images — a hardening step we have pulled forward with this CVE wave.
Why does CVE-2026-46333 follow Copy Fail and Dirty Frag — is there a structural pattern?+
Structurally similar but technically different. All three are kernel LPE bugs, but in different subsystems (AF_ALG crypto for Copy Fail, IPsec/RxRPC for Dirty Frag, ptrace for CVE-2026-46333) and with different bug classes (memory corruption for Copy Fail, race for Dirty Frag, logic bypass for CVE-2026-46333). What makes the cluster observable is not a shared root but a pattern on the researcher side: Qualys TRU has been working systematically against Linux kernel privilege boundaries in recent weeks, and independent researchers have become active in the same areas in parallel. The pragmatic operator takeaway: if you have not yet shifted your patch pipeline to a weekly cadence, do it now — the coming weeks will likely bring further kernel-LPE disclosures.
Does kernel.yama.ptrace_scope=2 work against all classes of ptrace bugs?+
No. This sysctl specifically protects against the pidfd_getfd() path in CVE-2026-46333 (and against some related classes in which YAMA is the final gatekeeper). It is not a silver bullet against all future ptrace class bugs — a kernel bug that bypasses the LSM hook or triggers before it would also slip past ptrace_scope=2. Plus the mitigation costs operability (see operational-impact points above). It is a sensible default baseline, not a patch replacement.
Conclusion
CVE-2026-46333 is a severe-grade kernel LPE bug — nine years old, in practically every Linux stack of the last half decade, with publicly circulating exploits, with four documented call chains to root and to sensitive credentials. The race pattern in the __ptrace_may_access() path is not a single typo — it is a logical gap that only became a full LPE chain via the later pidfd_getfd() (2020). Such “dormant” bugs are the honest candidates for future research sweeps: every new syscall that gates access decisions through __ptrace_may_access() has the potential to reactivate old race windows.
For Moselwal customers with Renovate / continuous-upgrade pipelines, the distribution kernel patches have been through since overnight. For self-hosted setups without an auto-update pipeline, the rule is: kernel patch and reboot within 24 hours, or at least kernel.yama.ptrace_scope=2 as interim mitigation in the next hour. Anyone running self-hosted CI runners for pull-request builds from forks is in the highest priority tier — this class of build workloads is exactly the scenario that turns the gap into a mass threat.
Beyond the acute wave: ptrace_scope=2 is, in our view, the better default for container host baselines. Production hosts do not need an open ptrace layer for unprivileged users. Anyone who has not yet introduced that into their hardening standards can use this CVE wave as the trigger — the operational costs are manageable, the protection extends beyond CVE-2026-46333.
The interesting question is not whether further kernel LPE bugs will surface in the coming weeks — they will. The question is whether your own patch pipeline is set up such that the next distribution kernel update runs through overnight without anyone having to wake up to it.
This post reflects our technical and strategic assessment. It does not replace your own code review or a data protection impact assessment if SSH host keys or credentials have to be rotated.
Keep your container hosts kernel-patch-ready?
If you run Linux container hosts, Kubernetes worker nodes or CI runners yourself and find yourself dragged into a manual update shift every time a kernel-LPE wave like today's hits, get in touch. We set up Renovate on your stack, define the auto-merge policy by severity, automate image rebuild plus worker restart, and integrate Tetragon/Falco detection hooks for kernel privilege-escalation attempts — so that next time you only get an email, not a night shift.
We check, mitigate and validate live Linux platforms. SBOM inventory, Renovate setup, kernel live-patch strategies, worker-node refresh pipelines, ptrace-scope baseline hardening. Platform operations, not consulting-on-paper.

![[Translate to English:] Foto von Kai Ole Hartwig.](/fileadmin/_processed_/e/9/csm_ole-neu_73323ad80d.jpeg)
![[Translate to English:] Eine präzise Reihe gleichartiger Kraftpapier-Pakete auf Beton, zwischen zwei Paketen entweicht ein dünner oxbloodfarbener Staubfaden nach oben; daneben Messingstempel und Lupe im kühlen Nordlicht.](/fileadmin/_processed_/b/7/csm_80c054b74d4e3192c9fd48e5d9456e1fea17ed40a306c4640fd659046bf50e9a_9681f71cee.jpg)