[PATCH] uhid: introduce new create event to fix 32/64-bitcompatibility issue

From: Kirill A. Shutemov
Date: Fri Feb 15 2013 - 08:33:59 EST


Current create event has pointer in payload. It causes problem with
32/64-bit compatibility. In particular it's not possible to use 32-bit
userspace driver with 64-bit kernel.

This patch deprecates old create event and introduce a new one. The new
create event has padding for pointer on 32-bit system to extend pointer
correctly.

Old userspace driver binaries will use old create event. It requires
simple recompile to switch to new one.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
---
drivers/hid/uhid.c | 31 +++++++++++++++++++++++++++++++
include/uapi/linux/uhid.h | 24 +++++++++++++++++++++++-
2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 714cd8c..2f0b0e4 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -343,6 +343,34 @@ err_free:
return ret;
}

+static int uhid_dev_old_create(struct uhid_device *uhid,
+ struct uhid_event *ev)
+{
+ struct uhid_event *new;
+
+ new = kzalloc(sizeof(new), GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ new->type = UHID_CREATE;
+ memcpy(new->u.create.name, ev->u.old_create.name, 128);
+ memcpy(new->u.create.phys, ev->u.old_create.phys, 64);
+ memcpy(new->u.create.uniq, ev->u.old_create.uniq, 64);
+
+ /* Assume kernel and userspace bitness are the same */
+ new->u.create.rd_data = ev->u.old_create.rd_data;
+
+ new->u.create.rd_size = ev->u.old_create.rd_size;
+ new->u.create.bus = ev->u.old_create.bus;
+ new->u.create.vendor = ev->u.old_create.vendor;
+ new->u.create.product = ev->u.old_create.product;
+ new->u.create.version = ev->u.old_create.version;
+ new->u.create.country = ev->u.old_create.country;
+ memcpy(ev, new, sizeof(new));
+ kfree(new);
+ return uhid_dev_create(uhid, ev);
+}
+
static int uhid_dev_destroy(struct uhid_device *uhid)
{
if (!uhid->running)
@@ -507,6 +535,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
case UHID_CREATE:
ret = uhid_dev_create(uhid, &uhid->input_buf);
break;
+ case UHID_OLD_CREATE:
+ ret = uhid_dev_old_create(uhid, &uhid->input_buf);
+ break;
case UHID_DESTROY:
ret = uhid_dev_destroy(uhid);
break;
diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h
index 9c6974f..bb18be8 100644
--- a/include/uapi/linux/uhid.h
+++ b/include/uapi/linux/uhid.h
@@ -23,7 +23,7 @@
#include <linux/types.h>

enum uhid_event_type {
- UHID_CREATE,
+ UHID_OLD_CREATE,
UHID_DESTROY,
UHID_START,
UHID_STOP,
@@ -34,13 +34,34 @@ enum uhid_event_type {
UHID_INPUT,
UHID_FEATURE,
UHID_FEATURE_ANSWER,
+ UHID_CREATE,
};

+struct uhid_old_create_req {
+ __u8 name[128];
+ __u8 phys[64];
+ __u8 uniq[64];
+ __u8 __user *rd_data;
+ __u16 rd_size;
+
+ __u16 bus;
+ __u32 vendor;
+ __u32 product;
+ __u32 version;
+ __u32 country;
+} __attribute__((__packed__));
+
struct uhid_create_req {
__u8 name[128];
__u8 phys[64];
__u8 uniq[64];
+#if __BITS_PER_LONG != 64 && __BYTE_ORDER == __BIG_ENDIAN
+ __u32 __pad;
+#endif
__u8 __user *rd_data;
+#if __BITS_PER_LONG != 64 && __BYTE_ORDER == __LITTLE_ENDIAN
+ __u32 __pad;
+#endif
__u16 rd_size;

__u16 bus;
@@ -92,6 +113,7 @@ struct uhid_event {
__u32 type;

union {
+ struct uhid_old_create_req old_create;
struct uhid_create_req create;
struct uhid_input_req input;
struct uhid_output_req output;
--
Kirill A. Shutemov
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/