oss-sec mailing list archives
Linux kernel: use-after-free in io_sqpoll_wait_sq
From: Xingyuan Mo <hdthky0 () gmail com>
Date: Thu, 22 Dec 2022 11:35:14 +0800
Hello, There is a use-after-free vulnerability in io_sqpoll_wait_sq() in fs/io_uring.c in linux-5.10.y through v5.10.154, which allows an attacker to crash the kernel, resulting in Denial of Service. =*=*=*=*=*=*=*=*= Bug Details =*=*=*=*=*=*=*=*= 9028: static int io_sqpoll_wait_sq(struct io_ring_ctx *ctx) 9029: { 9030: int ret = 0; 9031: DEFINE_WAIT(wait); 9032: 9033: do { 9034: if (!io_sqring_full(ctx)) 9035: break; 9036: 9037: prepare_to_wait(&ctx->sqo_sq_wait, &wait, TASK_INTERRUPTIBLE); 9038: 9039: if (unlikely(ctx->sqo_dead)) { 9040: ret = -EOWNERDEAD; 9041: goto out; 9042: } 9043: 9044: if (!io_sqring_full(ctx)) 9045: break; 9046: 9047: schedule(); 9048: } while (!signal_pending(current)); 9049: 9050: finish_wait(&ctx->sqo_sq_wait, &wait); 9051: out: 9052: return ret; 9053: } On line 9037 of fs/io_uring.c, a wait_queue_entry object on the stack named wait is added to wait queue ctx->sqo_sq_wait, which should be removed from ctx->sqo_sq_wait by calling finish_wait() once the current task does not need to wait for an available submission queue entry. Though, On line 9039, if ctx->sqo_dead is not 0, the control flow jumps to out, skipping the call to finish_wait() on line 9050. As a result, wait still exists in ctx->sqo_sq_wait even when the current task exits kernel mode or comes to an end, which means that the two entries before and after wait each contain a stale pointer to the expired kernel stack space. If one of the two entries is later unlinked from ctx->sqo_dead, the memory of the expired stack space pointed to by the stale pointer will be corrupted, resulting in use-after-free. As mentioned earlier, the condition for triggering the vulnerability is that ctx->sqo_dead is not 0, which can be achieved by forking a new process and terminating it quickly. When the new process exits, the copied io_uring file descriptor will be closed, causing the following call chain to be triggered: io_uring_flush()->io_uring_cancel_task_requests()->io_disable_sqo_submit(). In io_disable_sqo_submit(), ctx->sqo_dead is assigned 1 on line 8732. 8729: static void io_disable_sqo_submit(struct io_ring_ctx *ctx) 8730: { 8731: mutex_lock(&ctx->uring_lock); 8732: ctx->sqo_dead = 1; 8733: if (ctx->flags & IORING_SETUP_R_DISABLED) 8734: io_sq_offload_start(ctx); 8735: mutex_unlock(&ctx->uring_lock); 8736: 8737: /* make sure callers enter the ring to get error */ 8738: if (ctx->rings) 8739: io_ring_set_wakeup_flag(ctx); 8740: } =*=*=*=*=*=*=*=*= Patch =*=*=*=*=*=*=*=*= The patch can be found here: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v5.10.161&id=0f544353fec8e717d37724d95b92538e1de79e86 =*=*=*=*=*=*=*=*= Credit =*=*=*=*=*=*=*=*= Xingyuan Mo and Gengjia Chen of IceSword Lab, Qihoo 360 Technology Co. Ltd. Best Regards, Xingyuan Mo
Current thread:
- Linux kernel: use-after-free in io_sqpoll_wait_sq Xingyuan Mo (Dec 22)
- Re: Linux kernel: use-after-free in io_sqpoll_wait_sq Xingyuan Mo (Dec 27)