linux/drivers/infiniband/core/uverbs_ioctl.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2017, Mellanox Technologies inc.  All rights reserved.
   3 *
   4 * This software is available to you under a choice of one of two
   5 * licenses.  You may choose to be licensed under the terms of the GNU
   6 * General Public License (GPL) Version 2, available from the file
   7 * COPYING in the main directory of this source tree, or the
   8 * OpenIB.org BSD license below:
   9 *
  10 *     Redistribution and use in source and binary forms, with or
  11 *     without modification, are permitted provided that the following
  12 *     conditions are met:
  13 *
  14 *      - Redistributions of source code must retain the above
  15 *        copyright notice, this list of conditions and the following
  16 *        disclaimer.
  17 *
  18 *      - Redistributions in binary form must reproduce the above
  19 *        copyright notice, this list of conditions and the following
  20 *        disclaimer in the documentation and/or other materials
  21 *        provided with the distribution.
  22 *
  23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30 * SOFTWARE.
  31 */
  32
  33#include <rdma/rdma_user_ioctl.h>
  34#include <rdma/uverbs_ioctl.h>
  35#include "rdma_core.h"
  36#include "uverbs.h"
  37
  38static bool uverbs_is_attr_cleared(const struct ib_uverbs_attr *uattr,
  39                                   u16 len)
  40{
  41        if (uattr->len > sizeof(((struct ib_uverbs_attr *)0)->data))
  42                return ib_is_buffer_cleared(u64_to_user_ptr(uattr->data) + len,
  43                                            uattr->len - len);
  44
  45        return !memchr_inv((const void *)&uattr->data + len,
  46                           0, uattr->len - len);
  47}
  48
  49static int uverbs_process_attr(struct ib_device *ibdev,
  50                               struct ib_ucontext *ucontext,
  51                               const struct ib_uverbs_attr *uattr,
  52                               u16 attr_id,
  53                               const struct uverbs_attr_spec_hash *attr_spec_bucket,
  54                               struct uverbs_attr_bundle_hash *attr_bundle_h,
  55                               struct ib_uverbs_attr __user *uattr_ptr)
  56{
  57        const struct uverbs_attr_spec *spec;
  58        const struct uverbs_attr_spec *val_spec;
  59        struct uverbs_attr *e;
  60        const struct uverbs_object_spec *object;
  61        struct uverbs_obj_attr *o_attr;
  62        struct uverbs_attr *elements = attr_bundle_h->attrs;
  63
  64        if (attr_id >= attr_spec_bucket->num_attrs) {
  65                if (uattr->flags & UVERBS_ATTR_F_MANDATORY)
  66                        return -EINVAL;
  67                else
  68                        return 0;
  69        }
  70
  71        if (test_bit(attr_id, attr_bundle_h->valid_bitmap))
  72                return -EINVAL;
  73
  74        spec = &attr_spec_bucket->attrs[attr_id];
  75        val_spec = spec;
  76        e = &elements[attr_id];
  77        e->uattr = uattr_ptr;
  78
  79        switch (spec->type) {
  80        case UVERBS_ATTR_TYPE_ENUM_IN:
  81                if (uattr->attr_data.enum_data.elem_id >= spec->enum_def.num_elems)
  82                        return -EOPNOTSUPP;
  83
  84                if (uattr->attr_data.enum_data.reserved)
  85                        return -EINVAL;
  86
  87                val_spec = &spec->enum_def.ids[uattr->attr_data.enum_data.elem_id];
  88
  89                /* Currently we only support PTR_IN based enums */
  90                if (val_spec->type != UVERBS_ATTR_TYPE_PTR_IN)
  91                        return -EOPNOTSUPP;
  92
  93                e->ptr_attr.enum_id = uattr->attr_data.enum_data.elem_id;
  94        /* fall through */
  95        case UVERBS_ATTR_TYPE_PTR_IN:
  96                /* Ensure that any data provided by userspace beyond the known
  97                 * struct is zero. Userspace that knows how to use some future
  98                 * longer struct will fail here if used with an old kernel and
  99                 * non-zero content, making ABI compat/discovery simpler.
 100                 */
 101                if (uattr->len > val_spec->ptr.len &&
 102                    val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO &&
 103                    !uverbs_is_attr_cleared(uattr, val_spec->ptr.len))
 104                        return -EOPNOTSUPP;
 105
 106        /* fall through */
 107        case UVERBS_ATTR_TYPE_PTR_OUT:
 108                if (uattr->len < val_spec->ptr.min_len ||
 109                    (!(val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) &&
 110                     uattr->len > val_spec->ptr.len))
 111                        return -EINVAL;
 112
 113                if (spec->type != UVERBS_ATTR_TYPE_ENUM_IN &&
 114                    uattr->attr_data.reserved)
 115                        return -EINVAL;
 116
 117                e->ptr_attr.data = uattr->data;
 118                e->ptr_attr.len = uattr->len;
 119                e->ptr_attr.flags = uattr->flags;
 120                break;
 121
 122        case UVERBS_ATTR_TYPE_IDR:
 123                if (uattr->data >> 32)
 124                        return -EINVAL;
 125        /* fall through */
 126        case UVERBS_ATTR_TYPE_FD:
 127                if (uattr->attr_data.reserved)
 128                        return -EINVAL;
 129
 130                if (uattr->len != 0 || !ucontext || uattr->data > INT_MAX)
 131                        return -EINVAL;
 132
 133                o_attr = &e->obj_attr;
 134                object = uverbs_get_object(ibdev, spec->obj.obj_type);
 135                if (!object)
 136                        return -EINVAL;
 137                o_attr->type = object->type_attrs;
 138
 139                o_attr->id = (int)uattr->data;
 140                o_attr->uobject = uverbs_get_uobject_from_context(
 141                                        o_attr->type,
 142                                        ucontext,
 143                                        spec->obj.access,
 144                                        o_attr->id);
 145
 146                if (IS_ERR(o_attr->uobject))
 147                        return PTR_ERR(o_attr->uobject);
 148
 149                if (spec->obj.access == UVERBS_ACCESS_NEW) {
 150                        u64 id = o_attr->uobject->id;
 151
 152                        /* Copy the allocated id to the user-space */
 153                        if (put_user(id, &e->uattr->data)) {
 154                                uverbs_finalize_object(o_attr->uobject,
 155                                                       UVERBS_ACCESS_NEW,
 156                                                       false);
 157                                return -EFAULT;
 158                        }
 159                }
 160
 161                break;
 162        default:
 163                return -EOPNOTSUPP;
 164        }
 165
 166        set_bit(attr_id, attr_bundle_h->valid_bitmap);
 167        return 0;
 168}
 169
 170static int uverbs_uattrs_process(struct ib_device *ibdev,
 171                                 struct ib_ucontext *ucontext,
 172                                 const struct ib_uverbs_attr *uattrs,
 173                                 size_t num_uattrs,
 174                                 const struct uverbs_method_spec *method,
 175                                 struct uverbs_attr_bundle *attr_bundle,
 176                                 struct ib_uverbs_attr __user *uattr_ptr)
 177{
 178        size_t i;
 179        int ret = 0;
 180        int num_given_buckets = 0;
 181
 182        for (i = 0; i < num_uattrs; i++) {
 183                const struct ib_uverbs_attr *uattr = &uattrs[i];
 184                u16 attr_id = uattr->attr_id;
 185                struct uverbs_attr_spec_hash *attr_spec_bucket;
 186
 187                ret = uverbs_ns_idx(&attr_id, method->num_buckets);
 188                if (ret < 0) {
 189                        if (uattr->flags & UVERBS_ATTR_F_MANDATORY) {
 190                                uverbs_finalize_objects(attr_bundle,
 191                                                        method->attr_buckets,
 192                                                        num_given_buckets,
 193                                                        false);
 194                                return ret;
 195                        }
 196                        continue;
 197                }
 198
 199                /*
 200                 * ret is the found ns, so increase num_given_buckets if
 201                 * necessary.
 202                 */
 203                if (ret >= num_given_buckets)
 204                        num_given_buckets = ret + 1;
 205
 206                attr_spec_bucket = method->attr_buckets[ret];
 207                ret = uverbs_process_attr(ibdev, ucontext, uattr, attr_id,
 208                                          attr_spec_bucket, &attr_bundle->hash[ret],
 209                                          uattr_ptr++);
 210                if (ret) {
 211                        uverbs_finalize_objects(attr_bundle,
 212                                                method->attr_buckets,
 213                                                num_given_buckets,
 214                                                false);
 215                        return ret;
 216                }
 217        }
 218
 219        return num_given_buckets;
 220}
 221
 222static int uverbs_validate_kernel_mandatory(const struct uverbs_method_spec *method_spec,
 223                                            struct uverbs_attr_bundle *attr_bundle)
 224{
 225        unsigned int i;
 226
 227        for (i = 0; i < attr_bundle->num_buckets; i++) {
 228                struct uverbs_attr_spec_hash *attr_spec_bucket =
 229                        method_spec->attr_buckets[i];
 230
 231                if (!bitmap_subset(attr_spec_bucket->mandatory_attrs_bitmask,
 232                                   attr_bundle->hash[i].valid_bitmap,
 233                                   attr_spec_bucket->num_attrs))
 234                        return -EINVAL;
 235        }
 236
 237        for (; i < method_spec->num_buckets; i++) {
 238                struct uverbs_attr_spec_hash *attr_spec_bucket =
 239                        method_spec->attr_buckets[i];
 240
 241                if (!bitmap_empty(attr_spec_bucket->mandatory_attrs_bitmask,
 242                                  attr_spec_bucket->num_attrs))
 243                        return -EINVAL;
 244        }
 245
 246        return 0;
 247}
 248
 249static int uverbs_handle_method(struct ib_uverbs_attr __user *uattr_ptr,
 250                                const struct ib_uverbs_attr *uattrs,
 251                                size_t num_uattrs,
 252                                struct ib_device *ibdev,
 253                                struct ib_uverbs_file *ufile,
 254                                const struct uverbs_method_spec *method_spec,
 255                                struct uverbs_attr_bundle *attr_bundle)
 256{
 257        int ret;
 258        int finalize_ret;
 259        int num_given_buckets;
 260
 261        num_given_buckets = uverbs_uattrs_process(ibdev, ufile->ucontext, uattrs,
 262                                                  num_uattrs, method_spec,
 263                                                  attr_bundle, uattr_ptr);
 264        if (num_given_buckets <= 0)
 265                return -EINVAL;
 266
 267        attr_bundle->num_buckets = num_given_buckets;
 268        ret = uverbs_validate_kernel_mandatory(method_spec, attr_bundle);
 269        if (ret)
 270                goto cleanup;
 271
 272        ret = method_spec->handler(ibdev, ufile, attr_bundle);
 273cleanup:
 274        finalize_ret = uverbs_finalize_objects(attr_bundle,
 275                                               method_spec->attr_buckets,
 276                                               attr_bundle->num_buckets,
 277                                               !ret);
 278
 279        return ret ? ret : finalize_ret;
 280}
 281
 282#define UVERBS_OPTIMIZE_USING_STACK_SZ  256
 283static long ib_uverbs_cmd_verbs(struct ib_device *ib_dev,
 284                                struct ib_uverbs_file *file,
 285                                struct ib_uverbs_ioctl_hdr *hdr,
 286                                void __user *buf)
 287{
 288        const struct uverbs_object_spec *object_spec;
 289        const struct uverbs_method_spec *method_spec;
 290        long err = 0;
 291        unsigned int i;
 292        struct {
 293                struct ib_uverbs_attr           *uattrs;
 294                struct uverbs_attr_bundle       *uverbs_attr_bundle;
 295        } *ctx = NULL;
 296        struct uverbs_attr *curr_attr;
 297        unsigned long *curr_bitmap;
 298        size_t ctx_size;
 299        uintptr_t data[UVERBS_OPTIMIZE_USING_STACK_SZ / sizeof(uintptr_t)];
 300
 301        if (hdr->driver_id != ib_dev->driver_id)
 302                return -EINVAL;
 303
 304        object_spec = uverbs_get_object(ib_dev, hdr->object_id);
 305        if (!object_spec)
 306                return -EPROTONOSUPPORT;
 307
 308        method_spec = uverbs_get_method(object_spec, hdr->method_id);
 309        if (!method_spec)
 310                return -EPROTONOSUPPORT;
 311
 312        if ((method_spec->flags & UVERBS_ACTION_FLAG_CREATE_ROOT) ^ !file->ucontext)
 313                return -EINVAL;
 314
 315        ctx_size = sizeof(*ctx) +
 316                   sizeof(struct uverbs_attr_bundle) +
 317                   sizeof(struct uverbs_attr_bundle_hash) * method_spec->num_buckets +
 318                   sizeof(*ctx->uattrs) * hdr->num_attrs +
 319                   sizeof(*ctx->uverbs_attr_bundle->hash[0].attrs) *
 320                   method_spec->num_child_attrs +
 321                   sizeof(*ctx->uverbs_attr_bundle->hash[0].valid_bitmap) *
 322                        (method_spec->num_child_attrs / BITS_PER_LONG +
 323                         method_spec->num_buckets);
 324
 325        if (ctx_size <= UVERBS_OPTIMIZE_USING_STACK_SZ)
 326                ctx = (void *)data;
 327        if (!ctx)
 328                ctx = kmalloc(ctx_size, GFP_KERNEL);
 329        if (!ctx)
 330                return -ENOMEM;
 331
 332        ctx->uverbs_attr_bundle = (void *)ctx + sizeof(*ctx);
 333        ctx->uattrs = (void *)(ctx->uverbs_attr_bundle + 1) +
 334                              (sizeof(ctx->uverbs_attr_bundle->hash[0]) *
 335                               method_spec->num_buckets);
 336        curr_attr = (void *)(ctx->uattrs + hdr->num_attrs);
 337        curr_bitmap = (void *)(curr_attr + method_spec->num_child_attrs);
 338
 339        /*
 340         * We just fill the pointers and num_attrs here. The data itself will be
 341         * filled at a later stage (uverbs_process_attr)
 342         */
 343        for (i = 0; i < method_spec->num_buckets; i++) {
 344                unsigned int curr_num_attrs = method_spec->attr_buckets[i]->num_attrs;
 345
 346                ctx->uverbs_attr_bundle->hash[i].attrs = curr_attr;
 347                curr_attr += curr_num_attrs;
 348                ctx->uverbs_attr_bundle->hash[i].num_attrs = curr_num_attrs;
 349                ctx->uverbs_attr_bundle->hash[i].valid_bitmap = curr_bitmap;
 350                bitmap_zero(curr_bitmap, curr_num_attrs);
 351                curr_bitmap += BITS_TO_LONGS(curr_num_attrs);
 352        }
 353
 354        err = copy_from_user(ctx->uattrs, buf,
 355                             sizeof(*ctx->uattrs) * hdr->num_attrs);
 356        if (err) {
 357                err = -EFAULT;
 358                goto out;
 359        }
 360
 361        err = uverbs_handle_method(buf, ctx->uattrs, hdr->num_attrs, ib_dev,
 362                                   file, method_spec, ctx->uverbs_attr_bundle);
 363
 364        /*
 365         * EPROTONOSUPPORT is ONLY to be returned if the ioctl framework can
 366         * not invoke the method because the request is not supported.  No
 367         * other cases should return this code.
 368        */
 369        if (unlikely(err == -EPROTONOSUPPORT)) {
 370                WARN_ON_ONCE(err == -EPROTONOSUPPORT);
 371                err = -EINVAL;
 372        }
 373out:
 374        if (ctx != (void *)data)
 375                kfree(ctx);
 376        return err;
 377}
 378
 379#define IB_UVERBS_MAX_CMD_SZ 4096
 380
 381long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 382{
 383        struct ib_uverbs_file *file = filp->private_data;
 384        struct ib_uverbs_ioctl_hdr __user *user_hdr =
 385                (struct ib_uverbs_ioctl_hdr __user *)arg;
 386        struct ib_uverbs_ioctl_hdr hdr;
 387        struct ib_device *ib_dev;
 388        int srcu_key;
 389        long err;
 390
 391        srcu_key = srcu_read_lock(&file->device->disassociate_srcu);
 392        ib_dev = srcu_dereference(file->device->ib_dev,
 393                                  &file->device->disassociate_srcu);
 394        if (!ib_dev) {
 395                err = -EIO;
 396                goto out;
 397        }
 398
 399        if (cmd == RDMA_VERBS_IOCTL) {
 400                err = copy_from_user(&hdr, user_hdr, sizeof(hdr));
 401
 402                if (err || hdr.length > IB_UVERBS_MAX_CMD_SZ ||
 403                    hdr.length != sizeof(hdr) + hdr.num_attrs * sizeof(struct ib_uverbs_attr)) {
 404                        err = -EINVAL;
 405                        goto out;
 406                }
 407
 408                if (hdr.reserved1 || hdr.reserved2) {
 409                        err = -EPROTONOSUPPORT;
 410                        goto out;
 411                }
 412
 413                err = ib_uverbs_cmd_verbs(ib_dev, file, &hdr,
 414                                          (__user void *)arg + sizeof(hdr));
 415        } else {
 416                err = -ENOIOCTLCMD;
 417        }
 418out:
 419        srcu_read_unlock(&file->device->disassociate_srcu, srcu_key);
 420
 421        return err;
 422}
 423