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 <[email protected]>
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 <[email protected]>
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]

Attachment: PATCH_HID-hidraw-correctly-deallocate-memory-on-device-dis.patch
Description: PATCH_HID-hidraw-correctly-deallocate-memory-on-device-dis.patch

Reply via email to