aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorManoj Chourasia <mchourasia@nvidia.com>2013-07-22 06:03:13 -0400
committerJiri Kosina <jkosina@suse.cz>2013-08-09 05:27:00 -0400
commit212a871a3934beccf43431608c27ed2e05a476ec (patch)
treed79f836e7c2f2ec64a36af5725bccd1ef74423ee /drivers/hid
parent9854a6f929956c9099dcc837157fd344f6f1c227 (diff)
HID: hidraw: correctly deallocate memory on device disconnect
This changes puts the commit 4fe9f8e203f 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 4fe9f8e203f 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: Manoj Chourasia <mchourasia@nvidia.com> Tested-by: Peter Wu <lekensteyn@gmail.com> Cc: stable@vger.kernel.org Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hidraw.c60
1 files changed, 25 insertions, 35 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index a7451632ceb4..612a655bc9f0 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
113 __u8 *buf; 113 __u8 *buf;
114 int ret = 0; 114 int ret = 0;
115 115
116 if (!hidraw_table[minor]) { 116 if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
117 ret = -ENODEV; 117 ret = -ENODEV;
118 goto out; 118 goto out;
119 } 119 }
@@ -261,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
261 } 261 }
262 262
263 mutex_lock(&minors_lock); 263 mutex_lock(&minors_lock);
264 if (!hidraw_table[minor]) { 264 if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
265 err = -ENODEV; 265 err = -ENODEV;
266 goto out_unlock; 266 goto out_unlock;
267 } 267 }
@@ -302,39 +302,38 @@ static int hidraw_fasync(int fd, struct file *file, int on)
302 return fasync_helper(fd, file, on, &list->fasync); 302 return fasync_helper(fd, file, on, &list->fasync);
303} 303}
304 304
305static void drop_ref(struct hidraw *hidraw, int exists_bit)
306{
307 if (exists_bit) {
308 hid_hw_close(hidraw->hid);
309 hidraw->exist = 0;
310 if (hidraw->open)
311 wake_up_interruptible(&hidraw->wait);
312 } else {
313 --hidraw->open;
314 }
315
316 if (!hidraw->open && !hidraw->exist) {
317 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
318 hidraw_table[hidraw->minor] = NULL;
319 kfree(hidraw);
320 }
321}
322
305static int hidraw_release(struct inode * inode, struct file * file) 323static int hidraw_release(struct inode * inode, struct file * file)
306{ 324{
307 unsigned int minor = iminor(inode); 325 unsigned int minor = iminor(inode);
308 struct hidraw *dev;
309 struct hidraw_list *list = file->private_data; 326 struct hidraw_list *list = file->private_data;
310 int ret;
311 int i;
312 327
313 mutex_lock(&minors_lock); 328 mutex_lock(&minors_lock);
314 if (!hidraw_table[minor]) {
315 ret = -ENODEV;
316 goto unlock;
317 }
318 329
319 list_del(&list->node); 330 list_del(&list->node);
320 dev = hidraw_table[minor];
321 if (!--dev->open) {
322 if (list->hidraw->exist) {
323 hid_hw_power(dev->hid, PM_HINT_NORMAL);
324 hid_hw_close(dev->hid);
325 } else {
326 kfree(list->hidraw);
327 }
328 }
329
330 for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
331 kfree(list->buffer[i].value);
332 kfree(list); 331 kfree(list);
333 ret = 0;
334unlock:
335 mutex_unlock(&minors_lock);
336 332
337 return ret; 333 drop_ref(hidraw_table[minor], 0);
334
335 mutex_unlock(&minors_lock);
336 return 0;
338} 337}
339 338
340static long hidraw_ioctl(struct file *file, unsigned int cmd, 339static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -539,18 +538,9 @@ void hidraw_disconnect(struct hid_device *hid)
539 struct hidraw *hidraw = hid->hidraw; 538 struct hidraw *hidraw = hid->hidraw;
540 539
541 mutex_lock(&minors_lock); 540 mutex_lock(&minors_lock);
542 hidraw->exist = 0;
543
544 device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
545 541
546 hidraw_table[hidraw->minor] = NULL; 542 drop_ref(hidraw, 1);
547 543
548 if (hidraw->open) {
549 hid_hw_close(hid);
550 wake_up_interruptible(&hidraw->wait);
551 } else {
552 kfree(hidraw);
553 }
554 mutex_unlock(&minors_lock); 544 mutex_unlock(&minors_lock);
555} 545}
556EXPORT_SYMBOL_GPL(hidraw_disconnect); 546EXPORT_SYMBOL_GPL(hidraw_disconnect);