forked from mirrors/linux
		
	HID: protect hid_debug_list
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>
This commit is contained in:
		
							parent
							
								
									a5f04b9df1
								
							
						
					
					
						commit
						2353f2bea3
					
				
					 3 changed files with 8 additions and 0 deletions
				
			
		| 
						 | 
					@ -2319,6 +2319,7 @@ struct hid_device *hid_allocate_device(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init_waitqueue_head(&hdev->debug_wait);
 | 
						init_waitqueue_head(&hdev->debug_wait);
 | 
				
			||||||
	INIT_LIST_HEAD(&hdev->debug_list);
 | 
						INIT_LIST_HEAD(&hdev->debug_list);
 | 
				
			||||||
 | 
						mutex_init(&hdev->debug_list_lock);
 | 
				
			||||||
	sema_init(&hdev->driver_lock, 1);
 | 
						sema_init(&hdev->driver_lock, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return hdev;
 | 
						return hdev;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -580,12 +580,14 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
	struct hid_debug_list *list;
 | 
						struct hid_debug_list *list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&hdev->debug_list_lock);
 | 
				
			||||||
	list_for_each_entry(list, &hdev->debug_list, node) {
 | 
						list_for_each_entry(list, &hdev->debug_list, node) {
 | 
				
			||||||
		for (i = 0; i < strlen(buf); i++)
 | 
							for (i = 0; i < strlen(buf); i++)
 | 
				
			||||||
			list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
 | 
								list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
 | 
				
			||||||
				buf[i];
 | 
									buf[i];
 | 
				
			||||||
		list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
 | 
							list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
						mutex_unlock(&hdev->debug_list_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wake_up_interruptible(&hdev->debug_wait);
 | 
						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;
 | 
						file->private_data = list;
 | 
				
			||||||
	mutex_init(&list->read_mutex);
 | 
						mutex_init(&list->read_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&list->hdev->debug_list_lock);
 | 
				
			||||||
	list_add_tail(&list->node, &list->hdev->debug_list);
 | 
						list_add_tail(&list->node, &list->hdev->debug_list);
 | 
				
			||||||
 | 
						mutex_unlock(&list->hdev->debug_list_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	return err;
 | 
						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;
 | 
						struct hid_debug_list *list = file->private_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&list->hdev->debug_list_lock);
 | 
				
			||||||
	list_del(&list->node);
 | 
						list_del(&list->node);
 | 
				
			||||||
 | 
						mutex_unlock(&list->hdev->debug_list_lock);
 | 
				
			||||||
	kfree(list->hid_debug_buf);
 | 
						kfree(list->hid_debug_buf);
 | 
				
			||||||
	kfree(list);
 | 
						kfree(list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -512,6 +512,7 @@ struct hid_device {							/* device report descriptor */
 | 
				
			||||||
	struct dentry *debug_rdesc;
 | 
						struct dentry *debug_rdesc;
 | 
				
			||||||
	struct dentry *debug_events;
 | 
						struct dentry *debug_events;
 | 
				
			||||||
	struct list_head debug_list;
 | 
						struct list_head debug_list;
 | 
				
			||||||
 | 
						struct mutex debug_list_lock;
 | 
				
			||||||
	wait_queue_head_t debug_wait;
 | 
						wait_queue_head_t debug_wait;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue