Commit 7c5b2352 authored by Manoj Chourasia's avatar Manoj Chourasia Committed by Greg Kroah-Hartman

HID: hidraw: correctly deallocate memory on device disconnect

commit 212a871a upstream.

This changes puts the commit 4fe9f8e2 back in place
with the fixes for slab corruption because of the commit.

When a device is unplugged, wait for all processes that
have opened the device to close before deallocating the device.

This commit was solving kernel crash because of the corruption in
rb tree of vmalloc. The rootcause was the device data pointer was
geting excessed after the memory associated with hidraw was freed.

The commit 4fe9f8e2 was buggy as it was also freeing the hidraw
first and then calling delete operation on the list associated with
that hidraw leading to slab corruption.
Signed-off-by: default avatarManoj Chourasia <mchourasia@nvidia.com>
Tested-by: default avatarPeter Wu <lekensteyn@gmail.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
Cc: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 30817a87
......@@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
if (!hidraw_table[minor]) {
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
}
......@@ -261,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
mutex_lock(&minors_lock);
if (!hidraw_table[minor]) {
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
}
......@@ -295,39 +295,38 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
static void drop_ref(struct hidraw *hidraw, int exists_bit)
{
if (exists_bit) {
hid_hw_close(hidraw->hid);
hidraw->exist = 0;
if (hidraw->open)
wake_up_interruptible(&hidraw->wait);
} else {
--hidraw->open;
}
if (!hidraw->open && !hidraw->exist) {
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
hidraw_table[hidraw->minor] = NULL;
kfree(hidraw);
}
}
static int hidraw_release(struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
struct hidraw *dev;
struct hidraw_list *list = file->private_data;
int ret;
int i;
mutex_lock(&minors_lock);
if (!hidraw_table[minor]) {
ret = -ENODEV;
goto unlock;
}
list_del(&list->node);
dev = hidraw_table[minor];
if (!--dev->open) {
if (list->hidraw->exist) {
hid_hw_power(dev->hid, PM_HINT_NORMAL);
hid_hw_close(dev->hid);
} else {
kfree(list->hidraw);
}
}
for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
kfree(list->buffer[i].value);
kfree(list);
ret = 0;
unlock:
mutex_unlock(&minors_lock);
return ret;
drop_ref(hidraw_table[minor], 0);
mutex_unlock(&minors_lock);
return 0;
}
static long hidraw_ioctl(struct file *file, unsigned int cmd,
......@@ -531,18 +530,9 @@ void hidraw_disconnect(struct hid_device *hid)
struct hidraw *hidraw = hid->hidraw;
mutex_lock(&minors_lock);
hidraw->exist = 0;
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
hidraw_table[hidraw->minor] = NULL;
drop_ref(hidraw, 1);
if (hidraw->open) {
hid_hw_close(hid);
wake_up_interruptible(&hidraw->wait);
} else {
kfree(hidraw);
}
mutex_unlock(&minors_lock);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment