Skip to contentSkip to conversation

hiddev: Add 32bit ioctl compatibilty

PATCH In review Philip Langdale 4 participants 7 messages Oct 12, 2007 – Oct 20, 2007

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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 = {
#1 lore raw

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_ioctl implementation 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));
#2 lore raw
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

#3 lore raw

PATCH 1/1 compat_ioctl: introduce generic_compat_ioctl helper

In reply to Al Viro

Many 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
Index: linux-2.6/fs/compat_ioctl.c
===================================================================
@@ -1877,6 +1877,30 @@ lp_timeout_trans(unsigned int fd, unsign
 	return 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
Index: linux-2.6/include/linux/fs.h
===================================================================
@@ -1807,6 +1807,12 @@ extern void do_generic_mapping_read(stru
 extern 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);
#4 lore raw

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
Index: linux-2.6/drivers/hid/hidraw.c
===================================================================
@@ -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
Index: linux-2.6/drivers/hid/usbhid/hiddev.c
===================================================================
@@ -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
Index: linux-2.6/fs/compat_ioctl.c
===================================================================
@@ -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)
#5 lore raw

Re: [PATCH] compat_ioctl: introduce generic_compat_ioctl helper

In reply to Arnd Bergmann
Quoted 5 lines · Arnd Bergmann

Many 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.

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.

#6 lore raw

Re: [PATCH] compat_ioctl: introduce generic_compat_ioctl helper

Quoted 6 lines · Christoph Hellwig

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.

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 <><