Messages in this thread Patch in this message | | | From | Manoj Chourasia <> | Date | Mon, 22 Jul 2013 18:23:15 +0530 | Subject | [PATCH] hidraw: correctly deallocate memory on device disconnect |
| |
Hi Jiri,
This is mail is in regard to your commit for hidaw devices. We were seeing kernel crashes while connect and disconnect a hidraw device and we were hitting rb tree corruption in vmalloc and kernel was crashing because of null pointer de-reference. Later we figure out that was because the hid device disconnect is freeing memory which later got access by hidraw_read and hid_write.
commit df0cfd6990347c20ae031f3f34137cba274f1972 Author: Jiri Kosina <jkosina@suse.cz> Date: Thu Nov 1 11:33:26 2012 +0100
HID: hidraw: put old deallocation mechanism in place
This basically reverts commit 4fe9f8e203fda. It causes multiple problems, namely:
We found following commit by Ratan Nalumasu was helping in solving our issue commit 4fe9f8e203fdad1524c04beb390f3c6099781ed9 Author: Ratan Nalumasu <ratan@google.com> Date: Sat Sep 22 11:46:30 2012 -0700
HID: hidraw: don't deallocate memory when it is in use
When a device is unplugged, wait for all processes that have opened the device to close before deallocating the device.
But there was a bug in Ratan's that commit which was causing slab memory corruption. We fixed that bug and now our issue solved. I can give evidence on issue. The bug that was there in the commit that he was deleting list after words and feeing head of it before. I am attaching the patch.
-regards Manoj
-----------------------------------------------------------------------
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index a745163..612a655 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, __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; } @@ -302,39 +302,38 @@ static int hidraw_fasync(int fd, struct file *file, int on) return fasync_helper(fd, file, on, &list->fasync); } +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, @@ -539,18 +538,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); -- -Regards Manoj
[ 111.578116] Unable to handle kernel NULL pointer dereference at virtual address 00000001
[ 111.578134] pgd = aa870000
[ 111.578143] [00000001] *pgd=2afbb831, *pte=00000000, *ppte=00000000
[ 111.578168] Internal error: Oops: 17 [#1] PREEMPT SMP
[ 111.578352] CPU: 1 Tainted: P
[ 111.578378] PC is at rb_erase+0x1b4/0x370
[ 111.578399] LR is at __free_vmap_area+0x5c/0xf4
[ 111.578413] pc : [<801d2ba8>] lr : [<800b7fb8>] psr: 200f0013
[ 111.578419] sp : aa205d88 ip : 00000001 fp : 00000002
[ 111.578431] r10: 00000d30 r9 : 00000002 r8 : c1ab2000
[ 111.578442] r7 : 00000d30 r6 : 9ca544ec r5 : 805f38a0 r4 : 9ca24b6c
[ 111.578454] r3 : 9c8a87ec r2 : 00000001 r1 : b627bf6c r0 : 00000001
[ 111.578468] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[ 111.578481] Control: 10c5387d Table: 2a87004a DAC: 00000015
[ 111.578491]
[unhandled content-type:application/octet-stream] | |