diff options
author | Jiri Kosina <jkosina@suse.cz> | 2007-05-16 00:44:59 +0200 |
---|---|---|
committer | Chris Wright <chrisw@sous-sol.org> | 2007-05-23 14:32:53 -0700 |
commit | 81166863634472f5fcb45793e6676023d674f17c (patch) | |
tree | 4ec0aef985258d7aba5491cfa3abf2ff8876598c | |
parent | b1b292459c99d06e90b109f04e2dbd184fda9167 (diff) |
[PATCH] USB HID: hiddev - fix race between hiddev_send_event() and hiddev_release()
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: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
-rw-r--r-- | drivers/usb/input/hiddev.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index a8b3d66cd498..488d61bdbf2c 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -51,6 +51,7 @@ struct hiddev { wait_queue_head_t wait; struct hid_device *hid; struct list_head list; + spinlock_t list_lock; }; struct hiddev_list { @@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid, { struct hiddev *hiddev = hid->hiddev; struct hiddev_list *list; + unsigned long flags; + spin_lock_irqsave(&hiddev->list_lock, flags); list_for_each_entry(list, &hiddev->list, node) { if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { @@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid, kill_fasync(&list->fasync, SIGIO, POLL_IN); } } + spin_unlock_irqrestore(&hiddev->list_lock, flags); wake_up_interruptible(&hiddev->wait); } @@ -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) { struct hiddev_list *list = file->private_data; + unsigned long flags; hiddev_fasync(-1, file, 0); + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); if (!--list->hiddev->open) { if (list->hiddev->exist) @@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; + unsigned long flags; int i = iminor(inode) - HIDDEV_MINOR_BASE; @@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file) return -ENOMEM; list->hiddev = hiddev_table[i]; + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_add_tail(&list->node, &hiddev_table[i]->list); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); + file->private_data = list; if (!list->hiddev->open++) @@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid) init_waitqueue_head(&hiddev->wait); INIT_LIST_HEAD(&hiddev->list); + spin_lock_init(&hiddev->list_lock); hiddev->hid = hid; hiddev->exist = 1; |