aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYonghua Zheng <younghua.zheng@gmail.com>2013-08-26 11:38:35 -0400
committerJiri Kosina <jkosina@suse.cz>2013-08-26 15:40:24 -0400
commit277fe44dd862412ee034470ad1c13a79d24e533b (patch)
tree7333fed46331fc756cfde7dfd58a1127de97955b
parent06bb5219118fb098f4b0c7dcb484b28a52bf1c14 (diff)
HID: hidraw: Add spinlock in struct hidraw to protect list
It is unsafe to call list_for_each_entry in hidraw_report_event to traverse each hidraw_list node without a lock protection, the list could be modified if someone calls hidraw_release and list_del to remove itself from the list, this can cause hidraw_report_event to touch a deleted list struct and panic. To prevent this, introduce a spinlock in struct hidraw to protect list from concurrent access. Signed-off-by: Yonghua Zheng <younghua.zheng@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hidraw.c20
-rw-r--r--include/linux/hidraw.h1
2 files changed, 16 insertions, 5 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 612a655bc9f0..194a5660a389 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -253,6 +253,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
253 unsigned int minor = iminor(inode); 253 unsigned int minor = iminor(inode);
254 struct hidraw *dev; 254 struct hidraw *dev;
255 struct hidraw_list *list; 255 struct hidraw_list *list;
256 unsigned long flags;
256 int err = 0; 257 int err = 0;
257 258
258 if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { 259 if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
@@ -266,11 +267,6 @@ static int hidraw_open(struct inode *inode, struct file *file)
266 goto out_unlock; 267 goto out_unlock;
267 } 268 }
268 269
269 list->hidraw = hidraw_table[minor];
270 mutex_init(&list->read_mutex);
271 list_add_tail(&list->node, &hidraw_table[minor]->list);
272 file->private_data = list;
273
274 dev = hidraw_table[minor]; 270 dev = hidraw_table[minor];
275 if (!dev->open++) { 271 if (!dev->open++) {
276 err = hid_hw_power(dev->hid, PM_HINT_FULLON); 272 err = hid_hw_power(dev->hid, PM_HINT_FULLON);
@@ -283,9 +279,16 @@ static int hidraw_open(struct inode *inode, struct file *file)
283 if (err < 0) { 279 if (err < 0) {
284 hid_hw_power(dev->hid, PM_HINT_NORMAL); 280 hid_hw_power(dev->hid, PM_HINT_NORMAL);
285 dev->open--; 281 dev->open--;
282 goto out_unlock;
286 } 283 }
287 } 284 }
288 285
286 list->hidraw = hidraw_table[minor];
287 mutex_init(&list->read_mutex);
288 spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
289 list_add_tail(&list->node, &hidraw_table[minor]->list);
290 spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
291 file->private_data = list;
289out_unlock: 292out_unlock:
290 mutex_unlock(&minors_lock); 293 mutex_unlock(&minors_lock);
291out: 294out:
@@ -324,10 +327,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
324{ 327{
325 unsigned int minor = iminor(inode); 328 unsigned int minor = iminor(inode);
326 struct hidraw_list *list = file->private_data; 329 struct hidraw_list *list = file->private_data;
330 unsigned long flags;
327 331
328 mutex_lock(&minors_lock); 332 mutex_lock(&minors_lock);
329 333
334 spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
330 list_del(&list->node); 335 list_del(&list->node);
336 spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
331 kfree(list); 337 kfree(list);
332 338
333 drop_ref(hidraw_table[minor], 0); 339 drop_ref(hidraw_table[minor], 0);
@@ -456,7 +462,9 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
456 struct hidraw *dev = hid->hidraw; 462 struct hidraw *dev = hid->hidraw;
457 struct hidraw_list *list; 463 struct hidraw_list *list;
458 int ret = 0; 464 int ret = 0;
465 unsigned long flags;
459 466
467 spin_lock_irqsave(&dev->list_lock, flags);
460 list_for_each_entry(list, &dev->list, node) { 468 list_for_each_entry(list, &dev->list, node) {
461 int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); 469 int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
462 470
@@ -471,6 +479,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
471 list->head = new_head; 479 list->head = new_head;
472 kill_fasync(&list->fasync, SIGIO, POLL_IN); 480 kill_fasync(&list->fasync, SIGIO, POLL_IN);
473 } 481 }
482 spin_unlock_irqrestore(&dev->list_lock, flags);
474 483
475 wake_up_interruptible(&dev->wait); 484 wake_up_interruptible(&dev->wait);
476 return ret; 485 return ret;
@@ -519,6 +528,7 @@ int hidraw_connect(struct hid_device *hid)
519 528
520 mutex_unlock(&minors_lock); 529 mutex_unlock(&minors_lock);
521 init_waitqueue_head(&dev->wait); 530 init_waitqueue_head(&dev->wait);
531 spin_lock_init(&dev->list_lock);
522 INIT_LIST_HEAD(&dev->list); 532 INIT_LIST_HEAD(&dev->list);
523 533
524 dev->hid = hid; 534 dev->hid = hid;
diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h
index 2451662c728a..ddf52612eed8 100644
--- a/include/linux/hidraw.h
+++ b/include/linux/hidraw.h
@@ -23,6 +23,7 @@ struct hidraw {
23 wait_queue_head_t wait; 23 wait_queue_head_t wait;
24 struct hid_device *hid; 24 struct hid_device *hid;
25 struct device *dev; 25 struct device *dev;
26 spinlock_t list_lock;
26 struct list_head list; 27 struct list_head list;
27}; 28};
28 29