From 8543a39f03db8816a2d80d0c92c132a19f2afb48 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 21 Oct 2026 08:24:13 +0000 Subject: [PATCH] io_uring/bpf-ops: clear bpf_ctx while submitting SQEs bpf_io_uring_submit_sqes() calls io_submit_sqes() from inside the loop program, with current->bpf_ctx set, and requests issued inline run with it set too. The socket code uses has_current_bpf_ctx() to recognize calls from bpf_setsockopt() and friends, where the caller holds the socket lock and has already been vetted, and skips both. So a SOCKET_URING_OP_SETSOCKOPT from the SQ passes sockopt_capable() without the capability, and runs without lock_sock(). A task with no capability can set SO_MARK, IP_TRANSPARENT or TCP_REPAIR, and two threads changing IP_OPTIONS on one socket, one through the ring, double free in no time. SOCK_DESTROY via IORING_OP_SENDMSG ends up in tcp_abort() without the lock the same way. Clear the run context around io_submit_sqes(). The SQEs come from userspace and must be issued like those of any other ring. They still run inside the program's RCU tasks trace read section, so a request that sleeps there holds up that grace period, but other request types do that already. Fixes: 890819248a86 ("io_uring/bpf-ops: add kfunc helpers") Signed-off-by: Chris Mason Signed-off-by: Jens Axboe --- io_uring/bpf-ops.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c index cf2bd068e331..6c2a587fb1bf 100644 --- a/io_uring/bpf-ops.c +++ b/io_uring/bpf-ops.c @@ -17,8 +17,22 @@ __bpf_kfunc_start_defs(); __bpf_kfunc int bpf_io_uring_submit_sqes(struct iou_ctx *loop_ctx, u32 nr) { struct io_ring_ctx *ctx = io_loop_demangle_ctx(loop_ctx); + struct bpf_run_ctx *old_run_ctx; + int ret; - return io_submit_sqes(ctx, nr); + /* + * The requests come from the SQ, which user space can write, so they + * are issued for the task and not for the BPF program. Hide the run + * context of the program from them. The users of has_current_bpf_ctx(), + * such as sockopt_lock_sock() and sockopt_capable(), take a context for + * a call from a BPF helper, whose caller holds the socket lock already + * and needs no capability. + */ + old_run_ctx = bpf_set_run_ctx(NULL); + ret = io_submit_sqes(ctx, nr); + bpf_reset_run_ctx(old_run_ctx); + + return ret; } __bpf_kfunc