ublk: validate auto buf reg before taking uring_cmd

With UBLK_F_AUTO_BUF_REG, invalid sqe->addr can fail after
ublk_fill_io_cmd() has set UBLK_IO_FLAG_ACTIVE. The uring_cmd is
completed while the tag stays active, which can hang teardown.

Split validation from buffer apply so the check has no side effects,
then take the uring_cmd and store the already-validated buffer. Apply
the same order in FETCH so io->buf is not written before __ublk_fetch()
state checks.

Fixes: 52460dda3a ("ublk: move auto buffer register handling into one dedicated helper")
Suggested-by: Caleb Sander Mateos <csander@purestorage.com>
Signed-off-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Yang Xiuwei 2026-07-30 09:09:09 +08:00 committed by Jens Axboe
parent a8a79eba22
commit ca5a01eee3

View File

@ -3077,18 +3077,19 @@ static inline int ublk_check_cmd_op(u32 cmd_op)
return 0;
}
static inline int ublk_set_auto_buf_reg(struct ublk_io *io, struct io_uring_cmd *cmd)
/* Must run before ublk_fill_io_cmd() / __ublk_fetch(). */
static inline int ublk_validate_io_buf(const struct ublk_device *ub,
struct io_uring_cmd *cmd,
struct ublk_auto_buf_reg *buf)
{
struct ublk_auto_buf_reg buf;
if (!ublk_dev_support_auto_buf_reg(ub))
return 0;
buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
if (buf.reserved0 || buf.reserved1)
*buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
if (buf->reserved0 || buf->reserved1)
return -EINVAL;
if (buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK)
if (buf->flags & ~UBLK_AUTO_BUF_REG_F_MASK)
return -EINVAL;
io->buf.auto_reg = buf;
return 0;
}
@ -3109,17 +3110,25 @@ static void ublk_clear_auto_buf_reg(struct ublk_io *io,
* responsibility for unregistering the buffer, otherwise
* this ublk request gets stuck.
*/
if (io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd))
if (buf_idx &&
io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd))
*buf_idx = io->buf.auto_reg.index;
}
}
static int ublk_handle_auto_buf_reg(struct ublk_io *io,
struct io_uring_cmd *cmd,
u16 *buf_idx)
static inline void ublk_apply_io_buf(const struct ublk_device *ub,
struct ublk_io *io,
struct io_uring_cmd *cmd,
unsigned long buf_addr,
const struct ublk_auto_buf_reg *auto_buf,
u16 *buf_idx)
{
ublk_clear_auto_buf_reg(io, cmd, buf_idx);
return ublk_set_auto_buf_reg(io, cmd);
if (ublk_dev_support_auto_buf_reg(ub)) {
ublk_clear_auto_buf_reg(io, cmd, buf_idx);
io->buf.auto_reg = *auto_buf;
} else {
io->buf.addr = buf_addr;
}
}
/* Once we return, `io->req` can't be used any more */
@ -3136,18 +3145,6 @@ ublk_fill_io_cmd(struct ublk_io *io, struct io_uring_cmd *cmd)
return req;
}
static inline int
ublk_config_io_buf(const struct ublk_device *ub, struct ublk_io *io,
struct io_uring_cmd *cmd, unsigned long buf_addr,
u16 *buf_idx)
{
if (ublk_dev_support_auto_buf_reg(ub))
return ublk_handle_auto_buf_reg(io, cmd, buf_idx);
io->buf.addr = buf_addr;
return 0;
}
static inline void ublk_prep_cancel(struct io_uring_cmd *cmd,
unsigned int issue_flags,
struct ublk_queue *ubq, u16 tag)
@ -3288,6 +3285,7 @@ static int __ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
struct ublk_io *io, __u64 buf_addr, u16 q_id)
{
struct ublk_auto_buf_reg auto_buf;
int ret;
/*
@ -3296,11 +3294,13 @@ static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
* FETCH, so it is fine even for IO_URING_F_NONBLOCK.
*/
mutex_lock(&ub->mutex);
ret = __ublk_fetch(cmd, ub, io, q_id);
if (!ret)
ret = ublk_config_io_buf(ub, io, cmd, buf_addr, NULL);
ret = ublk_validate_io_buf(ub, cmd, &auto_buf);
if (!ret)
ret = __ublk_fetch(cmd, ub, io, q_id);
if (!ret) {
ublk_apply_io_buf(ub, io, cmd, buf_addr, &auto_buf, NULL);
ublk_mark_io_ready(ub, q_id, io);
}
mutex_unlock(&ub->mutex);
return ret;
}
@ -3443,13 +3443,18 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
case UBLK_IO_REGISTER_IO_BUF:
return ublk_daemon_register_io_buf(cmd, ub, q_id, tag, io, addr,
issue_flags);
case UBLK_IO_COMMIT_AND_FETCH_REQ:
case UBLK_IO_COMMIT_AND_FETCH_REQ: {
struct ublk_auto_buf_reg auto_buf;
ret = ublk_check_commit_and_fetch(ub, io, addr);
if (ret)
goto out;
ret = ublk_validate_io_buf(ub, cmd, &auto_buf);
if (ret)
goto out;
io->res = result;
req = ublk_fill_io_cmd(io, cmd);
ret = ublk_config_io_buf(ub, io, cmd, addr, &buf_idx);
ublk_apply_io_buf(ub, io, cmd, addr, &auto_buf, &buf_idx);
if (buf_idx != UBLK_INVALID_BUF_IDX)
io_buffer_unregister_bvec(cmd, buf_idx, issue_flags);
compl = ublk_need_complete_req(ub, io);
@ -3458,10 +3463,8 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
req->__sector = addr;
if (compl)
__ublk_complete_rq(req, io, ublk_dev_need_map_io(ub), NULL);
if (ret)
goto out;
break;
}
case UBLK_IO_NEED_GET_DATA:
/*
* ublk_get_data() may fail and fallback to requeue, so keep
@ -3469,8 +3472,7 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
* request
*/
req = ublk_fill_io_cmd(io, cmd);
ret = ublk_config_io_buf(ub, io, cmd, addr, NULL);
WARN_ON_ONCE(ret);
io->buf.addr = addr;
if (likely(ublk_get_data(ubq, io, req))) {
__ublk_prep_compl_io_cmd(io, req);
return UBLK_IO_RES_OK;