floppy: fix out-of-bounds read in next_valid_format [Linux 4.4.187]

This Linux kernel change "floppy: fix out-of-bounds read in next_valid_format" is included in the Linux 4.4.187 release. This change is authored by Denis Efremov <efremov [at] ispras.ru> on Fri Jul 12 21:55:21 2019 +0300. The commit for this change in Linux stable tree is 350de13 (patch) which is from upstream commit 5635f89. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 5635f89.

floppy: fix out-of-bounds read in next_valid_format

[ Upstream commit 5635f897ed83fd539df78e98ba69ee91592f9bb8 ]

This fixes a global out-of-bounds read access in the next_valid_format
function of the floppy driver.

The values from autodetect field of the struct floppy_drive_params are
used as indices for the floppy_type array in the next_valid_format
function 'floppy_type[DP->autodetect[probed_format]].sect'.

To trigger the bug, one could use a value out of range and set the drive
parameters with the FDSETDRVPRM ioctl.  A floppy disk is not required to
be inserted.

CAP_SYS_ADMIN is required to call FDSETDRVPRM.

The patch adds the check for values of the autodetect field to be in the
'0 <= x < ARRAY_SIZE(floppy_type)' range of the floppy_type array indices.

The bug was found by syzkaller.

Signed-off-by: Denis Efremov <efremov@ispras.ru>
Tested-by: Willy Tarreau <w@1wt.eu>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

There are 18 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/block/floppy.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index e7958ab..59e2adb 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -3384,6 +3384,20 @@ static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
    return 0;
 }

+static bool valid_floppy_drive_params(const short autodetect[8])
+{
+   size_t floppy_type_size = ARRAY_SIZE(floppy_type);
+   size_t i = 0;
+
+   for (i = 0; i < 8; ++i) {
+       if (autodetect[i] < 0 ||
+           autodetect[i] >= floppy_type_size)
+           return false;
+   }
+
+   return true;
+}
+
 static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
            unsigned long param)
 {
@@ -3510,6 +3524,8 @@ static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
        SUPBOUND(size, strlen((const char *)outparam) + 1);
        break;
    case FDSETDRVPRM:
+       if (!valid_floppy_drive_params(inparam.dp.autodetect))
+           return -EINVAL;
        *UDP = inparam.dp;
        break;
    case FDGETDRVPRM:
@@ -3707,6 +3723,8 @@ static int compat_setdrvprm(int drive,
        return -EPERM;
    if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params)))
        return -EFAULT;
+   if (!valid_floppy_drive_params(v.autodetect))
+       return -EINVAL;
    mutex_lock(&floppy_mutex);
    UDP->cmos = v.cmos;
    UDP->max_dtr = v.max_dtr;

Leave a Reply

Your email address will not be published. Required fields are marked *