mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
HID: fix locking in hidraw_open()
As open needs to sleep hidraw was wrong to call it with a spinlock held. Furthermore, open can of course fail which needs to be handled. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
4ffaf869c7
commit
7d672cd750
@ -38,7 +38,7 @@ static int hidraw_major;
|
|||||||
static struct cdev hidraw_cdev;
|
static struct cdev hidraw_cdev;
|
||||||
static struct class *hidraw_class;
|
static struct class *hidraw_class;
|
||||||
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
|
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
|
||||||
static DEFINE_SPINLOCK(minors_lock);
|
static DEFINE_MUTEX(minors_lock);
|
||||||
|
|
||||||
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
@ -159,13 +159,13 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
|||||||
struct hidraw_list *list;
|
struct hidraw_list *list;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
lock_kernel();
|
|
||||||
if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
|
if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&minors_lock);
|
lock_kernel();
|
||||||
|
mutex_lock(&minors_lock);
|
||||||
if (!hidraw_table[minor]) {
|
if (!hidraw_table[minor]) {
|
||||||
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
|
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
|
||||||
minor);
|
minor);
|
||||||
@ -180,13 +180,16 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
|||||||
file->private_data = list;
|
file->private_data = list;
|
||||||
|
|
||||||
dev = hidraw_table[minor];
|
dev = hidraw_table[minor];
|
||||||
if (!dev->open++)
|
if (!dev->open++) {
|
||||||
dev->hid->ll_driver->open(dev->hid);
|
err = dev->hid->ll_driver->open(dev->hid);
|
||||||
|
if (err < 0)
|
||||||
|
dev->open--;
|
||||||
|
}
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
spin_unlock(&minors_lock);
|
mutex_unlock(&minors_lock);
|
||||||
out:
|
|
||||||
unlock_kernel();
|
unlock_kernel();
|
||||||
|
out:
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -310,7 +313,7 @@ int hidraw_connect(struct hid_device *hid)
|
|||||||
|
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
|
|
||||||
spin_lock(&minors_lock);
|
mutex_lock(&minors_lock);
|
||||||
|
|
||||||
for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
|
for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
|
||||||
if (hidraw_table[minor])
|
if (hidraw_table[minor])
|
||||||
@ -320,9 +323,8 @@ int hidraw_connect(struct hid_device *hid)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock(&minors_lock);
|
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
mutex_unlock(&minors_lock);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -331,14 +333,14 @@ int hidraw_connect(struct hid_device *hid)
|
|||||||
NULL, "%s%d", "hidraw", minor);
|
NULL, "%s%d", "hidraw", minor);
|
||||||
|
|
||||||
if (IS_ERR(dev->dev)) {
|
if (IS_ERR(dev->dev)) {
|
||||||
spin_lock(&minors_lock);
|
|
||||||
hidraw_table[minor] = NULL;
|
hidraw_table[minor] = NULL;
|
||||||
spin_unlock(&minors_lock);
|
mutex_unlock(&minors_lock);
|
||||||
result = PTR_ERR(dev->dev);
|
result = PTR_ERR(dev->dev);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&minors_lock);
|
||||||
init_waitqueue_head(&dev->wait);
|
init_waitqueue_head(&dev->wait);
|
||||||
INIT_LIST_HEAD(&dev->list);
|
INIT_LIST_HEAD(&dev->list);
|
||||||
|
|
||||||
@ -360,9 +362,9 @@ void hidraw_disconnect(struct hid_device *hid)
|
|||||||
|
|
||||||
hidraw->exist = 0;
|
hidraw->exist = 0;
|
||||||
|
|
||||||
spin_lock(&minors_lock);
|
mutex_lock(&minors_lock);
|
||||||
hidraw_table[hidraw->minor] = NULL;
|
hidraw_table[hidraw->minor] = NULL;
|
||||||
spin_unlock(&minors_lock);
|
mutex_unlock(&minors_lock);
|
||||||
|
|
||||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user