31 min read
High
By

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.

Notar-Petschaft mit halbflüssigem oxblutroten Wachs auf cremefarbenem Aktendokument, eine brass-and-walnut Pinzette zieht ein eingebettetes Messing-Schlüsselrohling aus dem noch weichen Wachskern — Metapher für das Race-Window beim Kernel-ptrace-Pfad CVE-2026-46333, in dem ein sterbender privilegierter Prozess seine Filedeskriptoren noch hergibt, bevor das Siegel vollständig erkaltet.
AI-generated · gpt-image 2.0

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 later pidfd_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 the dumpable flag does not gate when mm is NULL. The attacker uses pidfd_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=2 persistent in /etc/sysctl.d/. It requires CAP_SYS_PTRACE for ptrace operations and blocks the public exploits because the pidfd_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

FieldValue
CVECVE-2026-46333
CWECWE-362 (Race Condition) + CWE-269 (Improper Privilege Management) + CWE-200 (Information Exposure)
ComponentLinux kernel: kernel/ptrace.c, function __ptrace_may_access(); syscall pidfd_getfd(2)
Vulnerability typeLogic flaw / TOCTOU race against do_exit() plus privilege escalation via file-descriptor hijack
Affected versionsLinux kernel since v4.10-rc1 (November 2016) up to the respective distribution patch versions from 20/21 May 2026
Fixed inUpstream 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
SeverityHigh — 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
CreditsSaeed 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:

  1. chage (set-uid-root or set-gid-shadow):chage opens /etc/shadow at start as shadow-group user. The attacker starts chage, races against do_exit(), hijacks the /etc/shadow FD via pidfd_getfd(), reads the file. Effect: full /etc/shadow disclosure from any unprivileged shell.
  2. ssh-keysign (set-uid-root): helper binary for SSH host authentication. Opens the SSH host private keys at /etc/ssh/*_key at start. Same path — race, FD hijack, disclosure of all host private keys.
  3. 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 an allow_active console 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).
  4. 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 (YAMA ptrace_scope=1 default 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

PointValue
Bug introducedLinux kernel v4.10-rc1 (November 2016) via commit bfedb58 — “mm: Add a user_ns owner to mm_struct and fix ptrace permission checks”
Patch commitLinux kernel mainline, commit 31e62c2 dated 14 May 2026, pushed by Linus Torvalds
Window of vulnerabilityNine years (Nov 2016 – May 2026) in every mainline-based Linux distribution kernel
Exploit preconditionpidfd_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):

  1. Attacker starts the set-uid target (e. g. chage -l self). The target opens /etc/shadow as shadow-group user (line 776 in the chage source), then calls setregid(rgid, rgid) plus setreuid(ruid, ruid) (lines 778–779) to drop its privileges.
  2. SIGKILL. Immediately after the privilege drop the attacker sends SIGKILL to the chage process. The kernel starts the do_exit() path.
  3. Tight loop of pidfd_getfd(). While chage moves through exit_mm(), the attacker calls pidfd_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.
  4. FD taken. As soon as the race lands (task->mm == NULL, task->files != NULL), pidfd_getfd() delivers the /etc/shadow FD 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:

DistributionDefault configuration affected?Patch status 21 May 2026
Debian 13 (trixie)verified by Qualys for all four exploitsPatched kernel via security repo, Qualys QID 6276533 / 6277424
Ubuntu 24.04 LTSverified 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 daemonPatched kernel via security pocket
Ubuntu 26.04 LTSverified by Qualys for chage, ssh-keysign, pkexec — accounts-daemon exploit blocks as on 24.04Patched 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 SetPasswordsusudoPatched 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 exploitsRHSA-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 QualysPatched kernel via security repo
Amazon Linux 2 / 2023, Oracle Linux, other RHEL derivativeslikely (kernel ≥ v4.10) — not explicitly mentioned by Qualys, own verification against vendor advisory recommendedWait 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 defaultHost 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 imageHost kernel patch — Alpine image rebuild does not help against kernel CVEs

Which stack layers concretely?

Who is not directly affected?

Impact

Local-only, but severe. The severity tag “local” is misleading if read as “low priority”. Concretely:

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:

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 lineFirst patched versionApplication
7.x7.0.8Current bleeding-edge kernel (mainline)
6.186.18.31Current stable line
6.12 LTS6.12.89Current LTS kernel
6.6 LTS6.6.139LTS kernel (Ubuntu 24.04 base)
6.1 LTS6.1.173LTS kernel (Debian 12 / 13 base)
5.15 LTS5.15.207LTS kernel (Ubuntu 22.04 base)
5.10 LTS5.10.256Oldest 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-getfd

Operator recommendation — operational decision block

QuestionAnswer → 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.

Next step

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.

Discuss container-host hardening

About the author

[Translate to English:] Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Founder · Moselwal Digitalagentur · OnlyOle

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012, now Moselwal. Over 100 projects, with a focus on security, performance, automation and quality.