summaryrefslogtreecommitdiff
path: root/drivers/infiniband/core/uverbs_ioctl.c
diff options
context:
space:
mode:
authorJason Gunthorpe <jgg@mellanox.com>2018-07-10 20:55:14 -0600
committerJason Gunthorpe <jgg@mellanox.com>2018-07-25 14:21:21 -0600
commit1250c3048cf1632f5dbb99a0242410baff67955d (patch)
treeccf33bb417e90d4b4acd8ab1a7f674beeded6983 /drivers/infiniband/core/uverbs_ioctl.c
parent3df593bfe6455f28cda879be8299b30b8601ce3b (diff)
downloadlwn-1250c3048cf1632f5dbb99a0242410baff67955d.tar.gz
lwn-1250c3048cf1632f5dbb99a0242410baff67955d.zip
IB/uverbs: Handle IDR and FD types without truncation
Our ABI for write() uses a s32 for FDs and a u32 for IDRs, but internally we ended up implicitly casting these ABI values into an 'int'. For ioctl() we use a s64 for FDs and a u64 for IDRs, again casting to an int. The various casts to int are all missing range checks which can cause userspace values that should be considered invalid to be accepted. Fix this by making the generic lookup routine accept a s64, which does not truncate the write API's u32/s32 or the ioctl API's s64. Then push the detailed range checking down to the actual type implementations to be shared by both interfaces. Finally, change the copy of the uobj->id to sign extend into a s64, so eg, if we ever wish to return a negative value for a FD it is carried properly. This ensures that userspace values are never weirdly interpreted due to the various trunctations and everything that is really out of range gets an EINVAL. Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
Diffstat (limited to 'drivers/infiniband/core/uverbs_ioctl.c')
-rw-r--r--drivers/infiniband/core/uverbs_ioctl.c16
1 files changed, 9 insertions, 7 deletions
diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c
index d3bf82cfaa2b..26ddc5cadcdb 100644
--- a/drivers/infiniband/core/uverbs_ioctl.c
+++ b/drivers/infiniband/core/uverbs_ioctl.c
@@ -136,15 +136,11 @@ static int uverbs_process_attr(struct ib_uverbs_file *ufile,
break;
case UVERBS_ATTR_TYPE_IDR:
- if (uattr->data >> 32)
- return -EINVAL;
- /* fall through */
case UVERBS_ATTR_TYPE_FD:
if (uattr->attr_data.reserved)
return -EINVAL;
- if (uattr->len != 0 || !ufile->ucontext ||
- uattr->data > INT_MAX)
+ if (uattr->len != 0 || !ufile->ucontext)
return -EINVAL;
o_attr = &e->obj_attr;
@@ -152,17 +148,23 @@ static int uverbs_process_attr(struct ib_uverbs_file *ufile,
if (!object)
return -EINVAL;
+ /*
+ * The type of uattr->data is u64 for UVERBS_ATTR_TYPE_IDR and
+ * s64 for UVERBS_ATTR_TYPE_FD. We can cast the u64 to s64
+ * here without caring about truncation as we know that the
+ * IDR implementation today rejects negative IDs
+ */
o_attr->uobject = uverbs_get_uobject_from_file(
object->type_attrs,
ufile,
spec->u.obj.access,
- (int)uattr->data);
+ uattr->data_s64);
if (IS_ERR(o_attr->uobject))
return PTR_ERR(o_attr->uobject);
if (spec->u.obj.access == UVERBS_ACCESS_NEW) {
- u64 id = o_attr->uobject->id;
+ s64 id = o_attr->uobject->id;
/* Copy the allocated id to the user-space */
if (put_user(id, &e->uattr->data)) {