hiddev: Add 32bit ioctl compatibilty
PATCH 1/1 hiddev: Add 32bit ioctl compatibilty
The hiddev driver currently lacks 32bit ioctl compatibility, so if you're running with a 64bit kernel and 32bit userspace, it won't work.
I'm pretty sure that the only thing missing is a compat_ioctl implementation as all structs have fixed size fields.
With this change I can use revoco to configure my MX Revolution mouse.
drivers/hid/usbhid/hiddev.c +12 -0
@@ -34,6 +34,7 @@#include <linux/usb.h> #include <linux/hid.h> #include <linux/hiddev.h> +#include <linux/compat.h> #include "usbhid.h" #ifdef CONFIG_USB_DYNAMIC_MINORS
@@ -738,6 +738,14 @@return -EINVAL ; } +#ifdef CONFIG_COMPAT +static long hiddev_compat_ioctl (struct file *file,unsigned int cmd,unsigned long arg) +{ +struct inode *inode = file->f_path.dentry->d_inode; +return hiddev_ioctl (inode, file, cmd,compat_ptr (arg)); +} +#endif +static const struct file_operations hiddev_fops = { .owner =THIS_MODULE , .read = hiddev_read,
@@ -747,6 +754,9 @@ .release = hiddev_release, .ioctl = hiddev_ioctl, .fasync = hiddev_fasync, +#ifdef CONFIG_COMPAT + .compat_ioctl = hiddev_compat_ioctl, +#endif };static struct usb_class_driver hiddev_class = {
drivers/hid/usbhid/hiddev.c line 746
Quoted 12 lines · Philip Langdale
The hiddev driver currently lacks 32bit ioctl compatibility, so if you're running with a 64bit kernel and 32bit userspace, it won't work.
I'm pretty sure that the only thing missing is a
compat_ioctlimplementation as all structs have fixed size fields.drivers/hid/usbhid/hiddev.c +5
-0+static long hiddev_compat_ioctl (struct file *file,unsigned int cmd,unsigned long arg) +{ +struct inode *inode = file->f_path.dentry->d_inode; +return hiddev_ioctl (inode, file, cmd,compat_ptr (arg)); +}
Just how many instances of that sucker do we need? It's nothing but
struct inode *inode = file->f_path.dentry->d_inode;
return file->f_op->ioctl(inode, file, cmd, compat_ptr(arg));Quoted 9 lines · Al Viro
drivers/hid/usbhid/hiddev.c line 746
Quoted 6 lines
drivers/hid/usbhid/hiddev.c +5
-0+static long hiddev_compat_ioctl (struct file *file,unsigned int cmd,unsigned long arg) +{ +struct inode *inode = file->f_path.dentry->d_inode; +return hiddev_ioctl (inode, file, cmd,compat_ptr (arg)); +}Just how many instances of that sucker do we need? It's nothing but
Quoted 3 lines
struct inode *inode = file->f_path.dentry->d_inode;
return file->f_op->ioctl(inode, file, cmd, compat_ptr(arg));So, I don't actually know what you're looking for, but of the 140 occasions that .compat_ioctl is implemented in Linus' tree, I can't find another one that actually uses this form. So, writing a shared implementation doesn't pick off any low hanging fruit. Now, it's possible that some of the other implementations could be reduced to this form - but for now, it seems the answer to your question is 'one' in either case. :-)
--phil
PATCH 1/1 compat_ioctl: introduce generic_compat_ioctl helper
In reply to Al ViroMany drivers use only compatible ioctl numbers. In order to avoid having to write a special compat_ioctl handler for each of them or listing every ioctl number in fs/compat_ioctl.c, let's introduce a generic handler that simply calls the driver specific f_op->unlocked_ioctl() or f_op->ioctl() handler.
---
Quoted 4 lines · Al Viro
Just how many instances of that sucker do we need? It's nothing but
struct inode *inode = file->f_path.dentry->d_inode; return file->f_op->ioctl(inode, file, cmd, compat_ptr(arg));
Is this what you had in mind? I've been meaning to do something like it for some time, but had forgotten about it.
I guess we can kill a significant amount of COMPATIBLE_IOCTL lines in fs/compat_ioctl.c with this.
fs/compat_ioctl.c +24 -0
@@ -1877,6 +1877,30 @@ lp_timeout_trans(unsigned int fd, unsignreturn sys_ioctl (fd, cmd, (unsigned long )tn); } +/* +* Helper function for device drivers that only have COMPATIBLE_IOCTL +* numbers. This can be used as f_op->compat_ioctl without any #ifdef +* and will simply call the native ioctl handler with the argument +* pointer. +* Don't use for drivers that interpret arg as an unsigned long instead +* of a pointer. +*/ +long generic_compat_ioctl (struct file *file,unsigned int cmd,unsigned long arg) +{ +int ret = -ENOTTY ; + arg = (unsigned long )compat_ptr (arg); + +if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl (file, cmd, arg); +else if (file->f_op->ioctl) { +lock_kernel (); + ret = file->f_op->ioctl (file->f_dentry->d_inode, file, cmd, arg); +unlock_kernel (); + } + +return ret; +} +EXPORT_SYMBOL_GPL (generic_compat_ioctl);typedef int (*ioctl_trans_handler_t )(unsigned int ,unsigned int ,unsigned long ,struct file *);
include/linux/fs.h +6 -0
@@ -1807,6 +1807,12 @@ extern void do_generic_mapping_read(struextern int generic_segment_checks (const struct iovec *iov,unsigned long *nr_segs,size_t *count,int access_flags); +#ifdef CONFIG_COMPAT +extern long generic_compat_ioctl (struct file *file,unsigned int cmd,unsigned long arg); +#else +#define generic_compat_ioctl (long (*)(struct file *, unsigned int, unsigned long))NULL +#endif +/* fs/splice.c */ extern ssize_t generic_file_splice_read (struct file *,loff_t *,struct pipe_inode_info *,size_t ,unsigned int );
PATCH 1/1 hiddev: simplify 32bit ioctl compatibilty
hiddev has both entries in fs/compat_ioctl.c and its own compat_ioctl() handler. Remove both and use the new generic_compat_ioctl helper instead.
drivers/hid/hidraw.c +1 -0
@@ -268,6 +268,7 @@ static const struct file_operations hidr .open = hidraw_open, .release = hidraw_release, .ioctl = hidraw_ioctl, + .compat_ioctl = generic_compat_ioctl, };void hidraw_report_event (struct hid_device *hid,u8 *data,int len)
drivers/hid/usbhid/hiddev.c +1 -11
@@ -739,14 +739,6 @@ inval:return -EINVAL ; } -#ifdef CONFIG_COMPAT -static long hiddev_compat_ioctl (struct file *file,unsigned int cmd,unsigned long arg) -{ -struct inode *inode = file->f_path.dentry->d_inode; -return hiddev_ioctl (inode, file, cmd,compat_ptr (arg)); -} -#endif -static const struct file_operations hiddev_fops = { .owner =THIS_MODULE , .read = hiddev_read,
@@ -756,9 +748,7 @@ static const struct file_operations hidd .release = hiddev_release, .ioctl = hiddev_ioctl, .fasync = hiddev_fasync, -#ifdef CONFIG_COMPAT - .compat_ioctl = hiddev_compat_ioctl, -#endif + .compat_ioctl = generic_compat_ioctl, };static struct usb_class_driver hiddev_class = {
fs/compat_ioctl.c +0 -19
@@ -102,8 +102,6 @@#include <linux/filter.h> #include <linux/pktcdvd.h> -#include <linux/hiddev.h> -#include <linux/dvb/audio.h> #include <linux/dvb/dmx.h> #include <linux/dvb/frontend.h>
@@ -2575,23 +2573,6 @@ COMPATIBLE_IOCTL(SIOCSIWPOWER) COMPATIBLE_IOCTL(SIOCGIWPOWER) COMPATIBLE_IOCTL(SIOCSIWAUTH) COMPATIBLE_IOCTL(SIOCGIWAUTH) -/* hiddev */ -COMPATIBLE_IOCTL(HIDIOCGVERSION) -COMPATIBLE_IOCTL(HIDIOCAPPLICATION) -COMPATIBLE_IOCTL(HIDIOCGDEVINFO) -COMPATIBLE_IOCTL(HIDIOCGSTRING) -COMPATIBLE_IOCTL(HIDIOCINITREPORT) -COMPATIBLE_IOCTL(HIDIOCGREPORT) -COMPATIBLE_IOCTL(HIDIOCSREPORT) -COMPATIBLE_IOCTL(HIDIOCGREPORTINFO) -COMPATIBLE_IOCTL(HIDIOCGFIELDINFO) -COMPATIBLE_IOCTL(HIDIOCGUSAGE) -COMPATIBLE_IOCTL(HIDIOCSUSAGE) -COMPATIBLE_IOCTL(HIDIOCGUCODE) -COMPATIBLE_IOCTL(HIDIOCGFLAG) -COMPATIBLE_IOCTL(HIDIOCSFLAG) -COMPATIBLE_IOCTL(HIDIOCGCOLLECTIONINDEX) -COMPATIBLE_IOCTL(HIDIOCGCOLLECTIONINFO) /* dvb */ COMPATIBLE_IOCTL(AUDIO_STOP) COMPATIBLE_IOCTL(AUDIO_PLAY)
Re: [PATCH] compat_ioctl: introduce generic_compat_ioctl helper
In reply to Arnd BergmannQuoted 5 lines · Arnd Bergmann
Many drivers use only compatible ioctl numbers. In order to avoid having to write a special
compat_ioctlhandler for each of them or listing every ioctl number infs/compat_ioctl.c, let's introduce a generic handler that simply calls the driver specificf_op->unlocked_ioctl()orf_op->ioctl()handler.
At least for the unlocked_ioctl case this is not nessecary because the driver an simply set both the unlocked_ioctl and compat_ioctl handlers to the same function. For the drivers not using unlocked_ioctl yet a function like this makes sense in theory, but I'd prefer to just switch them to ->unlocked_ioctl.
Re: [PATCH] compat_ioctl: introduce generic_compat_ioctl helper
Quoted 6 lines · Christoph Hellwig
At least for the
unlocked_ioctlcase this is not nessecary because the driver an simply set both theunlocked_ioctlandcompat_ioctlhandlers to the same function. For the drivers not usingunlocked_ioctlyet a function like this makes sense in theory, but I'd prefer to just switch them to ->unlocked_ioctl.
There is still the problem with s390 compat_ptr() conversion, a driver using the same handler for both may interpret a pointer with the high bit set as out of range.
Arnd <><