]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/vfio_ccw: Ensure index for read/write regions are within range
authorEric Farman <farman@linux.ibm.com>
Tue, 28 Jul 2026 03:30:18 +0000 (05:30 +0200)
committerChristian Borntraeger <borntraeger@linux.ibm.com>
Thu, 30 Jul 2026 17:50:24 +0000 (19:50 +0200)
The introduction of the capability chain rightly clamped the
region indexes to the range of the capabilities itself, but
neglected to do so for the existing read/write regions which
should also be enforced.

Fixes: db8e5d17ac03 ("vfio-ccw: add capabilities chain")
Cc: stable@vger.kernel.org
Cc: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Eric Farman <farman@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
drivers/s390/cio/vfio_ccw_async.c
drivers/s390/cio/vfio_ccw_chp.c
drivers/s390/cio/vfio_ccw_ops.c

index 420d89ba7f8387193a04470e560db32c38d543ee..4aff0b58fa5d56a03c3a96c68a65f32e791674ea 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/vfio.h>
+#include <linux/nospec.h>
 
 #include "vfio_ccw_private.h"
 
@@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private,
                return -EINVAL;
 
        mutex_lock(&private->io_mutex);
+
+       if (i >= private->num_regions) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       i = array_index_nospec(i, private->num_regions);
        region = private->region[i].data;
        if (copy_to_user(buf, (void *)region + pos, count))
                ret = -EFAULT;
        else
                ret = count;
+
+out_unlock:
        mutex_unlock(&private->io_mutex);
        return ret;
 }
@@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private,
        if (!mutex_trylock(&private->io_mutex))
                return -EAGAIN;
 
+       if (i >= private->num_regions) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       i = array_index_nospec(i, private->num_regions);
        region = private->region[i].data;
        if (copy_from_user((void *)region + pos, buf, count)) {
                ret = -EFAULT;
index 38c176cf6295724b60db5c27529a5a32b73350af..f3015132d4b5f768f7b2ad4eaacb33a7b8a9b550 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <linux/slab.h>
+#include <linux/nospec.h>
 #include <linux/vfio.h>
 #include "vfio_ccw_private.h"
 
@@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
                return -EINVAL;
 
        mutex_lock(&private->io_mutex);
+
+       if (i >= private->num_regions) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       i = array_index_nospec(i, private->num_regions);
        region = private->region[i].data;
 
        if (cio_update_schib(sch)) {
@@ -97,6 +105,12 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
                list_del(&crw->next);
 
        mutex_lock(&private->io_mutex);
+       if (i >= private->num_regions) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       i = array_index_nospec(i, private->num_regions);
        region = private->region[i].data;
 
        if (crw)
@@ -109,6 +123,7 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
 
        region->crw = 0;
 
+out:
        mutex_unlock(&private->io_mutex);
 
        kfree(crw);
index 45ec722d25ea696b2c14088de3c4378ff919d02b..032a1cdf4df7072b821672c65f6566cfdeb46564 100644 (file)
@@ -243,6 +243,7 @@ static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev,
                return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
        default:
                index -= VFIO_CCW_NUM_REGIONS;
+               index = array_index_nospec(index, private->num_regions);
                return private->region[index].ops->read(private, buf, count,
                                                        ppos);
        }
@@ -295,6 +296,7 @@ static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev,
                return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
        default:
                index -= VFIO_CCW_NUM_REGIONS;
+               index = array_index_nospec(index, private->num_regions);
                return private->region[index].ops->write(private, buf, count,
                                                         ppos);
        }
@@ -338,11 +340,8 @@ static int vfio_ccw_mdev_ioctl_get_region_info(struct vfio_device *vdev,
                    VFIO_CCW_NUM_REGIONS + private->num_regions)
                        return -EINVAL;
 
-               info->index = array_index_nospec(info->index,
-                                                VFIO_CCW_NUM_REGIONS +
-                                                private->num_regions);
-
                i = info->index - VFIO_CCW_NUM_REGIONS;
+               i = array_index_nospec(i, private->num_regions);
 
                info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
                info->size = private->region[i].size;