io_uring is a modern Linux interface for fast and efficient asynchronous I/O. Since its introduction into the kernel in version 5.1, it is widely used for high-performance server software. However, its complex architecture and novel ideas have introduced some tricky security issues. In 2022, one such bug came to light: CVE-2022-2602, a use-after-free (UAF) vulnerability involving the interaction between io_uring and Unix domain socket garbage collection, triggered by file descriptor passing using SCM_RIGHTS.

In this article, we’ll break down what this vulnerability is, how it can be exploited for privilege escalation, and discuss the details, including example code and links for further reference.

What Is CVE-2022-2602?

CVE-2022-2602 is a use-after-free bug in the Linux kernel’s implementation of io_uring and Unix domain sockets. Specifically, it’s a bug in the way io_uring manages file descriptors when they are sent over Unix domain sockets with SCM_RIGHTS, and those sockets participate in the garbage collection (GC) process.

Background: SCM_RIGHTS and Garbage Collection

If you’ve ever used Unix domain sockets, you may know you can send file descriptors (FDs) between processes using sendmsg and the SCM_RIGHTS control message type. This allows you to share access to files or sockets.

Behind the scenes, the kernel tracks which sockets are holding onto which FDs through “file references”. If every userland reference to an FD is gone but a socket holds a reference (waiting to be received), the kernel's Unix GC will make sure those FDs stay alive until they're unreferenced everywhere—including from the socket queue.

How io_uring Gets Involved

io_uring enables very efficient asynchronous I/O in Linux, but it also manages references to FDs for things like registered files or buffers. In some corner cases, interactions between io_uring and Unix socket FD passing can leave io_uring pointing at memory that’s just been freed by GC.

What’s the Bug?

When a process uses io_uring to work with FDs and passes one of those FDs over a Unix domain socket, the reference counting between io_uring’s file table and the Unix GC can get out of sync. If the GC collects a file still in use by io_uring (for example, because the only outstanding reference is from an io_uring operation), the structure representing the file can be freed while still in use—a classic use-after-free.

This opens the door for various attacks, such as escalating privileges or executing malicious code in kernel mode.

Example: Triggering the Use-After-Free

Below is an extremely simplified pseudocode outline showing how you might approach triggering this bug. (This is for educational discussion only!)

#define _GNU_SOURCE
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <liburing.h>
#include <stdlib.h>

// 1. Set up socketpair
int sv[2];
socketpair(AF_UNIX, SOCK_DGRAM, , sv);

// 2. Open a file and register with io_uring
int fd = open("testfile", O_RDWR | O_CREAT, 060);
struct io_uring ring;
io_uring_queue_init(8, &ring, );

struct io_uring_files_update files_update = {
    .offset = ,
    .fds = &fd,
};
io_uring_register_files(&ring, &fd, 1);

// 3. Send the fd via SCM_RIGHTS
struct msghdr msg = {};
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);

struct cmsghdr *cmsg = (struct cmsghdr*)buf;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));

sendmsg(sv[], &msg, );

// 4. Close original fd references
close(fd);
// Optionally drop all other references, trigger GC

// 5. Garbage collection in the kernel may now free fd, but io_uring may still reference it - UAF!

The kernel’s Unix socket garbage collector might now free the file structure while io_uring is still using it—a use-after-free situation.

Real Exploitation

Attackers can leverage this situation by forcing the kernel to allocate attacker-controlled data on top of the freed "struct file", then trigger io_uring to act on this corrupted object, leading to privilege escalation or arbitrary code execution.

Proof-of-concept exploits have been developed in the wild, and the issue has been confirmed as a potent local root vector.

- CVE Page (Mitre)
- Linux Kernel Patch
- Red Hat Security Advisory
- Exploit details by Andrey Konovalov
- Original Linux kernel io_uring Documentation

Is My System Affected?

Linux systems running vulnerable kernel versions (typically 5.16 and before patches landing in summer 2022) and allowing unprivileged users to create io_uring rings are susceptible. If you're running a recent, vendor-supported kernel or have disabled unprivileged io_uring, you’re likely not vulnerable.

Mitigation and Fix

Mitigation:
- Restrict access to io_uring (see /proc/sys/kernel/io_uring_disabled or systemd restrictions).

Prevent unprivileged users from running code on production hosts.

Patch:
Upgrade your kernel to a version after 5.16.11, or make sure it includes commit 129b6c017cfa45fe9334861e8f175c7ac9118c1f.

Conclusion

CVE-2022-2602 illustrates the risks of mixing complex subsystems—here, io_uring and Unix domain socket GC—in the Linux kernel. Bugs like this, deep in the kernel, are especially dangerous because they allow local users to escalate to root. If you run Linux in production environments, always keep on top of security advisories, and patch as soon as possible.

Stay safe—and happy hacking!

*You read it here first—if you found this helpful, share it with your Linux sysadmin friends!*

Timeline

Published on: 01/08/2024 18:15:00 UTC
Last modified on: 01/08/2024 19:05:00 UTC