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)
