Researcher Aaron Esau and the V12 Security team disclosed PinTheft, a Linux kernel local privilege escalation that chains an RDS zerocopy reference-count bug with io_uring fixed buffers to overwrite the page cache of a SUID-root binary. A public proof-of-concept is available. Any unprivileged local user on an affected host can use it to gain root.
This is the fifth Linux kernel privilege escalation we've written about in three weeks. We know the cadence is wearing on you. We keep moving fast on every disclosure to address them because we deeply care about our customers, and for PinTheft, the answer is good news. We tested the PoC across every CloudLinux platform: CL7, CL7h, CL8, CL9, CL10, and CloudLinux for Ubuntu 22.04 — and we confirmed that they are not affected. No patched kernel, and no emergency action is required.
The bug lives in the kernel's RDS (Reliable Datagram Sockets) zerocopy send path. rds_message_zcopy_from_user() pins user pages one at a time; if a later page faults, the error path drops the pages it already pinned, but the scatterlist entries persist after the zcopy notifier is cleared. RDS message cleanup later drops them again. A double decrement of the page's FOLL_PIN reference count per send. The published PoC weaponizes that refcount corruption into a page-cache overwrite by registering an anonymous page as an io_uring fixed buffer, draining its pin references through failing RDS zerocopy sends, freeing the page so it gets reclaimed as page cache for a readable SUID-root binary, and writing an ELF stub into that page cache via the still-live fixed-buffer entry. The next invocation of the targeted SUID binary runs the stub as root.
The chain is narrow. Exploitation requires all of:
Missing any one of these breaks the chain. The V12 PoC author notes the practical reach themselves:
"The RDS kernel module this requires is only default on Arch Linux among the common distributions we tested."
Details: public PoC · oss-security disclosure · upstream RDS fix on netdev · NVD entry — pending CVE assignment.
| Version | Stock (Default) | Reason |
|---|---|---|
| CloudLinux OS 7 (CL7) | ✅ Not affected |
# CONFIG_RDS is not set — RDS subsystem not compiled in. Kernel pre-dates io_uring. |
| CloudLinux OS 7h (CL7h) | ✅ Not affected |
# CONFIG_RDS is not set — RDS subsystem not compiled in. |
| CloudLinux OS 8 (CL8) | ✅ Not affected |
# CONFIG_RDS is not set — RDS subsystem not compiled in. |
| CloudLinux OS 9 (CL9) | ✅ Not affected |
# CONFIG_RDS is not set, and kernel.io_uring_disabled=2 (io_uring disabled by default). Double layer of protection. |
| CloudLinux OS 10 (CL10) | ✅ Not affected |
# CONFIG_RDS is not set, and kernel.io_uring_disabled=2 (io_uring disabled by default). Public PoC source also does not compile against the CL10 kernel headers. |
| CloudLinux for Ubuntu 22.04 LTS | ✅ Not affected by the public PoC |
Runs on the stock Ubuntu 22.04 kernel (5.15). The public PoC requires kernel 6.13+ APIs that do not exist on 5.15, and Ubuntu's kernel default blocks rds.ko autoload by unprivileged callers. See note below. |
Every CloudLinux OS kernel from CL7 through CL10 ships with # CONFIG_RDS is not set in its kernel configuration. The RDS protocol subsystem that PinTheft targets is not compiled into the kernel at all, the rds and rds_tcp modules are not present on disk, and socket(AF_RDS, …) returns EAFNOSUPPORT immediately. There is no code path to reach the bug.
CL9 and CL10 additionally ship with kernel.io_uring_disabled=2, which fully disables the io_uring interface that PinTheft uses to weaponize the RDS refcount bug. Even if a custom kernel re-enabled CONFIG_RDS, this second gate would still hold.
CloudLinux for Ubuntu 22.04 runs on the stock Ubuntu 22.04 kernel with the full CloudLinux userspace stack on top. The Ubuntu kernel does build CONFIG_RDS=m and CONFIG_RDS_TCP=m, so the kernel-config prerequisite is technically present. Two non-CloudLinux gates prevent the public PoC from working today:
The public PoC source does not compile against Ubuntu 22.04's 5.15 kernel headers. It requires IORING_REGISTER_CLONE_BUFFERS and the finalized struct io_uring_clone_buffers shape (with field nr), which only landed in mainline kernel 6.13. On 5.15 the symbols simply do not exist:
poc.c:344:35: error: storage size of 'arg' isn't known
struct io_uring_clone_buffers arg;
poc.c:350:23: error: 'IORING_REGISTER_CLONE_BUFFERS' undeclared
rds.ko autoload is denied for unprivileged callers by the Ubuntu kernel default. We verified that socket(AF_RDS, SOCK_SEQPACKET, 0) returns EAFNOSUPPORT from a CageFS-confined unprivileged user, both before and after rmmod rds_tcp rds. Loading RDS requires CAP_SYS_MODULE or CAP_NET_ADMIN, which a hosting tenant does not have.
Both of those gates can erode over time. An administrator or service can load rds.ko explicitly, and Ubuntu HWE will eventually move to kernels ≥ 6.13. If you want defense in depth today, the optional mitigation below removes the kernel-config gate entirely.
If you would prefer not to depend on Ubuntu's autoload behavior and want to keep the path closed even on future HWE kernels, blacklist the RDS modules so they cannot be loaded:
sudo sh -c "printf 'install rds /bin/false\ninstall rds_tcp /bin/false\ninstall rds_rdma /bin/false\n' > /etc/modprobe.d/pintheft.conf; rmmod rds_tcp 2>/dev/null; rmmod rds_rdma 2>/dev/null; rmmod rds 2>/dev/null; true"
To revert later:
sudo rm /etc/modprobe.d/pintheft.conf
Compatibility: RDS is the kernel's Reliable Datagram Sockets transport, used almost exclusively in HPC clusters and Oracle RAC deployments, not by typical web-hosting, shared-hosting, or general-purpose workloads. Do not apply this on hosts that genuinely depend on RDS.
If you would like to confirm the status on a specific host, run:
grep -E 'CONFIG_(RDS|RDS_TCP|IO_URING)' /boot/config-$(uname -r) 2>/dev/null
cat /proc/sys/kernel/io_uring_disabled 2>/dev/null
modprobe -n -v rds_tcp 2>&1 | head -3
If CONFIG_RDS is # not set, or rds_tcp returns "Module not found", the host cannot reach the bug.