(CVE-2023-1872) Linux Kernel io_uring Missing Lock in io_file_get_fixed Leading to Use-After-Free and Local Privilege Escalation
CVE: CVE-2023-1872
Affected Versions: Linux kernel 5.7 through 5.17 (exclusive); stable branches 5.10.170, 5.15.96
CVSS3.1: 7.8 (High) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Summary
| Product | Linux Kernel (io_uring) |
|---|---|
| Vendor | Linux Kernel |
| Severity | High — a local unprivileged attacker may exploit this to achieve local privilege escalation |
| Affected Versions | Linux kernel 5.7 through 5.17; stable branches 5.10.170, 5.15.96 |
| CVE Identifier | CVE-2023-1872 |
| CVE Description | A use-after-free vulnerability in the Linux kernel io_uring subsystem can be exploited to achieve local privilege escalation |
| CWE Classification(s) | CWE-416: Use After Free |
CVSS3.1 Scoring System
Base Score: 7.8 (High)
Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
| Metric | Value |
|---|---|
| Attack Vector (AV) | Local |
| Attack Complexity (AC) | Low |
| Privileges Required (PR) | Low |
| User Interaction (UI) | None |
| Scope (S) | Unchanged |
| Confidentiality (C) | High |
| Integrity (I) | High |
| Availability (A) | High |
Technical Details
The vulnerability exists in io_file_get_fixed, which retrieves a fixed file slot for a given file descriptor without holding ctx->uring_lock:
static inline struct file *io_file_get_fixed(struct io_ring_ctx *ctx,
struct io_kiocb *req, int fd)
{
struct file *file;
unsigned long file_ptr;
if (unlikely((unsigned int)fd >= ctx->nr_user_files))
return NULL;
fd = array_index_nospec(fd, ctx->nr_user_files);
file_ptr = io_fixed_file_slot(&ctx->file_table, fd)->file_ptr;
file = (struct file *) (file_ptr & FFS_MASK);
file_ptr &= ~FFS_MASK;
/* mask in overlapping REQ_F and FFS bits */
req->flags |= (file_ptr << REQ_F_NOWAIT_READ_BIT);
io_req_set_rsrc_node(req);
return file;
}
Without the lock, a concurrent file unregistration can free the fixed file while io_file_get_fixed is still referencing it. This race is reachable via an async IORING_OP_SPLICE operation, where the splice path calls into io_file_get_fixed from a worker thread without holding ctx->uring_lock, while another thread unregisters the fixed file set.
The resulting use-after-free of a struct file object (allocated from the filp slab cache) is exploitable for local privilege escalation.
KASAN Crash
[ 139.075384] ==================================================================
[ 139.086687] BUG: KASAN: use-after-free in do_splice_to+0x27/0xc0
[ 139.092853] Read of size 4 at addr ffff88812e42265c by task iou-wrk-516/517
[ 139.101692] CPU: 1 PID: 517 Comm: iou-wrk-516 Tainted: G E 5.10.170 #5
[ 139.109361] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
[ 139.116726] Call Trace:
[ 139.117775] dump_stack+0x7d/0xa7
[ 139.119009] print_address_description.constprop.0+0x1e/0x220
[ 139.121202] ? do_splice_to+0x27/0xc0
[ 139.125151] kasan_report.cold+0x1f/0x37
[ 139.129144] ? do_splice_to+0x27/0xc0
[ 139.130459] do_splice_to+0x27/0xc0
[ 139.131759] do_splice+0x96d/0xa30
[ 139.134765] ? generic_splice_sendpage+0x20/0x20
[ 139.136308] io_issue_sqe+0x24b7/0x2ba0
[ 139.139245] ? fill_random_ptr_key+0x30/0x30
[ 139.141846] ? io_write+0x620/0x620
[ 139.143165] ? do_raw_spin_lock+0x11a/0x1e0
[ 139.147506] io_wq_submit_work+0xb2/0x170
[ 139.150457] io_worker_handle_work+0x45d/0xa10
[ 139.152716] io_wqe_worker+0x341/0x5b0
[ 139.157273] ? io_worker_handle_work+0xa10/0xa10
[ 139.161554] ? recalc_sigpending_tsk+0x83/0xc0
[ 139.163205] ret_from_fork+0x22/0x30
[ 139.165024] Allocated by task 516:
[ 139.167674] __kasan_kmalloc.constprop.0+0xc9/0xd0
[ 139.169310] kmem_cache_alloc+0x12d/0x450
[ 139.170722] __alloc_file+0x25/0x1f0
[ 139.171951] alloc_empty_file+0x41/0xc0
[ 139.173133] path_openat+0xd1/0x18d0
[ 139.174379] do_filp_open+0x136/0x1c0
[ 139.176924] do_sys_open+0x8a/0xe0
[ 139.181596] Freed by task 23:
[ 139.186809] __kasan_slab_free+0x10f/0x160
[ 139.188259] kmem_cache_free+0xe7/0x440
[ 139.189678] rcu_core+0x36d/0x900
[ 139.190926] __do_softirq+0xff/0x388
[ 139.205774] The buggy address belongs to the object at ffff88812e422600
[ 139.205774] which belongs to the cache filp of size 320
[ 139.210015] The buggy address is located 92 bytes inside of
[ 139.210015] 320-byte region [ffff88812e422600, ffff88812e422740)
==================================================================
Fix
The fix adds ctx->uring_lock acquisition in io_file_get_fixed to prevent concurrent file unregistration during the splice path.
Credit
Billy Jheng Bing-Jhong of STAR Labs SG Pte. Ltd.
Timeline
- 2023-03-02 — Reported to Linux Kernel Security Team
- 2023-04-12 — CVE-2023-1872 published and fix merged