[RFC PATCH] io_uring/cmd: don't skip completion for a non-armed multishot uring_cmd

Vasileios Almpanis <[email protected]>
Newsgroups org.kernel.vger.io-uring,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
io_uring_cmd() treats any uring_cmd carrying IORING_URING_CMD_MULTISHOT
that returns >= 0 as "multishot armed, completion deferred" and returns
IOU_ISSUE_SKIP_COMPLETE, expecting the provider to complete the request
later. But the flag is user-controlled and validated only against buffer
select, not against provider capability. A ->uring_cmd() handler that
does not implement multishot and returns a normal >= 0 result then has
its request skipped and never completed, leaking the io_kiocb and its
io_async_cmd:

  BUG: memory leak
  unreferenced object (size 248):
    kmem_cache_alloc_bulk_noprof+0x272/0x3f0
    __io_alloc_req_refill+0x4a/0x150
    io_submit_sqes.cold+0x16e/0x20b
    __do_sys_io_uring_enter+0x56d/0xd60

syzbot hit this via ublk UBLK_U_CMD_ADD_DEV, but it is kernel-wide: the
same leak reproduces with SOCKET_URING_OP_SIOCINQ on any socket fd, which
returns the queued byte count and never inspects cmd->flags. No in-tree
provider of multishot actually returns >= 0. Both io_cmd_poll_multishot()
and ublk_handle_batch_fetch_cmd return -EIOCBQUEUED,

Only skip completion when the command is really multishot, i.e.
REQ_F_APOLL_MULTISHOT is set. Otherwise fall through and complete the
request normally with its result.

Fixes: 620a50c92700 ("io_uring: uring_cmd: add multishot support")
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=a4ccdd7ebf452e4d4701
Tested-by: [email protected]
Signed-off-by: Vasileios Almpanis <[email protected]>
---
Questions / Notes:
- I put this in the core io_uring_cmd() rather than in provider specific
  code because the leak is independent of the provider. From my research
  so far no ->uring_cmd() rejects IORING_URING_CMD_MULTISHOT, they just
  ignore it. The flag is only validated against buffer-select in
  io_uring_cmd_prep(). So any handler that returns a plain >= 0 result
  with the user-set flag leaks.
- Instead of checking if it has been really armed should we just drop
  the >= 0 check? The in-tree code that supports multishot returns
  -EIOCBQUEUED from what I have seen so far.
---
 io_uring/uring_cmd.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c
index c14c22cff49e..a2899a852879 100644
--- a/io_uring/uring_cmd.c
+++ b/io_uring/uring_cmd.c
@@ -269,7 +269,8 @@ int io_uring_cmd(struct io_kiocb *req, unsigned int issue_flags)
 	}
 
 	ret = file->f_op->uring_cmd(ioucmd, issue_flags);
-	if (ioucmd->flags & IORING_URING_CMD_MULTISHOT) {
+	if ((ioucmd->flags & IORING_URING_CMD_MULTISHOT) &&
+	    (req->flags & REQ_F_APOLL_MULTISHOT)) {
 		if (ret >= 0)
 			return IOU_ISSUE_SKIP_COMPLETE;
 	}

---
base-commit: d58772d8520c7ef247c4b95c9bd76d3a25da9ff5
change-id: 20260811-io_uring-169337c74617

Best regards,
--  
Vasileios Almpanis <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.