mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
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:
parent
a8a79eba22
commit
ca5a01eee3
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user