diff options
-rw-r--r-- | block/scsi_ioctl.c | 46 | ||||
-rw-r--r-- | drivers/scsi/sd.c | 11 | ||||
-rw-r--r-- | include/linux/blkdev.h | 1 |
3 files changed, 56 insertions, 2 deletions
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 29dc026ca31..0b592df2068 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -654,9 +654,55 @@ int scsi_cmd_ioctl(struct file *file, struct request_queue *q, EXPORT_SYMBOL(scsi_cmd_ioctl); +int scsi_verify_blk_ioctl(struct block_device *bd, unsigned int cmd) +{ + if (bd && bd == bd->bd_contains) + return 0; + + /* Actually none of these is particularly useful on a partition, + * but they are safe. + */ + switch (cmd) { + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_GET_PCI: + case SCSI_IOCTL_PROBE_HOST: + case SG_GET_VERSION_NUM: + case SG_SET_TIMEOUT: + case SG_GET_TIMEOUT: + case SG_GET_RESERVED_SIZE: + case SG_SET_RESERVED_SIZE: + case SG_EMULATED_HOST: + return 0; + case CDROM_GET_CAPABILITY: + /* Keep this until we remove the printk below. udev sends it + * and we do not want to spam dmesg about it. CD-ROMs do + * not have partitions, so we get here only for disks. + */ + return -ENOTTY; + default: + break; + } + + /* In particular, rule out all resets and host-specific ioctls. */ + if (printk_ratelimit()) + printk(KERN_WARNING + "%s: sending ioctl %x to a partition!\n", current->comm, cmd); + + return capable(CAP_SYS_RAWIO) ? 0 : -ENOTTY; +} +EXPORT_SYMBOL(scsi_verify_blk_ioctl); + + int scsi_cmd_blk_ioctl(struct file *file, struct block_device *bd, unsigned int cmd, void __user *arg) { + int ret; + + ret = scsi_verify_blk_ioctl(bd, cmd); + if (ret < 0) + return ret; + return scsi_cmd_ioctl(file, bd->bd_disk->queue, bd->bd_disk, cmd, arg); } EXPORT_SYMBOL(scsi_cmd_blk_ioctl); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 92fb11478b0..c4d6a9ce84d 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -749,6 +749,10 @@ static int sd_ioctl(struct inode * inode, struct file * filp, SCSI_LOG_IOCTL(1, printk("sd_ioctl: disk=%s, cmd=0x%x\n", disk->disk_name, cmd)); + error = scsi_verify_blk_ioctl(bdev, cmd); + if (error < 0) + return error; + /* * If we are in the middle of error recovery, don't let anyone * else try and use this device. Also, if error recovery fails, it @@ -927,6 +931,11 @@ static long sd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a struct block_device *bdev = file->f_path.dentry->d_inode->i_bdev; struct gendisk *disk = bdev->bd_disk; struct scsi_device *sdev = scsi_disk(disk)->device; + int ret; + + ret = scsi_verify_blk_ioctl(bdev, cmd); + if (ret < 0) + return -ENOIOCTLCMD; /* * If we are in the middle of error recovery, don't let anyone @@ -938,8 +947,6 @@ static long sd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a return -ENODEV; if (sdev->host->hostt->compat_ioctl) { - int ret; - ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); return ret; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 1a20bb0d43b..a5ceea04dd8 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -669,6 +669,7 @@ extern void blk_plug_device(struct request_queue *); extern void blk_plug_device_unlocked(struct request_queue *); extern int blk_remove_plug(struct request_queue *); extern void blk_recount_segments(struct request_queue *, struct bio *); +extern int scsi_verify_blk_ioctl(struct block_device *, unsigned int); extern int scsi_cmd_ioctl(struct file *, struct request_queue *, struct gendisk *, unsigned int, void __user *); extern int scsi_cmd_blk_ioctl(struct file *, struct block_device *, |