When growing a provided-buffer bundle, the old cached iovec is freed before the new buffers have all been validated. If validation fails, the request still points at the freed iovec, which can be freed again during completion cleanup. BUG: KASAN: double-free in io_vec_free+0x2c/0x90 Freed by task 73: kfree+0x104/0x3b0 io_vec_free+0x2c/0x90 __io_submit_flush_completions+0xc03/0x1e40 io_submit_sqes+0xdb5/0x2310 Allocated by task 73: io_ring_buffers_peek+0x559/0xc60 io_buffers_select+0x1c1/0x460 io_send+0x770/0x1050 Fix this by deferring the free of the old cached iovec until validation has succeeded. On failure, free the newly allocated iovec and leave the request pointing at the original one. change log: v2: slimming v1 patch Fixes: 46800585ae04 ("io_uring/kbuf: validate ring provided buffer addresses with access_ok()") Signed-off-by: Hao-Yu Yang --- io_uring/kbuf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index 3cd29477fff2..b6b969b55e12 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -287,8 +287,6 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, iov = kmalloc_objs(struct iovec, nr_avail); if (unlikely(!iov)) return -ENOMEM; - if (arg->mode & KBUF_MODE_FREE) - kfree(arg->iovs); arg->iovs = iov; nr_iovs = nr_avail; } else if (nr_avail < nr_iovs) { @@ -330,6 +328,9 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, buf = io_ring_head_to_buf(br, ++head, bl->mask); } while (--nr_iovs); + if (arg->mode & KBUF_MODE_FREE) + kfree(arg->iovs); + if (head == tail) req->flags |= REQ_F_BL_EMPTY; -- 2.34.1