Commit cdcb44e8 authored by Jiri Kosina's avatar Jiri Kosina

USB HID: hiddev - fix race between hiddev_send_event() and hiddev_release()

There is a small race window in which hiddev_release() could corrupt the
list that is being processed for new event in hiddev_send_event().
Synchronize the operations over this list.
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent fe7ba31f
...@@ -51,6 +51,7 @@ struct hiddev { ...@@ -51,6 +51,7 @@ struct hiddev {
wait_queue_head_t wait; wait_queue_head_t wait;
struct hid_device *hid; struct hid_device *hid;
struct list_head list; struct list_head list;
spinlock_t list_lock;
}; };
struct hiddev_list { struct hiddev_list {
...@@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid, ...@@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid,
{ {
struct hiddev *hiddev = hid->hiddev; struct hiddev *hiddev = hid->hiddev;
struct hiddev_list *list; struct hiddev_list *list;
unsigned long flags;
spin_lock_irqsave(&hiddev->list_lock, flags);
list_for_each_entry(list, &hiddev->list, node) { list_for_each_entry(list, &hiddev->list, node) {
if (uref->field_index != HID_FIELD_INDEX_NONE || if (uref->field_index != HID_FIELD_INDEX_NONE ||
(list->flags & HIDDEV_FLAG_REPORT) != 0) { (list->flags & HIDDEV_FLAG_REPORT) != 0) {
...@@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid, ...@@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid,
kill_fasync(&list->fasync, SIGIO, POLL_IN); kill_fasync(&list->fasync, SIGIO, POLL_IN);
} }
} }
spin_unlock_irqrestore(&hiddev->list_lock, flags);
wake_up_interruptible(&hiddev->wait); wake_up_interruptible(&hiddev->wait);
} }
...@@ -235,9 +239,13 @@ static int hiddev_fasync(int fd, struct file *file, int on) ...@@ -235,9 +239,13 @@ static int hiddev_fasync(int fd, struct file *file, int on)
static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_release(struct inode * inode, struct file * file)
{ {
struct hiddev_list *list = file->private_data; struct hiddev_list *list = file->private_data;
unsigned long flags;
hiddev_fasync(-1, file, 0); hiddev_fasync(-1, file, 0);
spin_lock_irqsave(&list->hiddev->list_lock, flags);
list_del(&list->node); list_del(&list->node);
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
if (!--list->hiddev->open) { if (!--list->hiddev->open) {
if (list->hiddev->exist) if (list->hiddev->exist)
...@@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file) ...@@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file)
static int hiddev_open(struct inode *inode, struct file *file) static int hiddev_open(struct inode *inode, struct file *file)
{ {
struct hiddev_list *list; struct hiddev_list *list;
unsigned long flags;
int i = iminor(inode) - HIDDEV_MINOR_BASE; int i = iminor(inode) - HIDDEV_MINOR_BASE;
...@@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file)
return -ENOMEM; return -ENOMEM;
list->hiddev = hiddev_table[i]; list->hiddev = hiddev_table[i];
spin_lock_irqsave(&list->hiddev->list_lock, flags);
list_add_tail(&list->node, &hiddev_table[i]->list); list_add_tail(&list->node, &hiddev_table[i]->list);
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
file->private_data = list; file->private_data = list;
if (!list->hiddev->open++) if (!list->hiddev->open++)
...@@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid) ...@@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid)
init_waitqueue_head(&hiddev->wait); init_waitqueue_head(&hiddev->wait);
INIT_LIST_HEAD(&hiddev->list); INIT_LIST_HEAD(&hiddev->list);
spin_lock_init(&hiddev->list_lock);
hiddev->hid = hid; hiddev->hid = hid;
hiddev->exist = 1; hiddev->exist = 1;
......
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