diff options
author | Jiri Kosina <jkosina@suse.cz> | 2007-05-10 02:45:56 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2007-05-10 02:45:56 -0400 |
commit | cdcb44e87bedcf5070eece61f89f9373a3810031 (patch) | |
tree | d9d1fdaa3b0e7789e0064377e3dedbd3ab6b9376 | |
parent | fe7ba31feadcc2cdb7749a8ed14960f989cbd021 (diff) |
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>
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index a8b3d66cd498..488d61bdbf2c 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c | |||
@@ -51,6 +51,7 @@ struct hiddev { | |||
51 | wait_queue_head_t wait; | 51 | wait_queue_head_t wait; |
52 | struct hid_device *hid; | 52 | struct hid_device *hid; |
53 | struct list_head list; | 53 | struct list_head list; |
54 | spinlock_t list_lock; | ||
54 | }; | 55 | }; |
55 | 56 | ||
56 | struct hiddev_list { | 57 | struct hiddev_list { |
@@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid, | |||
161 | { | 162 | { |
162 | struct hiddev *hiddev = hid->hiddev; | 163 | struct hiddev *hiddev = hid->hiddev; |
163 | struct hiddev_list *list; | 164 | struct hiddev_list *list; |
165 | unsigned long flags; | ||
164 | 166 | ||
167 | spin_lock_irqsave(&hiddev->list_lock, flags); | ||
165 | list_for_each_entry(list, &hiddev->list, node) { | 168 | list_for_each_entry(list, &hiddev->list, node) { |
166 | if (uref->field_index != HID_FIELD_INDEX_NONE || | 169 | if (uref->field_index != HID_FIELD_INDEX_NONE || |
167 | (list->flags & HIDDEV_FLAG_REPORT) != 0) { | 170 | (list->flags & HIDDEV_FLAG_REPORT) != 0) { |
@@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid, | |||
171 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | 174 | kill_fasync(&list->fasync, SIGIO, POLL_IN); |
172 | } | 175 | } |
173 | } | 176 | } |
177 | spin_unlock_irqrestore(&hiddev->list_lock, flags); | ||
174 | 178 | ||
175 | wake_up_interruptible(&hiddev->wait); | 179 | wake_up_interruptible(&hiddev->wait); |
176 | } | 180 | } |
@@ -235,9 +239,13 @@ static int hiddev_fasync(int fd, struct file *file, int on) | |||
235 | static int hiddev_release(struct inode * inode, struct file * file) | 239 | static int hiddev_release(struct inode * inode, struct file * file) |
236 | { | 240 | { |
237 | struct hiddev_list *list = file->private_data; | 241 | struct hiddev_list *list = file->private_data; |
242 | unsigned long flags; | ||
238 | 243 | ||
239 | hiddev_fasync(-1, file, 0); | 244 | hiddev_fasync(-1, file, 0); |
245 | |||
246 | spin_lock_irqsave(&list->hiddev->list_lock, flags); | ||
240 | list_del(&list->node); | 247 | list_del(&list->node); |
248 | spin_unlock_irqrestore(&list->hiddev->list_lock, flags); | ||
241 | 249 | ||
242 | if (!--list->hiddev->open) { | 250 | if (!--list->hiddev->open) { |
243 | if (list->hiddev->exist) | 251 | if (list->hiddev->exist) |
@@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file) | |||
257 | static int hiddev_open(struct inode *inode, struct file *file) | 265 | static int hiddev_open(struct inode *inode, struct file *file) |
258 | { | 266 | { |
259 | struct hiddev_list *list; | 267 | struct hiddev_list *list; |
268 | unsigned long flags; | ||
260 | 269 | ||
261 | int i = iminor(inode) - HIDDEV_MINOR_BASE; | 270 | int i = iminor(inode) - HIDDEV_MINOR_BASE; |
262 | 271 | ||
@@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file) | |||
267 | return -ENOMEM; | 276 | return -ENOMEM; |
268 | 277 | ||
269 | list->hiddev = hiddev_table[i]; | 278 | list->hiddev = hiddev_table[i]; |
279 | |||
280 | spin_lock_irqsave(&list->hiddev->list_lock, flags); | ||
270 | list_add_tail(&list->node, &hiddev_table[i]->list); | 281 | list_add_tail(&list->node, &hiddev_table[i]->list); |
282 | spin_unlock_irqrestore(&list->hiddev->list_lock, flags); | ||
283 | |||
271 | file->private_data = list; | 284 | file->private_data = list; |
272 | 285 | ||
273 | if (!list->hiddev->open++) | 286 | if (!list->hiddev->open++) |
@@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid) | |||
773 | 786 | ||
774 | init_waitqueue_head(&hiddev->wait); | 787 | init_waitqueue_head(&hiddev->wait); |
775 | INIT_LIST_HEAD(&hiddev->list); | 788 | INIT_LIST_HEAD(&hiddev->list); |
789 | spin_lock_init(&hiddev->list_lock); | ||
776 | hiddev->hid = hid; | 790 | hiddev->hid = hid; |
777 | hiddev->exist = 1; | 791 | hiddev->exist = 1; |
778 | 792 | ||