Commit 3381b779 authored by David Härdeman's avatar David Härdeman Committed by Mauro Carvalho Chehab

[media] media: lirc_dev: sanitize locking

Use the irctl mutex for all device operations and only use lirc_dev_lock
to protect the irctls array. Also, make sure that the device is alive
early in each fops function before doing anything else.

Since this patch touches nearly every line where the irctl mutex is
taken/released, it also renames the mutex at the same time (the name
irctl_lock will be misleading once struct irctl goes away in later
patches).

[mchehab@s-opensource.com: fix a merge conflict]
Signed-off-by: default avatarDavid Härdeman <david@hardeman.nu>
Signed-off-by: default avatarSean Young <sean@mess.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent 3bce5572
...@@ -38,7 +38,7 @@ struct irctl { ...@@ -38,7 +38,7 @@ struct irctl {
bool attached; bool attached;
int open; int open;
struct mutex irctl_lock; struct mutex mutex; /* protect from simultaneous accesses */
struct lirc_buffer *buf; struct lirc_buffer *buf;
bool buf_internal; bool buf_internal;
...@@ -46,6 +46,7 @@ struct irctl { ...@@ -46,6 +46,7 @@ struct irctl {
struct cdev cdev; struct cdev cdev;
}; };
/* This mutex protects the irctls array */
static DEFINE_MUTEX(lirc_dev_lock); static DEFINE_MUTEX(lirc_dev_lock);
static struct irctl *irctls[MAX_IRCTL_DEVICES]; static struct irctl *irctls[MAX_IRCTL_DEVICES];
...@@ -53,20 +54,25 @@ static struct irctl *irctls[MAX_IRCTL_DEVICES]; ...@@ -53,20 +54,25 @@ static struct irctl *irctls[MAX_IRCTL_DEVICES];
/* Only used for sysfs but defined to void otherwise */ /* Only used for sysfs but defined to void otherwise */
static struct class *lirc_class; static struct class *lirc_class;
static void lirc_release(struct device *ld) static void lirc_free_buffer(struct irctl *ir)
{ {
struct irctl *ir = container_of(ld, struct irctl, dev);
put_device(ir->dev.parent); put_device(ir->dev.parent);
if (ir->buf_internal) { if (ir->buf_internal) {
lirc_buffer_free(ir->buf); lirc_buffer_free(ir->buf);
kfree(ir->buf); kfree(ir->buf);
ir->buf = NULL;
} }
}
static void lirc_release(struct device *ld)
{
struct irctl *ir = container_of(ld, struct irctl, dev);
mutex_lock(&lirc_dev_lock); mutex_lock(&lirc_dev_lock);
irctls[ir->d.minor] = NULL; irctls[ir->d.minor] = NULL;
mutex_unlock(&lirc_dev_lock); mutex_unlock(&lirc_dev_lock);
lirc_free_buffer(ir);
kfree(ir); kfree(ir);
} }
...@@ -143,6 +149,28 @@ int lirc_register_driver(struct lirc_driver *d) ...@@ -143,6 +149,28 @@ int lirc_register_driver(struct lirc_driver *d)
return -EBADRQC; return -EBADRQC;
} }
/* some safety check 8-) */
d->name[sizeof(d->name) - 1] = '\0';
if (d->features == 0)
d->features = LIRC_CAN_REC_LIRCCODE;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
if (!ir)
return -ENOMEM;
mutex_init(&ir->mutex);
ir->d = *d;
if (LIRC_CAN_REC(d->features)) {
err = lirc_allocate_buffer(ir);
if (err) {
kfree(ir);
return err;
}
d->rbuf = ir->buf;
}
mutex_lock(&lirc_dev_lock); mutex_lock(&lirc_dev_lock);
/* find first free slot for driver */ /* find first free slot for driver */
...@@ -152,37 +180,18 @@ int lirc_register_driver(struct lirc_driver *d) ...@@ -152,37 +180,18 @@ int lirc_register_driver(struct lirc_driver *d)
if (minor == MAX_IRCTL_DEVICES) { if (minor == MAX_IRCTL_DEVICES) {
dev_err(d->dev, "no free slots for drivers!\n"); dev_err(d->dev, "no free slots for drivers!\n");
err = -ENOMEM; mutex_unlock(&lirc_dev_lock);
goto out_lock; lirc_free_buffer(ir);
} kfree(ir);
return -ENOMEM;
ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
if (!ir) {
err = -ENOMEM;
goto out_lock;
} }
mutex_init(&ir->irctl_lock);
irctls[minor] = ir; irctls[minor] = ir;
d->irctl = ir; d->irctl = ir;
d->minor = minor; d->minor = minor;
ir->d.minor = minor;
/* some safety check 8-) */ mutex_unlock(&lirc_dev_lock);
d->name[sizeof(d->name)-1] = '\0';
if (d->features == 0)
d->features = LIRC_CAN_REC_LIRCCODE;
ir->d = *d;
if (LIRC_CAN_REC(d->features)) {
err = lirc_allocate_buffer(irctls[minor]);
if (err) {
kfree(ir);
goto out_lock;
}
d->rbuf = ir->buf;
}
device_initialize(&ir->dev); device_initialize(&ir->dev);
ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor);
...@@ -196,10 +205,10 @@ int lirc_register_driver(struct lirc_driver *d) ...@@ -196,10 +205,10 @@ int lirc_register_driver(struct lirc_driver *d)
ir->attached = true; ir->attached = true;
err = cdev_device_add(&ir->cdev, &ir->dev); err = cdev_device_add(&ir->cdev, &ir->dev);
if (err) if (err) {
goto out_dev; put_device(&ir->dev);
return err;
mutex_unlock(&lirc_dev_lock); }
get_device(ir->dev.parent); get_device(ir->dev.parent);
...@@ -207,13 +216,6 @@ int lirc_register_driver(struct lirc_driver *d) ...@@ -207,13 +216,6 @@ int lirc_register_driver(struct lirc_driver *d)
ir->d.name, ir->d.minor); ir->d.name, ir->d.minor);
return 0; return 0;
out_dev:
put_device(&ir->dev);
out_lock:
mutex_unlock(&lirc_dev_lock);
return err;
} }
EXPORT_SYMBOL(lirc_register_driver); EXPORT_SYMBOL(lirc_register_driver);
...@@ -226,11 +228,13 @@ void lirc_unregister_driver(struct lirc_driver *d) ...@@ -226,11 +228,13 @@ void lirc_unregister_driver(struct lirc_driver *d)
ir = d->irctl; ir = d->irctl;
mutex_lock(&lirc_dev_lock);
dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
d->name, d->minor); d->name, d->minor);
cdev_device_del(&ir->cdev, &ir->dev);
mutex_lock(&ir->mutex);
ir->attached = false; ir->attached = false;
if (ir->open) { if (ir->open) {
dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
...@@ -238,9 +242,8 @@ void lirc_unregister_driver(struct lirc_driver *d) ...@@ -238,9 +242,8 @@ void lirc_unregister_driver(struct lirc_driver *d)
wake_up_interruptible(&ir->buf->wait_poll); wake_up_interruptible(&ir->buf->wait_poll);
} }
mutex_unlock(&lirc_dev_lock); mutex_unlock(&ir->mutex);
cdev_device_del(&ir->cdev, &ir->dev);
put_device(&ir->dev); put_device(&ir->dev);
} }
EXPORT_SYMBOL(lirc_unregister_driver); EXPORT_SYMBOL(lirc_unregister_driver);
...@@ -252,13 +255,24 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) ...@@ -252,13 +255,24 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
if (ir->open) retval = mutex_lock_interruptible(&ir->mutex);
return -EBUSY; if (retval)
return retval;
if (!ir->attached) {
retval = -ENODEV;
goto out;
}
if (ir->open) {
retval = -EBUSY;
goto out;
}
if (ir->d.rdev) { if (ir->d.rdev) {
retval = rc_open(ir->d.rdev); retval = rc_open(ir->d.rdev);
if (retval) if (retval)
return retval; goto out;
} }
if (ir->buf) if (ir->buf)
...@@ -268,24 +282,26 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) ...@@ -268,24 +282,26 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
lirc_init_pdata(inode, file); lirc_init_pdata(inode, file);
nonseekable_open(inode, file); nonseekable_open(inode, file);
mutex_unlock(&ir->mutex);
return 0; return 0;
out:
mutex_unlock(&ir->mutex);
return retval;
} }
EXPORT_SYMBOL(lirc_dev_fop_open); EXPORT_SYMBOL(lirc_dev_fop_open);
int lirc_dev_fop_close(struct inode *inode, struct file *file) int lirc_dev_fop_close(struct inode *inode, struct file *file)
{ {
struct irctl *ir = file->private_data; struct irctl *ir = file->private_data;
int ret;
ret = mutex_lock_killable(&lirc_dev_lock); mutex_lock(&ir->mutex);
WARN_ON(ret);
rc_close(ir->d.rdev); rc_close(ir->d.rdev);
ir->open--; ir->open--;
if (!ret)
mutex_unlock(&lirc_dev_lock); mutex_unlock(&ir->mutex);
return 0; return 0;
} }
...@@ -320,19 +336,20 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -320,19 +336,20 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct irctl *ir = file->private_data; struct irctl *ir = file->private_data;
__u32 mode; __u32 mode;
int result = 0; int result;
dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
ir->d.name, ir->d.minor, cmd); ir->d.name, ir->d.minor, cmd);
result = mutex_lock_interruptible(&ir->mutex);
if (result)
return result;
if (!ir->attached) { if (!ir->attached) {
dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", result = -ENODEV;
ir->d.name, ir->d.minor); goto out;
return -ENODEV;
} }
mutex_lock(&ir->irctl_lock);
switch (cmd) { switch (cmd) {
case LIRC_GET_FEATURES: case LIRC_GET_FEATURES:
result = put_user(ir->d.features, (__u32 __user *)arg); result = put_user(ir->d.features, (__u32 __user *)arg);
...@@ -386,8 +403,8 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -386,8 +403,8 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
result = -ENOTTY; result = -ENOTTY;
} }
mutex_unlock(&ir->irctl_lock); out:
mutex_unlock(&ir->mutex);
return result; return result;
} }
EXPORT_SYMBOL(lirc_dev_fop_ioctl); EXPORT_SYMBOL(lirc_dev_fop_ioctl);
...@@ -399,27 +416,31 @@ ssize_t lirc_dev_fop_read(struct file *file, ...@@ -399,27 +416,31 @@ ssize_t lirc_dev_fop_read(struct file *file,
{ {
struct irctl *ir = file->private_data; struct irctl *ir = file->private_data;
unsigned char *buf; unsigned char *buf;
int ret = 0, written = 0; int ret, written = 0;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
if (!LIRC_CAN_REC(ir->d.features))
return -EINVAL;
dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
buf = kzalloc(ir->buf->chunk_size, GFP_KERNEL); buf = kzalloc(ir->buf->chunk_size, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
if (mutex_lock_interruptible(&ir->irctl_lock)) { ret = mutex_lock_interruptible(&ir->mutex);
ret = -ERESTARTSYS; if (ret) {
goto out_unlocked; kfree(buf);
return ret;
} }
if (!ir->attached) { if (!ir->attached) {
ret = -ENODEV; ret = -ENODEV;
goto out_locked; goto out_locked;
} }
if (!LIRC_CAN_REC(ir->d.features)) {
ret = -EINVAL;
goto out_locked;
}
if (length % ir->buf->chunk_size) { if (length % ir->buf->chunk_size) {
ret = -EINVAL; ret = -EINVAL;
goto out_locked; goto out_locked;
...@@ -454,13 +475,13 @@ ssize_t lirc_dev_fop_read(struct file *file, ...@@ -454,13 +475,13 @@ ssize_t lirc_dev_fop_read(struct file *file,
break; break;
} }
mutex_unlock(&ir->irctl_lock); mutex_unlock(&ir->mutex);
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule(); schedule();
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
if (mutex_lock_interruptible(&ir->irctl_lock)) { ret = mutex_lock_interruptible(&ir->mutex);
ret = -ERESTARTSYS; if (ret) {
remove_wait_queue(&ir->buf->wait_poll, &wait); remove_wait_queue(&ir->buf->wait_poll, &wait);
goto out_unlocked; goto out_unlocked;
} }
...@@ -483,7 +504,7 @@ ssize_t lirc_dev_fop_read(struct file *file, ...@@ -483,7 +504,7 @@ ssize_t lirc_dev_fop_read(struct file *file,
remove_wait_queue(&ir->buf->wait_poll, &wait); remove_wait_queue(&ir->buf->wait_poll, &wait);
out_locked: out_locked:
mutex_unlock(&ir->irctl_lock); mutex_unlock(&ir->mutex);
out_unlocked: out_unlocked:
kfree(buf); kfree(buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment