Skip to content

Commit

Permalink
HID: uhid: make creating devices work on 64/32 systems
Browse files Browse the repository at this point in the history
Unfortunately UHID interface, as it was introduced, is broken with 32 bit
userspace running on 64 bit kernels as it uses a pointer in its userspace
facing API.

Fix it by checking if we are executing compat task and munge the request
appropriately.

Reported-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
Dmitry Torokhov authored and Jiri Kosina committed Feb 18, 2013
1 parent c284979 commit befde02
Showing 1 changed file with 92 additions and 3 deletions.
95 changes: 92 additions & 3 deletions drivers/hid/uhid.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

#include <linux/atomic.h>
#include <linux/compat.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/hid.h>
Expand Down Expand Up @@ -276,6 +277,94 @@ static struct hid_ll_driver uhid_hid_driver = {
.parse = uhid_hid_parse,
};

#ifdef CONFIG_COMPAT

/* Apparently we haven't stepped on these rakes enough times yet. */
struct uhid_create_req_compat {
__u8 name[128];
__u8 phys[64];
__u8 uniq[64];

compat_uptr_t rd_data;
__u16 rd_size;

__u16 bus;
__u32 vendor;
__u32 product;
__u32 version;
__u32 country;
} __attribute__((__packed__));

static int uhid_event_from_user(const char __user *buffer, size_t len,
struct uhid_event *event)
{
if (is_compat_task()) {
u32 type;

if (get_user(type, buffer))
return -EFAULT;

if (type == UHID_CREATE) {
/*
* This is our messed up request with compat pointer.
* It is largish (more than 256 bytes) so we better
* allocate it from the heap.
*/
struct uhid_create_req_compat *compat;

compat = kmalloc(sizeof(*compat), GFP_KERNEL);
if (!compat)
return -ENOMEM;

buffer += sizeof(type);
len -= sizeof(type);
if (copy_from_user(compat, buffer,
min(len, sizeof(*compat)))) {
kfree(compat);
return -EFAULT;
}

/* Shuffle the data over to proper structure */
event->type = type;

memcpy(event->u.create.name, compat->name,
sizeof(compat->name));
memcpy(event->u.create.phys, compat->phys,
sizeof(compat->phys));
memcpy(event->u.create.uniq, compat->uniq,
sizeof(compat->uniq));

event->u.create.rd_data = compat_ptr(compat->rd_data);
event->u.create.rd_size = compat->rd_size;

event->u.create.bus = compat->bus;
event->u.create.vendor = compat->vendor;
event->u.create.product = compat->product;
event->u.create.version = compat->version;
event->u.create.country = compat->country;

kfree(compat);
return 0;
}
/* All others can be copied directly */
}

if (copy_from_user(event, buffer, min(len, sizeof(*event))))
return -EFAULT;

return 0;
}
#else
static int uhid_event_from_user(const char __user *buffer, size_t len,
struct uhid_event *event)
{
if (copy_from_user(event, buffer, min(len, sizeof(*event))))
return -EFAULT;

return 0;
}
#endif

static int uhid_dev_create(struct uhid_device *uhid,
const struct uhid_event *ev)
{
Expand Down Expand Up @@ -498,10 +587,10 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,

memset(&uhid->input_buf, 0, sizeof(uhid->input_buf));
len = min(count, sizeof(uhid->input_buf));
if (copy_from_user(&uhid->input_buf, buffer, len)) {
ret = -EFAULT;

ret = uhid_event_from_user(buffer, len, &uhid->input_buf);
if (ret)
goto unlock;
}

switch (uhid->input_buf.type) {
case UHID_CREATE:
Expand Down

0 comments on commit befde02

Please sign in to comment.