aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/core/uverbs_ioctl.c
diff options
context:
space:
mode:
authorMatan Barak <matanb@mellanox.com>2018-03-19 15:02:36 +0200
committerJason Gunthorpe <jgg@mellanox.com>2018-03-19 14:45:17 -0600
commitc66db31113948ba61682f55265df8d032e793fcc (patch)
tree826060ded95bbe44514fec94a1a54f87ba661caa /drivers/infiniband/core/uverbs_ioctl.c
parentIB/uverbs: Enable compact representation of uverbs_attr_spec (diff)
downloadlinux-dev-c66db31113948ba61682f55265df8d032e793fcc.tar.xz
linux-dev-c66db31113948ba61682f55265df8d032e793fcc.zip
IB/uverbs: Safely extend existing attributes
Previously, we've used UVERBS_ATTR_SPEC_F_MIN_SZ for extending existing attributes. The behavior of this flag was the kernel accepts anything bigger than the minimum size it specified. This is unsafe, since in order to safely extend an attribute, we need to make sure unknown size is zeroed. Replacing UVERBS_ATTR_SPEC_F_MIN_SZ with UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO, which essentially checks that the unknown size is zero. In addition, attributes are now decorated with UVERBS_ATTR_TYPE and UVERBS_ATTR_STRUCT, so we can provide the minimum and known length. Users of this flag needs to use copy_from_or_zero functions/macros. Reviewed-by: Yishai Hadas <yishaih@mellanox.com> Signed-off-by: Matan Barak <matanb@mellanox.com> Signed-off-by: Leon Romanovsky <leonro@mellanox.com> 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.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c
index 82a1775ba657..1e6bf2488584 100644
--- a/drivers/infiniband/core/uverbs_ioctl.c
+++ b/drivers/infiniband/core/uverbs_ioctl.c
@@ -35,6 +35,17 @@
#include "rdma_core.h"
#include "uverbs.h"
+static bool uverbs_is_attr_cleared(const struct ib_uverbs_attr *uattr,
+ u16 len)
+{
+ if (uattr->len > sizeof(((struct ib_uverbs_attr *)0)->data))
+ return ib_is_buffer_cleared(u64_to_user_ptr(uattr->data) + len,
+ uattr->len - len);
+
+ return !memchr_inv((const void *)&uattr->data + len,
+ 0, uattr->len - len);
+}
+
static int uverbs_process_attr(struct ib_device *ibdev,
struct ib_ucontext *ucontext,
const struct ib_uverbs_attr *uattr,
@@ -68,9 +79,20 @@ static int uverbs_process_attr(struct ib_device *ibdev,
switch (spec->type) {
case UVERBS_ATTR_TYPE_PTR_IN:
+ /* Ensure that any data provided by userspace beyond the known
+ * struct is zero. Userspace that knows how to use some future
+ * longer struct will fail here if used with an old kernel and
+ * non-zero content, making ABI compat/discovery simpler.
+ */
+ if (uattr->len > spec->ptr.len &&
+ spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO &&
+ !uverbs_is_attr_cleared(uattr, spec->ptr.len))
+ return -EOPNOTSUPP;
+
+ /* fall through */
case UVERBS_ATTR_TYPE_PTR_OUT:
- if (uattr->len < spec->ptr.len ||
- (!(spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ) &&
+ if (uattr->len < spec->ptr.min_len ||
+ (!(spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) &&
uattr->len > spec->ptr.len))
return -EINVAL;