From 37efd09ba2c62e46e3a3a793025c186f8cd1a417 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 7 Aug 2026 11:50:22 +0100 Subject: [PATCH] media: rc: Ensure registered is cleared in error path If rc_register_device() fails, ensure that registered is not set to true. If lirc_register() succeeded, then userspace could have an open file descriptor open. This leads to a use-after-free. lirc_unregister() must be called with registered = false, else there is a possibility that an fd is open and polling, which would never return POLLERR unless registered is false. Note that registered must be changed under dev->lock, many functions read registered under lock (e.g. lirc_ioctl). Fixes: dccc0c3ddf8f ("media: rc: fix race between unregister and urb/irq callbacks") Signed-off-by: Sean Young Cc: stable@vger.kernel.org Acked-by: Hans Verkuil --- drivers/media/rc/rc-main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d93e98189c1a..930d45517229 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1934,7 +1934,8 @@ int rc_register_device(struct rc_dev *dev) goto out_raw; } - dev->registered = true; + scoped_guard(mutex, &dev->lock) + dev->registered = true; rc = device_add(&dev->dev); if (rc) @@ -1954,7 +1955,7 @@ int rc_register_device(struct rc_dev *dev) if (dev->allowed_protocols != RC_PROTO_BIT_CEC) { rc = lirc_register(dev); if (rc < 0) - goto out_dev; + goto out_lirc; } if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { @@ -1977,11 +1978,18 @@ int rc_register_device(struct rc_dev *dev) out_rx: rc_free_rx_device(dev); out_lirc: - if (dev->allowed_protocols != RC_PROTO_BIT_CEC) + scoped_guard(mutex, &dev->lock) + dev->registered = false; + + if (device_is_registered(&dev->lirc_dev)) lirc_unregister(dev); -out_dev: device_del(&dev->dev); + /* registered already cleared above */ + goto out_free_table; out_rx_free: + scoped_guard(mutex, &dev->lock) + dev->registered = false; +out_free_table: ir_free_table(&dev->rc_map); out_raw: ida_free(&rc_ida, minor);