Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89d9e8d3 authored by Matan Barak's avatar Matan Barak Committed by Jason Gunthorpe
Browse files

IB/uverbs: Always use the attribute size provided by the user



This fixes several bugs around the copy_to/from user path:
 - copy_to used the user provided size of the attribute
   and could copy data beyond the end of the kernel buffer into
   userspace.
 - copy_from didn't know the size of the kernel buffer and
   could have left kernel memory unexpectedly un-initialized.
 - copy_from did not use the user length to determine if the
   attribute data is inlined or not.

Signed-off-by: default avatarMatan Barak <matanb@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leon@kernel.org>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 415bb699
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -323,7 +323,8 @@ static int uverbs_create_cq_handler(struct ib_device *ib_dev,
	cq->res.type = RDMA_RESTRACK_CQ;
	cq->res.type = RDMA_RESTRACK_CQ;
	rdma_restrack_add(&cq->res);
	rdma_restrack_add(&cq->res);


	ret = uverbs_copy_to(attrs, CREATE_CQ_RESP_CQE, &cq->cqe);
	ret = uverbs_copy_to(attrs, CREATE_CQ_RESP_CQE, &cq->cqe,
			     sizeof(cq->cqe));
	if (ret)
	if (ret)
		goto err_cq;
		goto err_cq;


@@ -375,7 +376,7 @@ static int uverbs_destroy_cq_handler(struct ib_device *ib_dev,
	resp.comp_events_reported  = obj->comp_events_reported;
	resp.comp_events_reported  = obj->comp_events_reported;
	resp.async_events_reported = obj->async_events_reported;
	resp.async_events_reported = obj->async_events_reported;


	return uverbs_copy_to(attrs, DESTROY_CQ_RESP, &resp);
	return uverbs_copy_to(attrs, DESTROY_CQ_RESP, &resp, sizeof(resp));
}
}


static DECLARE_UVERBS_METHOD(
static DECLARE_UVERBS_METHOD(
+28 −7
Original line number Original line Diff line number Diff line
@@ -351,29 +351,50 @@ static inline const struct uverbs_attr *uverbs_attr_get(const struct uverbs_attr
}
}


static inline int uverbs_copy_to(const struct uverbs_attr_bundle *attrs_bundle,
static inline int uverbs_copy_to(const struct uverbs_attr_bundle *attrs_bundle,
				 size_t idx, const void *from)
				 size_t idx, const void *from, size_t size)
{
{
	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
	u16 flags;
	u16 flags;
	size_t min_size;


	if (IS_ERR(attr))
	if (IS_ERR(attr))
		return PTR_ERR(attr);
		return PTR_ERR(attr);


	min_size = min_t(size_t, attr->ptr_attr.len, size);
	if (copy_to_user(attr->ptr_attr.ptr, from, min_size))
		return -EFAULT;

	flags = attr->ptr_attr.flags | UVERBS_ATTR_F_VALID_OUTPUT;
	flags = attr->ptr_attr.flags | UVERBS_ATTR_F_VALID_OUTPUT;
	return (!copy_to_user(attr->ptr_attr.ptr, from, attr->ptr_attr.len) &&
	if (put_user(flags, &attr->uattr->flags))
		!put_user(flags, &attr->uattr->flags)) ? 0 : -EFAULT;
		return -EFAULT;

	return 0;
}
}


static inline int _uverbs_copy_from(void *to, size_t to_size,
static inline bool uverbs_attr_ptr_is_inline(const struct uverbs_attr *attr)
{
	return attr->ptr_attr.len <= sizeof(attr->ptr_attr.data);
}

static inline int _uverbs_copy_from(void *to,
				    const struct uverbs_attr_bundle *attrs_bundle,
				    const struct uverbs_attr_bundle *attrs_bundle,
				    size_t idx)
				    size_t idx,
				    size_t size)
{
{
	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);


	if (IS_ERR(attr))
	if (IS_ERR(attr))
		return PTR_ERR(attr);
		return PTR_ERR(attr);


	if (to_size <= sizeof(((struct ib_uverbs_attr *)0)->data))
	/*
	 * Validation ensures attr->ptr_attr.len >= size. If the caller is
	 * using UVERBS_ATTR_SPEC_F_MIN_SZ then it must call copy_from with
	 * the right size.
	 */
	if (unlikely(size < attr->ptr_attr.len))
		return -EINVAL;

	if (uverbs_attr_ptr_is_inline(attr))
		memcpy(to, &attr->ptr_attr.data, attr->ptr_attr.len);
		memcpy(to, &attr->ptr_attr.data, attr->ptr_attr.len);
	else if (copy_from_user(to, attr->ptr_attr.ptr, attr->ptr_attr.len))
	else if (copy_from_user(to, attr->ptr_attr.ptr, attr->ptr_attr.len))
		return -EFAULT;
		return -EFAULT;
@@ -382,7 +403,7 @@ static inline int _uverbs_copy_from(void *to, size_t to_size,
}
}


#define uverbs_copy_from(to, attrs_bundle, idx)				      \
#define uverbs_copy_from(to, attrs_bundle, idx)				      \
	_uverbs_copy_from(to, sizeof(*(to)), attrs_bundle, idx)
	_uverbs_copy_from(to, attrs_bundle, idx, sizeof(*to))


/* =================================================
/* =================================================
 *	 Definitions -> Specs infrastructure
 *	 Definitions -> Specs infrastructure