The Linux kernel is at the heart of countless systems—servers, desktops, phones, and much more. As with any complex software, bugs occasionally slip through, even in high-profile features like io_uring, the fast and flexible async I/O interface. One such vulnerability, CVE-2021-47040, was found in the io_uring implementation. In this article, we’ll break down what happened, how the bug could be exploited, and the fixes that patched it up, using simple and direct language.
What is CVE-2021-47040?
CVE-2021-47040 is a vulnerability in the Linux kernel's io_uring subsystem, namely how it handles provided buffers with the io_provide_buffers_prep function. Due to improper checks on integer operations, attackers could potentially trigger overflows, which may lead to memory corruption or privilege escalation.
Reference:
- CVE-2021-47040 on NIST NVD
- Fix commit d81269fecb8ce (git.kernel.org)
io_uring and Provided Buffers
io_uring speeds up I/O (input/output) in Linux by letting applications submit many operations simultaneously. Sometimes, apps can "provide" their own buffers that the kernel should fill up. That’s where io_provide_buffers_prep() comes in.
The Bug: Improper Overflow Checking
When the kernel receives buffer data from a user, it needs to calculate how much memory to use and where to put it. If those calculations aren't safely checked, an attacker could use very large or negative values to trick the system, causing integer overflows or sign extension issues (where a negative number becomes a huge positive one due to signed/unsigned type confusion).
Colin and others noticed these checks were either missing or ineffective, making it possible to supply specially-crafted data to break the kernel’s assumptions.
The buggy code was in io_provide_buffers_prep. Look at this simplified version
struct io_provide_buf {
__aligned_u64 addr;
__aligned_u32 len;
__aligned_u16 bgid;
__aligned_u16 nbufs;
__aligned_u64 resv;
};
...
static int io_provide_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) {
struct io_provide_buf *p = req->sqe->buf;
if ((p->len * p->nbufs) > MAX_ALLOWED) {
// should reject, but checks can be bypassed
}
// code continues...
}
Exploitation: How Could an Attacker Use It?
If an attacker provides a negative len (which appears large if interpreted as unsigned), or a huge nbufs value, they can make internal calculations wrap around—making the kernel allocate or copy memory to the wrong place. This might lead to:
This is only a *conceptual* outline, not a working exploit. But here's an idea in C-like pseudocode
struct io_provide_buf payload = {
.addr = user_buffer,
.len = -1, // negative length, when seen as unsigned, is huge!
.nbufs = 16, // moderate count
... // rest
};
// User submits this via io_uring
If the kernel isn't properly checking for overflows or negative values, the math can go haywire, compromising kernel memory.
The fix came in two parts
1. Added proper overflow checking: The kernel now uses helper functions (check_mul_overflow, check_add_overflow) to safely perform arithmetic.
Patch Example
// Before:
struct io_provide_buf {
__aligned_u64 addr;
__aligned_u32 len; // signed!
...
};
// After:
struct io_provide_buf {
__aligned_u64 addr;
__aligned_u32 len; // ensure unsigned!
...
}
// Safe multiplication
size_t total_size;
if (check_mul_overflow(len, nbufs, &total_size)) {
// Fail safely if size would overflow
}
Now, even if someone provides crazy values, the kernel rejects them.
Fix commit:
- d81269fecb8ce: io_uring: fix provide_buffers sign extension
How Serious Was It? Who’s Affected?
- Severity: Medium. Local attackers could crash or interfere with the system; with other bugs, it could be worse.
Affected versions: Kernels with io_uring support, before v5.13-rc1 (fixed in May 2021)
- Who’s at risk: Systems allowing untrusted code to use io_uring, especially multi-user shared systems (like hosting).
Mitigation:
References and Further Reading
- CVE-2021-47040 on NVD
- Linux Kernel Git: d81269fecb8ce (fix for this bug)
- io_uring documentation
- Linux kernel io_uring source
Summary
CVE-2021-47040 highlights why safe arithmetic is crucial in systems programming. Even one little oversight in data type or bounds checking could open the doors to dangerous kernel vulnerabilities. The Linux community quickly patched this up, but it pays to remember: always check your math, especially in code this close to the metal.
Timeline
Published on: 02/28/2024 09:15:39 UTC
Last modified on: 01/09/2025 15:29:49 UTC