[PATCH rdma-next 05/21] IB/uverbs: Add validations in attr_get and copy_{to,from} functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Matan Barak <matanb@xxxxxxxxxxxx>

Add validations to make sure the kernel developers used the correct
functions. We'll WARN_ON if [s]he doesn't.

Signed-off-by: Matan Barak <matanb@xxxxxxxxxxxx>
Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxxxx>
---
 drivers/infiniband/core/uverbs_ioctl.c |  1 +
 include/rdma/uverbs_ioctl.h            | 66 ++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)

diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c
index 8c93970dc8f1..1b030ccbde78 100644
--- a/drivers/infiniband/core/uverbs_ioctl.c
+++ b/drivers/infiniband/core/uverbs_ioctl.c
@@ -255,6 +255,7 @@ static int uverbs_handle_method(struct ib_uverbs_attr __user *uattr_ptr,
 	if (num_given_buckets <= 0)
 		return -EINVAL;
 
+	attr_bundle->method = method_spec;
 	attr_bundle->num_buckets = num_given_buckets;
 	ret = uverbs_validate_kernel_mandatory(method_spec, attr_bundle);
 	if (ret)
diff --git a/include/rdma/uverbs_ioctl.h b/include/rdma/uverbs_ioctl.h
index 86b3a780bbef..64b5f336cace 100644
--- a/include/rdma/uverbs_ioctl.h
+++ b/include/rdma/uverbs_ioctl.h
@@ -362,6 +362,7 @@ struct uverbs_attr_bundle_hash {
 };
 
 struct uverbs_attr_bundle {
+	const struct uverbs_method_spec	*method;
 	size_t				num_buckets;
 	struct uverbs_attr_bundle_hash  hash[];
 };
@@ -426,14 +427,28 @@ static inline const struct uverbs_attr *uverbs_attr_get(const struct uverbs_attr
 	return &attrs_bundle->hash[idx_bucket].attrs[idx & ~UVERBS_ID_NS_MASK];
 }
 
+#define uverbs_validate_spec_debug(_method, _idx, _name, _cond) ({\
+	const struct uverbs_attr_spec *_name;			  \
+								  \
+	_name = uverbs_get_spec(_method, idx);			  \
+	WARN_ON(IS_ERR(_name)) ? PTR_ERR(_name) :		  \
+		WARN_ON(!(_cond)) ? -EINVAL : 0;		  \
+})
+
 static inline int uverbs_attr_get_enum_id(const struct uverbs_attr_bundle *attrs_bundle,
 					  u16 idx)
 {
 	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
+	int ret;
 
 	if (IS_ERR(attr))
 		return PTR_ERR(attr);
 
+	ret = uverbs_validate_spec_debug(attrs_bundle->method, idx, spec,
+					 spec->type == UVERBS_ATTR_TYPE_ENUM_IN);
+	if (ret)
+		return ret;
+
 	return attr->ptr_attr.enum_id;
 }
 
@@ -449,16 +464,53 @@ static inline void *uverbs_attr_get_obj(const struct uverbs_attr_bundle *attrs_b
 	return uobj->object;
 }
 
+static inline const struct uverbs_attr_spec *
+uverbs_get_actual_spec(const struct uverbs_attr_bundle *attrs_bundle, u16 idx)
+{
+	const struct uverbs_attr_spec *spec =
+		uverbs_get_spec(attrs_bundle->method, idx);
+	int enum_id;
+
+	if (IS_ERR(spec))
+		return spec;
+
+	if (likely(spec->type != UVERBS_ATTR_TYPE_ENUM_IN))
+		return spec;
+
+	enum_id = uverbs_attr_get_enum_id(attrs_bundle, idx);
+	if (enum_id < 0)
+		return ERR_PTR(enum_id);
+
+	/* We already know that enum_id is correct, because we didn't fail at
+	 * the parsing stage.
+	 */
+	return &spec->enum_def.ids[enum_id];
+}
+
+#define uverbs_validate_actual_spec_debug(_method, _idx, _name, _cond) ({\
+	const struct uverbs_attr_spec *_name;			  \
+								  \
+	_name = uverbs_get_actual_spec(_method, idx);		  \
+	WARN_ON(IS_ERR(_name)) ? PTR_ERR(_name) :		  \
+		WARN_ON(!(_cond)) ? -EINVAL : 0;		  \
+})
+
 static inline int uverbs_copy_to(const struct uverbs_attr_bundle *attrs_bundle,
 				 size_t idx, const void *from, size_t size)
 {
 	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
 	u16 flags;
 	size_t min_size;
+	int ret;
 
 	if (IS_ERR(attr))
 		return PTR_ERR(attr);
 
+	ret = uverbs_validate_actual_spec_debug(attrs_bundle, idx, spec,
+						spec->type == UVERBS_ATTR_TYPE_PTR_OUT);
+	if (ret)
+		return ret;
+
 	min_size = min_t(size_t, attr->ptr_attr.len, size);
 	if (copy_to_user(u64_to_user_ptr(attr->ptr_attr.data), from, min_size))
 		return -EFAULT;
@@ -481,10 +533,17 @@ static inline int _uverbs_copy_from(void *to,
 				    size_t size)
 {
 	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
+	int ret;
 
 	if (IS_ERR(attr))
 		return PTR_ERR(attr);
 
+	ret = uverbs_validate_actual_spec_debug(attrs_bundle, idx, spec,
+						spec->type == UVERBS_ATTR_TYPE_PTR_IN &&
+						spec->flags & ~UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO);
+	if (ret)
+		return ret;
+
 	/*
 	 * Validation ensures attr->ptr_attr.len >= size. If the caller is
 	 * using UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO then it must call
@@ -509,10 +568,17 @@ static inline int _uverbs_copy_from_or_zero(void *to,
 {
 	const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
 	size_t min_size;
+	int ret;
 
 	if (IS_ERR(attr))
 		return PTR_ERR(attr);
 
+	ret = uverbs_validate_actual_spec_debug(attrs_bundle, idx, spec,
+						spec->type == UVERBS_ATTR_TYPE_PTR_IN &&
+						spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO);
+	if (ret)
+		return ret;
+
 	min_size = min_t(size_t, size, attr->ptr_attr.len);
 
 	if (uverbs_attr_ptr_is_inline(attr))
-- 
2.14.3

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Yosemite Photos]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux