Skip to content

Commit 287e661

Browse files
arndbhtejun
authored andcommitted
libata: fix HDIO_GET_32BIT ioctl
As reported by Soohoon Lee, the HDIO_GET_32BIT ioctl does not work correctly in compat mode with libata. I have investigated the issue further and found multiple problems that all appeared with the same commit that originally introduced HDIO_GET_32BIT handling in libata back in linux-2.6.8 and presumably also linux-2.4, as the code uses "copy_to_user(arg, &val, 1)" to copy a 'long' variable containing either 0 or 1 to user space. The problems with this are: * On big-endian machines, this will always write a zero because it stores the wrong byte into user space. * In compat mode, the upper three bytes of the variable are updated by the compat_hdio_ioctl() function, but they now contain uninitialized stack data. * The hdparm tool calling this ioctl uses a 'static long' variable to store the result. This means at least the upper bytes are initialized to zero, but calling another ioctl like HDIO_GET_MULTCOUNT would fill them with data that remains stale when the low byte is overwritten. Fortunately libata doesn't implement any of the affected ioctl commands, so this would only happen when we query both an IDE and an ATA device in the same command such as "hdparm -N -c /dev/hda /dev/sda" * The libata code for unknown reasons started using ATA_IOC_GET_IO32 and ATA_IOC_SET_IO32 as aliases for HDIO_GET_32BIT and HDIO_SET_32BIT, while the ioctl commands that were added later use the normal HDIO_* names. This is harmless but rather confusing. This addresses all four issues by changing the code to use put_user() on an 'unsigned long' variable in HDIO_GET_32BIT, like the IDE subsystem does, and by clarifying the names of the ioctl commands. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Reported-by: Soohoon Lee <Soohoon.Lee@f5.com> Tested-by: Soohoon Lee <Soohoon.Lee@f5.com> Cc: stable@vger.kernel.org Signed-off-by: Tejun Heo <tj@kernel.org>
1 parent 32aea26 commit 287e661

File tree

2 files changed

+7
-8
lines changed

2 files changed

+7
-8
lines changed

drivers/ata/libata-scsi.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -675,19 +675,18 @@ static int ata_ioc32(struct ata_port *ap)
675675
int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *scsidev,
676676
int cmd, void __user *arg)
677677
{
678-
int val = -EINVAL, rc = -EINVAL;
678+
unsigned long val;
679+
int rc = -EINVAL;
679680
unsigned long flags;
680681

681682
switch (cmd) {
682-
case ATA_IOC_GET_IO32:
683+
case HDIO_GET_32BIT:
683684
spin_lock_irqsave(ap->lock, flags);
684685
val = ata_ioc32(ap);
685686
spin_unlock_irqrestore(ap->lock, flags);
686-
if (copy_to_user(arg, &val, 1))
687-
return -EFAULT;
688-
return 0;
687+
return put_user(val, (unsigned long __user *)arg);
689688

690-
case ATA_IOC_SET_IO32:
689+
case HDIO_SET_32BIT:
691690
val = (unsigned long) arg;
692691
rc = 0;
693692
spin_lock_irqsave(ap->lock, flags);

include/linux/ata.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,8 @@ enum ata_tf_protocols {
487487
};
488488

489489
enum ata_ioctls {
490-
ATA_IOC_GET_IO32 = 0x309,
491-
ATA_IOC_SET_IO32 = 0x324,
490+
ATA_IOC_GET_IO32 = 0x309, /* HDIO_GET_32BIT */
491+
ATA_IOC_SET_IO32 = 0x324, /* HDIO_SET_32BIT */
492492
};
493493

494494
/* core structures */

0 commit comments

Comments
 (0)