Commit da0f60df authored by Jason Gunthorpe's avatar Jason Gunthorpe

RDMA/uverbs: Prohibit write() calls with too small buffers

The size meta-data in the prior patch describes the smallest acceptable
buffer for the write() interface. Globally check this in the core code.

This is necessary in the case of write() methods that have a driver udata
to prevent computing a negative udata buffer length.

The return code of -ENOSPC is chosen here as some of the handlers already
use this code, however many other handler use EINVAL.
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
parent 669dac1e
...@@ -589,15 +589,18 @@ struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file ...@@ -589,15 +589,18 @@ struct file *ib_uverbs_alloc_async_event_file(struct ib_uverbs_file *uverbs_file
} }
static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr, static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr,
struct ib_uverbs_ex_cmd_hdr *ex_hdr, struct ib_uverbs_ex_cmd_hdr *ex_hdr, size_t count,
size_t count, bool extended) const struct uverbs_api_write_method *method_elm)
{ {
if (extended) { if (method_elm->is_ex) {
count -= sizeof(*hdr) + sizeof(*ex_hdr); count -= sizeof(*hdr) + sizeof(*ex_hdr);
if ((hdr->in_words + ex_hdr->provider_in_words) * 8 != count) if ((hdr->in_words + ex_hdr->provider_in_words) * 8 != count)
return -EINVAL; return -EINVAL;
if (hdr->in_words * 8 < method_elm->req_size)
return -ENOSPC;
if (ex_hdr->cmd_hdr_reserved) if (ex_hdr->cmd_hdr_reserved)
return -EINVAL; return -EINVAL;
...@@ -605,6 +608,9 @@ static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr, ...@@ -605,6 +608,9 @@ static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr,
if (!hdr->out_words && !ex_hdr->provider_out_words) if (!hdr->out_words && !ex_hdr->provider_out_words)
return -EINVAL; return -EINVAL;
if (hdr->out_words * 8 < method_elm->resp_size)
return -ENOSPC;
if (!access_ok(VERIFY_WRITE, if (!access_ok(VERIFY_WRITE,
u64_to_user_ptr(ex_hdr->response), u64_to_user_ptr(ex_hdr->response),
(hdr->out_words + ex_hdr->provider_out_words) * 8)) (hdr->out_words + ex_hdr->provider_out_words) * 8))
...@@ -621,6 +627,24 @@ static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr, ...@@ -621,6 +627,24 @@ static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr,
if (hdr->in_words * 4 != count) if (hdr->in_words * 4 != count)
return -EINVAL; return -EINVAL;
if (count < method_elm->req_size + sizeof(hdr)) {
/*
* rdma-core v18 and v19 have a bug where they send DESTROY_CQ
* with a 16 byte write instead of 24. Old kernels didn't
* check the size so they allowed this. Now that the size is
* checked provide a compatibility work around to not break
* those userspaces.
*/
if (hdr->command == IB_USER_VERBS_CMD_DESTROY_CQ &&
count == 16) {
hdr->in_words = 6;
return 0;
}
return -ENOSPC;
}
if (hdr->out_words * 4 < method_elm->resp_size)
return -ENOSPC;
return 0; return 0;
} }
...@@ -659,7 +683,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, ...@@ -659,7 +683,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,
return -EFAULT; return -EFAULT;
} }
ret = verify_hdr(&hdr, &ex_hdr, count, method_elm->is_ex); ret = verify_hdr(&hdr, &ex_hdr, count, method_elm);
if (ret) if (ret)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment