The Ol' Switcheroo: How a Typos in io_uring Nuked Kernel Queues
Jan 15, 2026·5 min read
Executive Summary (TL;DR)
In io_uring network commands, a call to skb_queue_splice() had its arguments transposed. This caused the permanent socket error queue to be moved into a temporary on-stack list that gets destroyed when the function returns. The result is data loss, state corruption, and a high probability of a kernel crash (DoS) triggered by a local user.
A classic logic error in the Linux kernel's io_uring subsystem where a developer confused the source and destination arguments in a list-splicing function. Instead of saving temporary data to the socket queue, the kernel accidentally emptied the socket's critical error queue into a temporary variable destined for oblivion.
The Hook: io_uring Strikes Again
Ah, io_uring. The Linux kernel's high-performance asynchronous I/O interface that keeps on giving. It’s fast, it’s complex, and it’s become a favorite hunting ground for vulnerability researchers. If you want speed, you use io_uring. If you want job security as an exploit dev, you also use io_uring.
This specific vulnerability (CVE-2025-68234) hides in the cmd_net implementation—essentially the part of io_uring handling advanced network commands. Specifically, it involves the handling of packet timestamps (SO_TIMESTAMPING).
When you're doing high-precision networking, knowing exactly when a packet hit the wire is crucial. The kernel supports this by queuing timestamp notifications back to the user. But in this case, the mechanism to retry retrieving those timestamps had a fatal flaw. It wasn't a buffer overflow, and it wasn't a race condition. It was something far simpler and more embarrassing: a developer got their left and right mixed up.
The Flaw: A Tale of Two Lists
To understand the bug, you have to understand how the kernel manages lists of socket buffers (SKBs). The kernel frequently moves packets between queues. The function skb_queue_splice(list, head) is designed to take all elements from the list (source) and append them to head (destination).
In the vulnerable code path, the kernel has a temporary local list (on the stack) containing some SKBs that need to be put back into the socket's persistent error queue (sk->sk_error_queue).
The logic should have been: "Take the stuff from my temporary list and put it safely into the socket."
Instead, the code did this:
skb_queue_splice(&sk->sk_error_queue, &list);
See the problem? They passed the socket's permanent queue as the source and the temporary stack list as the destination.
This is the programming equivalent of trying to pour a glass of water into the ocean, but accidentally pouring the entire ocean into your glass. The physics of the kernel (and reality) don't like that.
The Code: The Smoking Gun
Let's look at the diff. It is painfully simple. This single line caused the entire vulnerability.
In io_uring/net.c, inside the retry loop for timestamp retrieval, the code attempts to clean up:
// VULNERABLE CODE (Before)
// 'list' is a temporary struct sk_buff_head on the stack.
// 'sk->sk_error_queue' is the persistent socket queue.
skb_queue_splice(&sk->sk_error_queue, &list); Because skb_queue_splice empties the first argument into the second, this instruction rips every existing error packet out of the socket and dumps it into list.
When the function eventually returns, list goes out of scope. Depending on how the cleanup code handles list, those packets are either leaked into the void (memory leak) or freed while the socket might still think it owns them (state corruption).
Here is the fix in commit c85d2cfc5e24e6866b56c7253fd4e1c7db35986c:
// PATCHED CODE (After)
// Now we correctly move the temporary items back to the socket.
skb_queue_splice(&list, &sk->sk_error_queue);One comma, two arguments swapped. That's the difference between a stable system and a kernel panic.
The Exploit: Crashing the Party
Exploiting this isn't about gaining root immediately; it's about causing chaos (Denial of Service). A local user with low privileges can trigger this because io_uring is generally available to users.
Here is the attack strategy:
- Setup: Create a socket and enable
SO_TIMESTAMPING. We need the kernel to generate error queue entries (timestamps) for us. - Fill the Queue: Send enough traffic to populate
sk->sk_error_queuewith timestamp notifications. - Trigger the Bug: Issue an
io_uringnetwork command that hits the retry path incmd_net. This is the tricky part—you need to induce a condition where the kernel decides to splice the lists. Usually, this happens during error handling or retries. - The Crash: When the bug triggers, the socket's queue is emptied.
If the kernel subsequently tries to access those packets via the socket (expecting them to be there), it hits a NULL pointer or a dangling reference. Alternatively, if the stack variable list destroys the SKBs when it goes out of scope, but the socket logic still tracks the count of packets it thinks it has, we get a beautiful state inconsistency that usually ends in a panic.
Researcher Note: While currently classified mainly as a DoS, whenever you have "unaccounted for freeing of objects" (if the local list cleanup frees them), you are technically flirting with Use-After-Free (UAF) scenarios if you can race another thread to access that socket queue before the cleanup finishes.
The Fix: RTFM (Read The Function Manual)
The mitigation is straightforward: Apply the patch. The Linux kernel team fixed this in 6.18 and backported it to 6.17.11.
If you are stuck on a vulnerable kernel (6.17 - 6.17.10) and cannot reboot, your options are limited. You could try to block io_uring access entirely using sysctl, which effectively kills the attack vector at the cost of performance for applications that rely on it:
sysctl -w kernel.io_uring_disabled=1But really, just patch your kernel. This is a logic bug in a core subsystem; it's not going away with a firewall rule.
Official Patches
Fix Analysis (2)
Technical Appendix
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Linux Kernel Linux | >= 6.17, < 6.17.11 | 6.17.11 |
| Attribute | Detail |
|---|---|
| Attack Vector | Local |
| CVSS v3.1 | 6.2 (Medium) |
| Impact | Denial of Service (System Crash) |
| Vulnerability Type | Logic Error (Argument Transposition) |
| Affected Subsystem | io_uring / net |
| EPSS Score | 0.00024 |
MITRE ATT&CK Mapping
The product calls a function, procedure, or routine, but the caller specifies the arguments in an incorrect order, leading to incorrect behavior or security vulnerabilities.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.