]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/nfsd/blocklayoutxdr.c
nfsd: better layoutupdate bounds-checking
[mirror_ubuntu-bionic-kernel.git] / fs / nfsd / blocklayoutxdr.c
index 6d834dc9bbc826bf8b711fb4adfaf58c82ae5c48..6c3b316f932e94d993f34a054f88e0e7355ff8fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Christoph Hellwig.
+ * Copyright (c) 2014-2016 Christoph Hellwig.
  */
 #include <linux/sunrpc/svc.h>
 #include <linux/exportfs.h>
@@ -53,6 +53,18 @@ nfsd4_block_encode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
                p = xdr_encode_hyper(p, b->simple.offset);
                p = xdr_encode_opaque(p, b->simple.sig, b->simple.sig_len);
                break;
+       case PNFS_BLOCK_VOLUME_SCSI:
+               len = 4 + 4 + 4 + 4 + b->scsi.designator_len + 8;
+               p = xdr_reserve_space(xdr, len);
+               if (!p)
+                       return -ETOOSMALL;
+
+               *p++ = cpu_to_be32(b->type);
+               *p++ = cpu_to_be32(b->scsi.code_set);
+               *p++ = cpu_to_be32(b->scsi.designator_type);
+               p = xdr_encode_opaque(p, b->scsi.designator, b->scsi.designator_len);
+               p = xdr_encode_hyper(p, b->scsi.pr_key);
+               break;
        default:
                return -ENOTSUPP;
        }
@@ -93,18 +105,22 @@ nfsd4_block_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp,
                u32 block_size)
 {
        struct iomap *iomaps;
-       u32 nr_iomaps, expected, i;
+       u32 nr_iomaps, i;
 
        if (len < sizeof(u32)) {
                dprintk("%s: extent array too small: %u\n", __func__, len);
                return -EINVAL;
        }
+       len -= sizeof(u32);
+       if (len % PNFS_BLOCK_EXTENT_SIZE) {
+               dprintk("%s: extent array invalid: %u\n", __func__, len);
+               return -EINVAL;
+       }
 
        nr_iomaps = be32_to_cpup(p++);
-       expected = sizeof(__be32) + nr_iomaps * PNFS_BLOCK_EXTENT_SIZE;
-       if (len != expected) {
+       if (nr_iomaps != len / PNFS_BLOCK_EXTENT_SIZE) {
                dprintk("%s: extent array size mismatch: %u/%u\n",
-                       __func__, len, expected);
+                       __func__, len, nr_iomaps);
                return -EINVAL;
        }
 
@@ -155,3 +171,54 @@ fail:
        kfree(iomaps);
        return -EINVAL;
 }
+
+int
+nfsd4_scsi_decode_layoutupdate(__be32 *p, u32 len, struct iomap **iomapp,
+               u32 block_size)
+{
+       struct iomap *iomaps;
+       u32 nr_iomaps, expected, i;
+
+       if (len < sizeof(u32)) {
+               dprintk("%s: extent array too small: %u\n", __func__, len);
+               return -EINVAL;
+       }
+
+       nr_iomaps = be32_to_cpup(p++);
+       expected = sizeof(__be32) + nr_iomaps * PNFS_SCSI_RANGE_SIZE;
+       if (len != expected) {
+               dprintk("%s: extent array size mismatch: %u/%u\n",
+                       __func__, len, expected);
+               return -EINVAL;
+       }
+
+       iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
+       if (!iomaps) {
+               dprintk("%s: failed to allocate extent array\n", __func__);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < nr_iomaps; i++) {
+               u64 val;
+
+               p = xdr_decode_hyper(p, &val);
+               if (val & (block_size - 1)) {
+                       dprintk("%s: unaligned offset 0x%llx\n", __func__, val);
+                       goto fail;
+               }
+               iomaps[i].offset = val;
+
+               p = xdr_decode_hyper(p, &val);
+               if (val & (block_size - 1)) {
+                       dprintk("%s: unaligned length 0x%llx\n", __func__, val);
+                       goto fail;
+               }
+               iomaps[i].length = val;
+       }
+
+       *iomapp = iomaps;
+       return nr_iomaps;
+fail:
+       kfree(iomaps);
+       return -EINVAL;
+}