]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hpet: Support 32-bit userspace
authorHe Zhe <zhe.he@windriver.com>
Thu, 6 Jun 2024 12:39:08 +0000 (20:39 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 3 Jul 2024 14:20:03 +0000 (16:20 +0200)
hpet_compat_ioctl and read file operations failed to handle parameters from
32-bit userspace and thus samples/timers/hpet_example.c fails as below.

root@intel-x86-64:~# ./hpet_example-32.out poll /dev/hpet 1 2
-hpet: executing poll
hpet_poll: HPET_IRQFREQ failed

This patch fixes cmd and arg handling in hpet_compat_ioctl and adds compat
handling for 32-bit userspace in hpet_read.

hpet_example now shows that it works for both 64-bit and 32-bit.

root@intel-x86-64:~# ./hpet_example-32.out poll /dev/hpet 1 2
-hpet: executing poll
hpet_poll: info.hi_flags 0x0
hpet_poll: expired time = 0xf4298
hpet_poll: revents = 0x1
hpet_poll: data 0x1
hpet_poll: expired time = 0xf4235
hpet_poll: revents = 0x1
hpet_poll: data 0x1
root@intel-x86-64:~# ./hpet_example-64.out poll /dev/hpet 1 2
-hpet: executing poll
hpet_poll: info.hi_flags 0x0
hpet_poll: expired time = 0xf42a1
hpet_poll: revents = 0x1
hpet_poll: data 0x1
hpet_poll: expired time = 0xf4232
hpet_poll: revents = 0x1
hpet_poll: data 0x1

Cc: stable@vger.kernel.org
Signed-off-by: He Zhe <zhe.he@windriver.com>
Fixes: 54066a57c584 ("hpet: kill BKL, add compat_ioctl")
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20240606123908.738733-1-zhe.he@windriver.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/char/hpet.c

index d51fc8321d411ce60281a1be71f2e3a01022907f..da32e8ed0830967fcf9b08a2832dd28ae8584f35 100644 (file)
@@ -269,8 +269,13 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
        if (!devp->hd_ireqfreq)
                return -EIO;
 
-       if (count < sizeof(unsigned long))
-               return -EINVAL;
+       if (in_compat_syscall()) {
+               if (count < sizeof(compat_ulong_t))
+                       return -EINVAL;
+       } else {
+               if (count < sizeof(unsigned long))
+                       return -EINVAL;
+       }
 
        add_wait_queue(&devp->hd_waitqueue, &wait);
 
@@ -294,9 +299,16 @@ hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
                schedule();
        }
 
-       retval = put_user(data, (unsigned long __user *)buf);
-       if (!retval)
-               retval = sizeof(unsigned long);
+       if (in_compat_syscall()) {
+               retval = put_user(data, (compat_ulong_t __user *)buf);
+               if (!retval)
+                       retval = sizeof(compat_ulong_t);
+       } else {
+               retval = put_user(data, (unsigned long __user *)buf);
+               if (!retval)
+                       retval = sizeof(unsigned long);
+       }
+
 out:
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(&devp->hd_waitqueue, &wait);
@@ -651,12 +663,24 @@ struct compat_hpet_info {
        unsigned short hi_timer;
 };
 
+/* 32-bit types would lead to different command codes which should be
+ * translated into 64-bit ones before passed to hpet_ioctl_common
+ */
+#define COMPAT_HPET_INFO       _IOR('h', 0x03, struct compat_hpet_info)
+#define COMPAT_HPET_IRQFREQ    _IOW('h', 0x6, compat_ulong_t)
+
 static long
 hpet_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct hpet_info info;
        int err;
 
+       if (cmd == COMPAT_HPET_INFO)
+               cmd = HPET_INFO;
+
+       if (cmd == COMPAT_HPET_IRQFREQ)
+               cmd = HPET_IRQFREQ;
+
        mutex_lock(&hpet_mutex);
        err = hpet_ioctl_common(file->private_data, cmd, arg, &info);
        mutex_unlock(&hpet_mutex);