* [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg()
@ 2026-04-22 15:53 Ali Raza
2026-04-22 21:05 ` sashiko-bot
2026-04-22 21:20 ` Gabriel Krisman Bertazi
0 siblings, 2 replies; 5+ messages in thread
From: Ali Raza @ 2026-04-22 15:53 UTC (permalink / raw)
To: Jens Axboe; +Cc: io-uring, linux-kernel, bpf, Ali Raza, Pavel Begunkov
bpf_io_reg() installs a BPF struct_ops loop_step on any io_uring ring
the caller holds a file descriptor for. io_uring_ctx_get_file() only
validates that the fd resolves to an io_uring file; it does not verify
the caller has authority over the ring's submitter_task.
A parallel path in io_uring_register() already enforces this:
if (ctx->submitter_task && ctx->submitter_task != current)
return -EEXIST; /* register.c:733 */
Without the equivalent check in bpf_io_reg(), a local user with
CAP_PERFMON can exploit IORING_SETUP_R_DISABLED -- which defers
submitter_task assignment until IORING_REGISTER_ENABLE_RINGS -- to
install a loop_step on a ring before a more-privileged process becomes
its submitter_task. The loop_step then executes in the privileged
process's task context and can issue arbitrary io_uring operations
(IORING_OP_WRITE, IORING_OP_READ, IORING_OP_SPLICE) against that
process's open file table. This provides a cross-privilege io_uring
execution primitive that can serve as a component in a privilege
escalation chain when combined with a vector that induces a privileged
process to adopt an attacker-controlled ring.
Affected: v7.1-rc1+ with CONFIG_IO_URING_BPF_OPS=y.
Requires: IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER.
Add the ownership check in io_install_bpf(), which is called under
uring_lock, matching the locking context of the register.c check.
Signed-off-by: Ali Raza <elirazamumtaz@gmail.com>
Cc: Pavel Begunkov <asml.silence@gmail.com>
---
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 937e48bef40b..cac11c929297 100644
--- a/io_uring/bpf-ops.c
+++ b/io_uring/bpf-ops.c
@@ -162,6 +162,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops)
return -EOPNOTSUPP;
if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN))
return -EOPNOTSUPP;
+ if (ctx->submitter_task && ctx->submitter_task != current)
+ return -EPERM;
if (ctx->bpf_ops)
return -EBUSY;
---
base-commit: bea8d77e45a8b77f2beca1affc9aa7ed28f39b17
change-id: 20260422-master-d96fe0e8bb3c
Best regards,
--
Ali Raza <elirazamumtaz@gmail.com>
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg()
2026-04-22 15:53 [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg() Ali Raza
@ 2026-04-22 21:05 ` sashiko-bot
2026-04-22 21:20 ` Gabriel Krisman Bertazi
1 sibling, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-04-22 21:05 UTC (permalink / raw)
To: Ali Raza; +Cc: bpf
Sashiko AI review found 2 potential issue(s):
- [Critical] Incomplete fix for privilege escalation via deferred `submitter_task` in `io_install_bpf()`
- [Medium] Denial of Service via user-triggerable WARN_ON_ONCE for missing `ops->loop_step`
--
commit 0ef4622575dbd984db4b264672ee9cc5b456a439
Author: Ali Raza <elirazamumtaz@gmail.com>
io_uring: fix missing submitter_task ownership check in bpf_io_reg()
This commit attempts to fix a cross-privilege execution vulnerability in
bpf_io_reg() where an attacker could install a malicious BPF loop_step
on a deferred ring before a privileged process adopts it.
> diff --git a/io_uring/bpf-ops.c b/io_uring/bpf-ops.c
> index 937e48bef40bc..cac11c9292971 100644
> --- a/io_uring/bpf-ops.c
> +++ b/io_uring/bpf-ops.c
> @@ -162,6 +162,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops)
> return -EOPNOTSUPP;
> if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN))
> return -EOPNOTSUPP;
> + if (ctx->submitter_task && ctx->submitter_task != current)
> + return -EPERM;
The commit message states that the vulnerability involves exploiting
IORING_SETUP_R_DISABLED, which defers submitter_task assignment. If the
ring is disabled, wouldn't ctx->submitter_task initially be NULL?
Because the check uses ctx->submitter_task && ..., the short-circuit
evaluation will evaluate to false and bypass the permission check entirely
when the submitter task is NULL. Does this allow the attacker to still
successfully install the BPF operations during the deferred assignment
window?
Should this explicitly reject registration if the submitter task is not
yet established, for example by checking:
if (!ctx->submitter_task || ctx->submitter_task != current)?
>
> if (ctx->bpf_ops)
> return -EBUSY;
Looking a few lines further down in io_install_bpf():
if (WARN_ON_ONCE(!ops->loop_step))
return -EINVAL;
Since ops->loop_step comes from a user-provided BPF struct_ops map, can a
local user intentionally omit it to trigger this warning?
On systems configured with panic_on_warn=1, could this be used to trigger
a denial of service? Would it be safer to use a standard conditional
return without the warning macro?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260422-master-v1-1-e82f47558345@gmail.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg()
2026-04-22 15:53 [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg() Ali Raza
2026-04-22 21:05 ` sashiko-bot
@ 2026-04-22 21:20 ` Gabriel Krisman Bertazi
2026-04-22 21:46 ` Jens Axboe
2026-04-22 21:58 ` Pavel Begunkov
1 sibling, 2 replies; 5+ messages in thread
From: Gabriel Krisman Bertazi @ 2026-04-22 21:20 UTC (permalink / raw)
To: Ali Raza; +Cc: Jens Axboe, io-uring, linux-kernel, bpf, Pavel Begunkov
Ali Raza <elirazamumtaz@gmail.com> writes:
> bpf_io_reg() installs a BPF struct_ops loop_step on any io_uring ring
> the caller holds a file descriptor for. io_uring_ctx_get_file() only
> validates that the fd resolves to an io_uring file; it does not verify
> the caller has authority over the ring's submitter_task.
>
> A parallel path in io_uring_register() already enforces this:
>
> if (ctx->submitter_task && ctx->submitter_task != current)
> return -EEXIST; /* register.c:733 */
How is this a protection? I thought ctx->submitter_task is about
IORING_SETUP_SINGLE_ISSUER. there is no permission or capability over
it against other processes.
> Without the equivalent check in bpf_io_reg(), a local user with
> CAP_PERFMON can exploit IORING_SETUP_R_DISABLED -- which defers
I'd argue this is a non-issue. If you have CAP_PERFMON, you are able to
mess with the process in many ways beyond this. Otherwise, how a
process would be able to get the fd in the first place?
> submitter_task assignment until IORING_REGISTER_ENABLE_RINGS -- to
> install a loop_step on a ring before a more-privileged process becomes
> its submitter_task. The loop_step then executes in the privileged
> process's task context and can issue arbitrary io_uring operations
> (IORING_OP_WRITE, IORING_OP_READ, IORING_OP_SPLICE) against that
> process's open file table. This provides a cross-privilege io_uring
> execution primitive that can serve as a component in a privilege
> escalation chain when combined with a vector that induces a privileged
> process to adopt an attacker-controlled ring.
>
> Affected: v7.1-rc1+ with CONFIG_IO_URING_BPF_OPS=y.
> Requires: IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER.
>
> Add the ownership check in io_install_bpf(), which is called under
> uring_lock, matching the locking context of the register.c check.
>
> Signed-off-by: Ali Raza <elirazamumtaz@gmail.com>
> Cc: Pavel Begunkov <asml.silence@gmail.com>
> ---
> 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 937e48bef40b..cac11c929297 100644
> --- a/io_uring/bpf-ops.c
> +++ b/io_uring/bpf-ops.c
> @@ -162,6 +162,8 @@ static int io_install_bpf(struct io_ring_ctx *ctx, struct io_uring_bpf_ops *ops)
> return -EOPNOTSUPP;
> if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN))
> return -EOPNOTSUPP;
> + if (ctx->submitter_task && ctx->submitter_task != current)
> + return -EPERM;
>
> if (ctx->bpf_ops)
> return -EBUSY;
>
> ---
> base-commit: bea8d77e45a8b77f2beca1affc9aa7ed28f39b17
> change-id: 20260422-master-d96fe0e8bb3c
>
> Best regards,
> --
>
> Ali Raza <elirazamumtaz@gmail.com>
>
--
Gabriel Krisman Bertazi
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg()
2026-04-22 21:20 ` Gabriel Krisman Bertazi
@ 2026-04-22 21:46 ` Jens Axboe
2026-04-22 21:58 ` Pavel Begunkov
1 sibling, 0 replies; 5+ messages in thread
From: Jens Axboe @ 2026-04-22 21:46 UTC (permalink / raw)
To: Gabriel Krisman Bertazi, Ali Raza
Cc: io-uring, linux-kernel, bpf, Pavel Begunkov
On 4/22/26 3:20 PM, Gabriel Krisman Bertazi wrote:
> Ali Raza <elirazamumtaz@gmail.com> writes:
>
>> bpf_io_reg() installs a BPF struct_ops loop_step on any io_uring ring
>> the caller holds a file descriptor for. io_uring_ctx_get_file() only
>> validates that the fd resolves to an io_uring file; it does not verify
>> the caller has authority over the ring's submitter_task.
>>
>> A parallel path in io_uring_register() already enforces this:
>>
>> if (ctx->submitter_task && ctx->submitter_task != current)
>> return -EEXIST; /* register.c:733 */
>
> How is this a protection? I thought ctx->submitter_task is about
> IORING_SETUP_SINGLE_ISSUER. there is no permission or capability over
> it against other processes.
>
>> Without the equivalent check in bpf_io_reg(), a local user with
>> CAP_PERFMON can exploit IORING_SETUP_R_DISABLED -- which defers
>
> I'd argue this is a non-issue. If you have CAP_PERFMON, you are able to
> mess with the process in many ways beyond this. Otherwise, how a
> process would be able to get the fd in the first place?
It is a non-issue. It relies entirely on an unrealistic scenarior. Yes
if you have a privileged task that can take over a non-privileged ring
fd, yes than you can do bad things. News at 11...
--
Jens Axboe
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg()
2026-04-22 21:20 ` Gabriel Krisman Bertazi
2026-04-22 21:46 ` Jens Axboe
@ 2026-04-22 21:58 ` Pavel Begunkov
1 sibling, 0 replies; 5+ messages in thread
From: Pavel Begunkov @ 2026-04-22 21:58 UTC (permalink / raw)
To: Gabriel Krisman Bertazi, Ali Raza; +Cc: Jens Axboe, io-uring, linux-kernel, bpf
On 4/22/26 22:20, Gabriel Krisman Bertazi wrote:
> Ali Raza <elirazamumtaz@gmail.com> writes:
>
>> bpf_io_reg() installs a BPF struct_ops loop_step on any io_uring ring
>> the caller holds a file descriptor for. io_uring_ctx_get_file() only
>> validates that the fd resolves to an io_uring file; it does not verify
>> the caller has authority over the ring's submitter_task.
>>
>> A parallel path in io_uring_register() already enforces this:
>>
>> if (ctx->submitter_task && ctx->submitter_task != current)
>> return -EEXIST; /* register.c:733 */
>
> How is this a protection? I thought ctx->submitter_task is about
> IORING_SETUP_SINGLE_ISSUER. there is no permission or capability over
> it against other processes.
>
>> Without the equivalent check in bpf_io_reg(), a local user with
>> CAP_PERFMON can exploit IORING_SETUP_R_DISABLED -- which defers
>
> I'd argue this is a non-issue.
Right, it involves receiving a ring from an untrusted source and
then using it. Any application doing that is extremely broken,
even without any bpf you can use that to do some pretty nasty things
If you have CAP_PERFMON, you are able to
> mess with the process in many ways beyond this. Otherwise, how a
> process would be able to get the fd in the first place?
--
Pavel Begunkov
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-04-22 21:58 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-22 15:53 [PATCH] io_uring: fix missing submitter_task ownership check in bpf_io_reg() Ali Raza
2026-04-22 21:05 ` sashiko-bot
2026-04-22 21:20 ` Gabriel Krisman Bertazi
2026-04-22 21:46 ` Jens Axboe
2026-04-22 21:58 ` Pavel Begunkov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox