From f1596ba3e6b390aa0fef8466afce44efecf39d8d Mon Sep 17 00:00:00 2001 From: Jaeyeong Lee Date: Sun, 12 Jul 2026 14:27:12 +0000 Subject: [PATCH 1/3] io_uring/kbuf: free the replaced iovec after a successful grow The provided-buffer validation fix deferred freeing a cached iovec until validation completed. However, the deferred free uses arg->iovs. After a grow, that points to the newly allocated array. Without a grow, it points to the cached array that remains in use. This leaves the caller with a dangling iovec in both cases and can result in repeated frees. Only free org_iovs when arg->iovs actually replaced it. Fixes: cd053d788c3f ("io_uring: fix dangling iovec after provided-buffer bundle grow failure") Assisted-by: Codex:gpt-5.3-codex-spark Signed-off-by: Jaeyeong Lee Link: https://patch.msgid.link/20260712142612.188695595-iostreampy@proton.me Signed-off-by: Jens Axboe --- io_uring/kbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index b6b969b55e12..de0129bceaba 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -328,8 +328,8 @@ 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 (arg->iovs != org_iovs && (arg->mode & KBUF_MODE_FREE)) + kfree(org_iovs); if (head == tail) req->flags |= REQ_F_BL_EMPTY; From cc609376e9a43166a2fba2aef6c5f9ea262ce722 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Tue, 14 Jul 2026 11:03:06 +0800 Subject: [PATCH 2/3] io_uring/fs: check unused sqe fields for unlinkat Zero check unused SQE fields addr3 and pad2 for unlinkat. They're not needed now, but could be used sometime in the future. Signed-off-by: Yi Xie Reviewed-by: Gabriel Krisman Bertazi Link: https://patch.msgid.link/20260714030306.64820-1-xieyi@kylinos.cn Signed-off-by: Jens Axboe --- io_uring/fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io_uring/fs.c b/io_uring/fs.c index d0580c754bf8..26ea841a22e7 100644 --- a/io_uring/fs.c +++ b/io_uring/fs.c @@ -110,7 +110,8 @@ int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) const char __user *fname; int err; - if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in) + if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in || + sqe->addr3 || sqe->__pad2[0]) return -EINVAL; if (unlikely(req->flags & REQ_F_FIXED_FILE)) return -EBADF; From 3afc64c61ce906a04f073ca350b46de10e8302f9 Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Fri, 17 Jul 2026 22:45:37 +0700 Subject: [PATCH 3/3] io_uring/bpf-ops: reject re-registration of an already-bound ops io_install_bpf() only rejects a second registration on the ctx side (ctx->bpf_ops) and sets the per-map back-pointer ops->priv unconditionally. The struct_ops link path never advances a map past BPF_STRUCT_OPS_STATE_READY, so the same io_uring_bpf_ops map can be registered more than once, and bpf_io_reg() re-resolves the target ring via fget(ops->ring_fd) on every call. A caller can therefore point the same ring_fd at a different io_ring_ctx between two BPF_LINK_CREATE calls. The second registration passes the ctx->bpf_ops check (the new ctx has none) and overwrites ops->priv, orphaning the first ctx. Teardown (io_eject_bpf()/bpf_io_unreg()) only reaches a ctx through ops->priv, so the orphaned ctx is never torn down: its ctx->loop_step keeps pointing into the struct_ops trampoline, which is freed once the map is gone. A later io_uring_enter() on the orphaned ring then calls the dangling ctx->loop_step from io_run_loop() -- a use-after-free of freed executable memory, reachable by a task with CAP_BPF + CAP_PERFMON. Reject registration when ops->priv is already set, as hid_bpf_reg() does for its struct_ops. Cc: stable@vger.kernel.org Fixes: 98f37634b12b ("io_uring/bpf-ops: implement bpf ops registration") Signed-off-by: Woraphat Khiaodaeng Reviewed-by: Gabriel Krisman Bertazi Reviewed-by: Pavel Begunkov Link: https://patch.msgid.link/20260717154537.129736-1-worapat.kd2@gmail.com Signed-off-by: Jens Axboe --- io_uring/bpf-ops.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c index 5a50f0675fe5..cf2bd068e331 100644 --- a/io_uring/bpf-ops.c +++ b/io_uring/bpf-ops.c @@ -168,6 +168,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops) if (ctx->bpf_ops) return -EBUSY; + if (ops->priv) + return -EBUSY; if (WARN_ON_ONCE(!ops->loop_step)) return -EINVAL;