[media] ir-lirc-codec: let lirc_dev handle the lirc_buffer

ir_lirc_register() currently creates its own lirc_buffer before
passing the lirc_driver to lirc_register_driver().

When a module is later unloaded, ir_lirc_unregister() gets called
which performs a call to lirc_unregister_driver() and then free():s
the lirc_buffer.

The problem is that:

a) there can still be a userspace app holding an open lirc fd
   when lirc_unregister_driver() returns; and

b) the lirc_buffer contains "wait_queue_head_t wait_poll" which
   is potentially used as long as any userspace app is still around.

The result is an oops which can be triggered quite easily by a
userspace app monitoring its lirc fd using epoll() and not closing
the fd promptly on device removal.

The minimalistic fix is to let lirc_dev create the lirc_buffer since
lirc_dev will then also free the buffer once it believes it is safe to
do so.

Signed-off-by: David Härdeman <david@hardeman.nu>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
David Härdeman
2017-05-01 10:32:34 -03:00
committed by Mauro Carvalho Chehab
parent b2aceb739b
commit 0f7c4063f8
2 changed files with 19 additions and 19 deletions

View File

@@ -52,6 +52,7 @@ struct irctl {
struct mutex irctl_lock;
struct lirc_buffer *buf;
bool buf_internal;
unsigned int chunk_size;
struct device dev;
@@ -83,7 +84,7 @@ static void lirc_release(struct device *ld)
put_device(ir->dev.parent);
if (ir->buf != ir->d.rbuf) {
if (ir->buf_internal) {
lirc_buffer_free(ir->buf);
kfree(ir->buf);
}
@@ -198,6 +199,7 @@ static int lirc_allocate_buffer(struct irctl *ir)
if (d->rbuf) {
ir->buf = d->rbuf;
ir->buf_internal = false;
} else {
ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
if (!ir->buf) {
@@ -208,8 +210,11 @@ static int lirc_allocate_buffer(struct irctl *ir)
err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
if (err) {
kfree(ir->buf);
ir->buf = NULL;
goto out;
}
ir->buf_internal = true;
}
ir->chunk_size = ir->buf->chunk_size;
@@ -362,6 +367,12 @@ int lirc_register_driver(struct lirc_driver *d)
err = lirc_allocate_buffer(irctls[minor]);
if (err)
lirc_unregister_driver(minor);
else
/*
* This is kind of a hack but ir-lirc-codec needs
* access to the buffer that lirc_dev allocated.
*/
d->rbuf = irctls[minor]->buf;
}
return err ? err : minor;