HID: protect hid_debug_list
authorJiri Kosina <jkosina@suse.cz>
Tue, 16 Apr 2013 22:40:09 +0000 (15:40 -0700)
committerJiri Kosina <jkosina@suse.cz>
Tue, 30 Apr 2013 08:09:31 +0000 (10:09 +0200)
Accesses to hid_device->hid_debug_list are not serialized properly, which
could result in SMP concurrency issues when HID debugfs events are accessesed
by multiple userspace processess.

Serialize all the list operations by a mutex.

Spotted by Al Viro.

Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-core.c
drivers/hid/hid-debug.c
include/linux/hid.h

index f86dd9708ca5b81fe50e6d8abe2e92b12e6524e9..e7765ede339e6f8d2e5a04bcb3c5bc8295bcbf5b 100644 (file)
@@ -2319,6 +2319,7 @@ struct hid_device *hid_allocate_device(void)
 
        init_waitqueue_head(&hdev->debug_wait);
        INIT_LIST_HEAD(&hdev->debug_list);
+       mutex_init(&hdev->debug_list_lock);
        sema_init(&hdev->driver_lock, 1);
 
        return hdev;
index 094cbcfe1e1ade65de75c9cd7def4bb54d984cf9..7e56cb3855e329696abea711445bffe588d101f6 100644 (file)
@@ -580,12 +580,14 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
        int i;
        struct hid_debug_list *list;
 
+       mutex_lock(&hdev->debug_list_lock);
        list_for_each_entry(list, &hdev->debug_list, node) {
                for (i = 0; i < strlen(buf); i++)
                        list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
                                buf[i];
                list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
         }
+       mutex_unlock(&hdev->debug_list_lock);
 
        wake_up_interruptible(&hdev->debug_wait);
 }
@@ -990,7 +992,9 @@ static int hid_debug_events_open(struct inode *inode, struct file *file)
        file->private_data = list;
        mutex_init(&list->read_mutex);
 
+       mutex_lock(&list->hdev->debug_list_lock);
        list_add_tail(&list->node, &list->hdev->debug_list);
+       mutex_unlock(&list->hdev->debug_list_lock);
 
 out:
        return err;
@@ -1085,7 +1089,9 @@ static int hid_debug_events_release(struct inode *inode, struct file *file)
 {
        struct hid_debug_list *list = file->private_data;
 
+       mutex_lock(&list->hdev->debug_list_lock);
        list_del(&list->node);
+       mutex_unlock(&list->hdev->debug_list_lock);
        kfree(list->hid_debug_buf);
        kfree(list);
 
index e14b465b11463d353553027e602d79499d8be01d..06579c72d19544622b42d6adc4151ab7bae64823 100644 (file)
@@ -512,6 +512,7 @@ struct hid_device {                                                 /* device report descriptor */
        struct dentry *debug_rdesc;
        struct dentry *debug_events;
        struct list_head debug_list;
+       struct mutex debug_list_lock;
        wait_queue_head_t debug_wait;
 };