--- /dev/null
+From stable+bounces-183326-greg=kroah.com@vger.kernel.org Fri Oct 3 22:43:33 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 16:43:23 -0400
+Subject: media: i2c: tc358743: Fix use-after-free bugs caused by orphan timer in probe
+To: stable@vger.kernel.org
+Cc: Duoming Zhou <duoming@zju.edu.cn>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251003204323.3377744-1-sashal@kernel.org>
+
+From: Duoming Zhou <duoming@zju.edu.cn>
+
+[ Upstream commit 79d10f4f21a92e459b2276a77be62c59c1502c9d ]
+
+The state->timer is a cyclic timer that schedules work_i2c_poll and
+delayed_work_enable_hotplug, while rearming itself. Using timer_delete()
+fails to guarantee the timer isn't still running when destroyed, similarly
+cancel_delayed_work() cannot ensure delayed_work_enable_hotplug has
+terminated if already executing. During probe failure after timer
+initialization, these may continue running as orphans and reference the
+already-freed tc358743_state object through tc358743_irq_poll_timer.
+
+The following is the trace captured by KASAN.
+
+BUG: KASAN: slab-use-after-free in __run_timer_base.part.0+0x7d7/0x8c0
+Write of size 8 at addr ffff88800ded83c8 by task swapper/1/0
+...
+Call Trace:
+ <IRQ>
+ dump_stack_lvl+0x55/0x70
+ print_report+0xcf/0x610
+ ? __pfx_sched_balance_find_src_group+0x10/0x10
+ ? __run_timer_base.part.0+0x7d7/0x8c0
+ kasan_report+0xb8/0xf0
+ ? __run_timer_base.part.0+0x7d7/0x8c0
+ __run_timer_base.part.0+0x7d7/0x8c0
+ ? rcu_sched_clock_irq+0xb06/0x27d0
+ ? __pfx___run_timer_base.part.0+0x10/0x10
+ ? try_to_wake_up+0xb15/0x1960
+ ? tmigr_update_events+0x280/0x740
+ ? _raw_spin_lock_irq+0x80/0xe0
+ ? __pfx__raw_spin_lock_irq+0x10/0x10
+ tmigr_handle_remote_up+0x603/0x7e0
+ ? __pfx_tmigr_handle_remote_up+0x10/0x10
+ ? sched_balance_trigger+0x98/0x9f0
+ ? sched_tick+0x221/0x5a0
+ ? _raw_spin_lock_irq+0x80/0xe0
+ ? __pfx__raw_spin_lock_irq+0x10/0x10
+ ? tick_nohz_handler+0x339/0x440
+ ? __pfx_tmigr_handle_remote_up+0x10/0x10
+ __walk_groups.isra.0+0x42/0x150
+ tmigr_handle_remote+0x1f4/0x2e0
+ ? __pfx_tmigr_handle_remote+0x10/0x10
+ ? ktime_get+0x60/0x140
+ ? lapic_next_event+0x11/0x20
+ ? clockevents_program_event+0x1d4/0x2a0
+ ? hrtimer_interrupt+0x322/0x780
+ handle_softirqs+0x16a/0x550
+ irq_exit_rcu+0xaf/0xe0
+ sysvec_apic_timer_interrupt+0x70/0x80
+ </IRQ>
+...
+
+Allocated by task 141:
+ kasan_save_stack+0x24/0x50
+ kasan_save_track+0x14/0x30
+ __kasan_kmalloc+0x7f/0x90
+ __kmalloc_node_track_caller_noprof+0x198/0x430
+ devm_kmalloc+0x7b/0x1e0
+ tc358743_probe+0xb7/0x610 i2c_device_probe+0x51d/0x880
+ really_probe+0x1ca/0x5c0
+ __driver_probe_device+0x248/0x310
+ driver_probe_device+0x44/0x120
+ __device_attach_driver+0x174/0x220
+ bus_for_each_drv+0x100/0x190
+ __device_attach+0x206/0x370
+ bus_probe_device+0x123/0x170
+ device_add+0xd25/0x1470
+ i2c_new_client_device+0x7a0/0xcd0
+ do_one_initcall+0x89/0x300
+ do_init_module+0x29d/0x7f0
+ load_module+0x4f48/0x69e0
+ init_module_from_file+0xe4/0x150
+ idempotent_init_module+0x320/0x670
+ __x64_sys_finit_module+0xbd/0x120
+ do_syscall_64+0xac/0x280
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+Freed by task 141:
+ kasan_save_stack+0x24/0x50
+ kasan_save_track+0x14/0x30
+ kasan_save_free_info+0x3a/0x60
+ __kasan_slab_free+0x3f/0x50
+ kfree+0x137/0x370
+ release_nodes+0xa4/0x100
+ devres_release_group+0x1b2/0x380
+ i2c_device_probe+0x694/0x880
+ really_probe+0x1ca/0x5c0
+ __driver_probe_device+0x248/0x310
+ driver_probe_device+0x44/0x120
+ __device_attach_driver+0x174/0x220
+ bus_for_each_drv+0x100/0x190
+ __device_attach+0x206/0x370
+ bus_probe_device+0x123/0x170
+ device_add+0xd25/0x1470
+ i2c_new_client_device+0x7a0/0xcd0
+ do_one_initcall+0x89/0x300
+ do_init_module+0x29d/0x7f0
+ load_module+0x4f48/0x69e0
+ init_module_from_file+0xe4/0x150
+ idempotent_init_module+0x320/0x670
+ __x64_sys_finit_module+0xbd/0x120
+ do_syscall_64+0xac/0x280
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+...
+
+Replace timer_delete() with timer_delete_sync() and cancel_delayed_work()
+with cancel_delayed_work_sync() to ensure proper termination of timer and
+work items before resource cleanup.
+
+This bug was initially identified through static analysis. For reproduction
+and testing, I created a functional emulation of the tc358743 device via a
+kernel module and introduced faults through the debugfs interface.
+
+Fixes: 869f38ae07f7 ("media: i2c: tc358743: Fix crash in the probe error path when using polling")
+Fixes: d32d98642de6 ("[media] Driver for Toshiba TC358743 HDMI to CSI-2 bridge")
+Cc: stable@vger.kernel.org
+Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+[ replaced del_timer() instead of timer_delete() ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/i2c/tc358743.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/tc358743.c
++++ b/drivers/media/i2c/tc358743.c
+@@ -2200,10 +2200,10 @@ static int tc358743_probe(struct i2c_cli
+ err_work_queues:
+ cec_unregister_adapter(state->cec_adap);
+ if (!state->i2c_client->irq) {
+- del_timer(&state->timer);
++ timer_delete_sync(&state->timer);
+ flush_work(&state->work_i2c_poll);
+ }
+- cancel_delayed_work(&state->delayed_work_enable_hotplug);
++ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug);
+ mutex_destroy(&state->confctl_mutex);
+ err_hdl:
+ media_entity_cleanup(&sd->entity);
--- /dev/null
+From stable+bounces-183334-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:48 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 16:55:36 -0400
+Subject: media: imon: grab lock earlier in imon_ir_change_protocol()
+To: stable@vger.kernel.org
+Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>, Sean Young <sean@mess.org>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251003205537.3386848-3-sashal@kernel.org>
+
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+
+[ Upstream commit 7019553ab850ce1d3f0e512e16d14ab153f91c04 ]
+
+Move mutex_trylock() in imon_ir_change_protocol() to the beginning,
+for memcpy() which modifies ictx->usb_tx_buf should be protected by
+ictx->lock.
+
+Also, verify at the beginning of send_packet() that ictx->lock is held
+in case send_packet() is by error called from imon_ir_change_protocol()
+when mutex_trylock() failed due to concurrent requests.
+
+Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Stable-dep-of: fa0f61cc1d82 ("media: rc: fix races with imon_disconnect()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/rc/imon.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -598,6 +598,8 @@ static int send_packet(struct imon_conte
+ int retval = 0;
+ struct usb_ctrlrequest *control_req = NULL;
+
++ lockdep_assert_held(&ictx->lock);
++
+ /* Check if we need to use control or interrupt urb */
+ if (!ictx->tx_control) {
+ pipe = usb_sndintpipe(ictx->usbdev_intf0,
+@@ -1126,7 +1128,7 @@ static int imon_ir_change_protocol(struc
+ int retval;
+ struct imon_context *ictx = rc->priv;
+ struct device *dev = ictx->dev;
+- bool unlock = false;
++ const bool unlock = mutex_trylock(&ictx->lock);
+ unsigned char ir_proto_packet[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
+
+@@ -1153,8 +1155,6 @@ static int imon_ir_change_protocol(struc
+
+ memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
+
+- unlock = mutex_trylock(&ictx->lock);
+-
+ retval = send_packet(ictx);
+ if (retval)
+ goto out;
--- /dev/null
+From stable+bounces-183333-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:46 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 16:55:35 -0400
+Subject: media: imon: reorganize serialization
+To: stable@vger.kernel.org
+Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>, syzbot <syzbot+c558267ad910fc494497@syzkaller.appspotmail.com>, Alan Stern <stern@rowland.harvard.edu>, Sean Young <sean@mess.org>, Mauro Carvalho Chehab <mchehab@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251003205537.3386848-2-sashal@kernel.org>
+
+From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+
+[ Upstream commit db264d4c66c0fe007b5d19fd007707cd0697603d ]
+
+Since usb_register_dev() from imon_init_display() from imon_probe() holds
+minor_rwsem while display_open() which holds driver_lock and ictx->lock is
+called with minor_rwsem held from usb_open(), holding driver_lock or
+ictx->lock when calling usb_register_dev() causes circular locking
+dependency problem.
+
+Since usb_deregister_dev() from imon_disconnect() holds minor_rwsem while
+display_open() which holds driver_lock is called with minor_rwsem held,
+holding driver_lock when calling usb_deregister_dev() also causes circular
+locking dependency problem.
+
+Sean Young explained that the problem is there are imon devices which have
+two usb interfaces, even though it is one device. The probe and disconnect
+function of both usb interfaces can run concurrently.
+
+Alan Stern responded that the driver and USB cores guarantee that when an
+interface is probed, both the interface and its USB device are locked.
+Ditto for when the disconnect callback gets run. So concurrent probing/
+disconnection of multiple interfaces on the same device is not possible.
+
+Therefore, we don't need locks for handling race between imon_probe() and
+imon_disconnect(). But we still need to handle race between display_open()
+/vfd_write()/lcd_write()/display_close() and imon_disconnect(), for
+disconnect event can happen while file descriptors are in use.
+
+Since "struct file"->private_data is set by display_open(), vfd_write()/
+lcd_write()/display_close() can assume that "struct file"->private_data
+is not NULL even after usb_set_intfdata(interface, NULL) was called.
+
+Replace insufficiently held driver_lock with refcount_t based management.
+Add a boolean flag for recording whether imon_disconnect() was already
+called. Use RCU for accessing this boolean flag and refcount_t.
+
+Since the boolean flag for imon_disconnect() is shared, disconnect event
+on either intf0 or intf1 affects both interfaces. But I assume that this
+change does not matter, for usually disconnect event would not happen
+while interfaces are in use.
+
+Link: https://syzkaller.appspot.com/bug?extid=c558267ad910fc494497
+
+Reported-by: syzbot <syzbot+c558267ad910fc494497@syzkaller.appspotmail.com>
+Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+Tested-by: syzbot <syzbot+c558267ad910fc494497@syzkaller.appspotmail.com>
+Cc: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+Stable-dep-of: fa0f61cc1d82 ("media: rc: fix races with imon_disconnect()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/rc/imon.c | 99 ++++++++++++++++++++++--------------------------
+ 1 file changed, 47 insertions(+), 52 deletions(-)
+
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -153,6 +153,24 @@ struct imon_context {
+ const struct imon_usb_dev_descr *dev_descr;
+ /* device description with key */
+ /* table for front panels */
++ /*
++ * Fields for deferring free_imon_context().
++ *
++ * Since reference to "struct imon_context" is stored into
++ * "struct file"->private_data, we need to remember
++ * how many file descriptors might access this "struct imon_context".
++ */
++ refcount_t users;
++ /*
++ * Use a flag for telling display_open()/vfd_write()/lcd_write() that
++ * imon_disconnect() was already called.
++ */
++ bool disconnected;
++ /*
++ * We need to wait for RCU grace period in order to allow
++ * display_open() to safely check ->disconnected and increment ->users.
++ */
++ struct rcu_head rcu;
+ };
+
+ #define TOUCH_TIMEOUT (HZ/30)
+@@ -160,18 +178,18 @@ struct imon_context {
+ /* vfd character device file operations */
+ static const struct file_operations vfd_fops = {
+ .owner = THIS_MODULE,
+- .open = &display_open,
+- .write = &vfd_write,
+- .release = &display_close,
++ .open = display_open,
++ .write = vfd_write,
++ .release = display_close,
+ .llseek = noop_llseek,
+ };
+
+ /* lcd character device file operations */
+ static const struct file_operations lcd_fops = {
+ .owner = THIS_MODULE,
+- .open = &display_open,
+- .write = &lcd_write,
+- .release = &display_close,
++ .open = display_open,
++ .write = lcd_write,
++ .release = display_close,
+ .llseek = noop_llseek,
+ };
+
+@@ -439,9 +457,6 @@ static struct usb_driver imon_driver = {
+ .id_table = imon_usb_id_table,
+ };
+
+-/* to prevent races between open() and disconnect(), probing, etc */
+-static DEFINE_MUTEX(driver_lock);
+-
+ /* Module bookkeeping bits */
+ MODULE_AUTHOR(MOD_AUTHOR);
+ MODULE_DESCRIPTION(MOD_DESC);
+@@ -481,9 +496,11 @@ static void free_imon_context(struct imo
+ struct device *dev = ictx->dev;
+
+ usb_free_urb(ictx->tx_urb);
++ WARN_ON(ictx->dev_present_intf0);
+ usb_free_urb(ictx->rx_urb_intf0);
++ WARN_ON(ictx->dev_present_intf1);
+ usb_free_urb(ictx->rx_urb_intf1);
+- kfree(ictx);
++ kfree_rcu(ictx, rcu);
+
+ dev_dbg(dev, "%s: iMON context freed\n", __func__);
+ }
+@@ -499,9 +516,6 @@ static int display_open(struct inode *in
+ int subminor;
+ int retval = 0;
+
+- /* prevent races with disconnect */
+- mutex_lock(&driver_lock);
+-
+ subminor = iminor(inode);
+ interface = usb_find_interface(&imon_driver, subminor);
+ if (!interface) {
+@@ -509,13 +523,16 @@ static int display_open(struct inode *in
+ retval = -ENODEV;
+ goto exit;
+ }
+- ictx = usb_get_intfdata(interface);
+
+- if (!ictx) {
++ rcu_read_lock();
++ ictx = usb_get_intfdata(interface);
++ if (!ictx || ictx->disconnected || !refcount_inc_not_zero(&ictx->users)) {
++ rcu_read_unlock();
+ pr_err("no context found for minor %d\n", subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
++ rcu_read_unlock();
+
+ mutex_lock(&ictx->lock);
+
+@@ -533,8 +550,10 @@ static int display_open(struct inode *in
+
+ mutex_unlock(&ictx->lock);
+
++ if (retval && refcount_dec_and_test(&ictx->users))
++ free_imon_context(ictx);
++
+ exit:
+- mutex_unlock(&driver_lock);
+ return retval;
+ }
+
+@@ -544,16 +563,9 @@ exit:
+ */
+ static int display_close(struct inode *inode, struct file *file)
+ {
+- struct imon_context *ictx = NULL;
++ struct imon_context *ictx = file->private_data;
+ int retval = 0;
+
+- ictx = file->private_data;
+-
+- if (!ictx) {
+- pr_err("no context for device\n");
+- return -ENODEV;
+- }
+-
+ mutex_lock(&ictx->lock);
+
+ if (!ictx->display_supported) {
+@@ -568,6 +580,8 @@ static int display_close(struct inode *i
+ }
+
+ mutex_unlock(&ictx->lock);
++ if (refcount_dec_and_test(&ictx->users))
++ free_imon_context(ictx);
+ return retval;
+ }
+
+@@ -936,15 +950,12 @@ static ssize_t vfd_write(struct file *fi
+ int offset;
+ int seq;
+ int retval = 0;
+- struct imon_context *ictx;
++ struct imon_context *ictx = file->private_data;
+ static const unsigned char vfd_packet6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+
+- ictx = file->private_data;
+- if (!ictx) {
+- pr_err_ratelimited("no context for device\n");
++ if (ictx->disconnected)
+ return -ENODEV;
+- }
+
+ if (mutex_lock_interruptible(&ictx->lock))
+ return -ERESTARTSYS;
+@@ -1021,13 +1032,10 @@ static ssize_t lcd_write(struct file *fi
+ size_t n_bytes, loff_t *pos)
+ {
+ int retval = 0;
+- struct imon_context *ictx;
++ struct imon_context *ictx = file->private_data;
+
+- ictx = file->private_data;
+- if (!ictx) {
+- pr_err_ratelimited("no context for device\n");
++ if (ictx->disconnected)
+ return -ENODEV;
+- }
+
+ mutex_lock(&ictx->lock);
+
+@@ -2402,7 +2410,6 @@ static int imon_probe(struct usb_interfa
+ int ifnum, sysfs_err;
+ int ret = 0;
+ struct imon_context *ictx = NULL;
+- struct imon_context *first_if_ctx = NULL;
+ u16 vendor, product;
+
+ usbdev = usb_get_dev(interface_to_usbdev(interface));
+@@ -2414,17 +2421,12 @@ static int imon_probe(struct usb_interfa
+ dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
+ __func__, vendor, product, ifnum);
+
+- /* prevent races probing devices w/multiple interfaces */
+- mutex_lock(&driver_lock);
+-
+ first_if = usb_ifnum_to_if(usbdev, 0);
+ if (!first_if) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+- first_if_ctx = usb_get_intfdata(first_if);
+-
+ if (ifnum == 0) {
+ ictx = imon_init_intf0(interface, id);
+ if (!ictx) {
+@@ -2432,9 +2434,11 @@ static int imon_probe(struct usb_interfa
+ ret = -ENODEV;
+ goto fail;
+ }
++ refcount_set(&ictx->users, 1);
+
+ } else {
+ /* this is the secondary interface on the device */
++ struct imon_context *first_if_ctx = usb_get_intfdata(first_if);
+
+ /* fail early if first intf failed to register */
+ if (!first_if_ctx) {
+@@ -2448,14 +2452,13 @@ static int imon_probe(struct usb_interfa
+ ret = -ENODEV;
+ goto fail;
+ }
++ refcount_inc(&ictx->users);
+
+ }
+
+ usb_set_intfdata(interface, ictx);
+
+ if (ifnum == 0) {
+- mutex_lock(&ictx->lock);
+-
+ if (product == 0xffdc && ictx->rf_device) {
+ sysfs_err = sysfs_create_group(&interface->dev.kobj,
+ &imon_rf_attr_group);
+@@ -2466,21 +2469,17 @@ static int imon_probe(struct usb_interfa
+
+ if (ictx->display_supported)
+ imon_init_display(ictx, interface);
+-
+- mutex_unlock(&ictx->lock);
+ }
+
+ dev_info(dev, "iMON device (%04x:%04x, intf%d) on usb<%d:%d> initialized\n",
+ vendor, product, ifnum,
+ usbdev->bus->busnum, usbdev->devnum);
+
+- mutex_unlock(&driver_lock);
+ usb_put_dev(usbdev);
+
+ return 0;
+
+ fail:
+- mutex_unlock(&driver_lock);
+ usb_put_dev(usbdev);
+ dev_err(dev, "unable to register, err %d\n", ret);
+
+@@ -2496,10 +2495,8 @@ static void imon_disconnect(struct usb_i
+ struct device *dev;
+ int ifnum;
+
+- /* prevent races with multi-interface device probing and display_open */
+- mutex_lock(&driver_lock);
+-
+ ictx = usb_get_intfdata(interface);
++ ictx->disconnected = true;
+ dev = ictx->dev;
+ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+
+@@ -2540,11 +2537,9 @@ static void imon_disconnect(struct usb_i
+ }
+ }
+
+- if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1)
++ if (refcount_dec_and_test(&ictx->users))
+ free_imon_context(ictx);
+
+- mutex_unlock(&driver_lock);
+-
+ dev_dbg(dev, "%s: iMON device (intf%d) disconnected\n",
+ __func__, ifnum);
+ }
--- /dev/null
+From stable+bounces-183332-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:46 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 16:55:34 -0400
+Subject: media: rc: Add support for another iMON 0xffdc device
+To: stable@vger.kernel.org
+Cc: Flavius Georgescu <pretoriano.mp@gmail.com>, Chris Vandomelen <chris@sightworks.com>, Sean Young <sean@mess.org>, Mauro Carvalho Chehab <mchehab+samsung@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251003205537.3386848-1-sashal@kernel.org>
+
+From: Flavius Georgescu <pretoriano.mp@gmail.com>
+
+[ Upstream commit cf330691668a3bee37b8ac8212709b3ccdd87997 ]
+
+The device it's an iMON UltraBay (0x98 in config byte) with LCD,
+IR and dual-knobs front panel.
+
+To work properly the device also require its own key table,
+and repeat suppression for all buttons.
+
+Signed-off-by: Flavius Georgescu <pretoriano.mp@gmail.com>
+Co-developed-by: Chris Vandomelen <chris@sightworks.com>
+Signed-off-by: Chris Vandomelen <chris@sightworks.com>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+Stable-dep-of: fa0f61cc1d82 ("media: rc: fix races with imon_disconnect()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/rc/imon.c | 61 ++++++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 51 insertions(+), 10 deletions(-)
+
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -83,6 +83,7 @@ struct imon_usb_dev_descr {
+ __u16 flags;
+ #define IMON_NO_FLAGS 0
+ #define IMON_NEED_20MS_PKT_DELAY 1
++#define IMON_SUPPRESS_REPEATED_KEYS 2
+ struct imon_panel_key_table key_table[];
+ };
+
+@@ -149,8 +150,9 @@ struct imon_context {
+ struct timer_list ttimer; /* touch screen timer */
+ int touch_x; /* x coordinate on touchscreen */
+ int touch_y; /* y coordinate on touchscreen */
+- struct imon_usb_dev_descr *dev_descr; /* device description with key
+- table for front panels */
++ const struct imon_usb_dev_descr *dev_descr;
++ /* device description with key */
++ /* table for front panels */
+ };
+
+ #define TOUCH_TIMEOUT (HZ/30)
+@@ -315,6 +317,32 @@ static const struct imon_usb_dev_descr i
+ }
+ };
+
++/* imon ultrabay front panel key table */
++static const struct imon_usb_dev_descr ultrabay_table = {
++ .flags = IMON_SUPPRESS_REPEATED_KEYS,
++ .key_table = {
++ { 0x0000000f0000ffeell, KEY_MEDIA }, /* Go */
++ { 0x000000000100ffeell, KEY_UP },
++ { 0x000000000001ffeell, KEY_DOWN },
++ { 0x000000160000ffeell, KEY_ENTER },
++ { 0x0000001f0000ffeell, KEY_AUDIO }, /* Music */
++ { 0x000000200000ffeell, KEY_VIDEO }, /* Movie */
++ { 0x000000210000ffeell, KEY_CAMERA }, /* Photo */
++ { 0x000000270000ffeell, KEY_DVD }, /* DVD */
++ { 0x000000230000ffeell, KEY_TV }, /* TV */
++ { 0x000000050000ffeell, KEY_PREVIOUS }, /* Previous */
++ { 0x000000070000ffeell, KEY_REWIND },
++ { 0x000000040000ffeell, KEY_STOP },
++ { 0x000000020000ffeell, KEY_PLAYPAUSE },
++ { 0x000000080000ffeell, KEY_FASTFORWARD },
++ { 0x000000060000ffeell, KEY_NEXT }, /* Next */
++ { 0x000100000000ffeell, KEY_VOLUMEUP },
++ { 0x010000000000ffeell, KEY_VOLUMEDOWN },
++ { 0x000000010000ffeell, KEY_MUTE },
++ { 0, KEY_RESERVED },
++ }
++};
++
+ /*
+ * USB Device ID for iMON USB Control Boards
+ *
+@@ -1261,9 +1289,11 @@ static u32 imon_mce_key_lookup(struct im
+
+ static u32 imon_panel_key_lookup(struct imon_context *ictx, u64 code)
+ {
+- int i;
++ const struct imon_panel_key_table *key_table;
+ u32 keycode = KEY_RESERVED;
+- struct imon_panel_key_table *key_table = ictx->dev_descr->key_table;
++ int i;
++
++ key_table = ictx->dev_descr->key_table;
+
+ for (i = 0; key_table[i].hw_code != 0; i++) {
+ if (key_table[i].hw_code == (code | 0xffee)) {
+@@ -1547,7 +1577,6 @@ static void imon_incoming_packet(struct
+ u32 kc;
+ u64 scancode;
+ int press_type = 0;
+- long msec;
+ ktime_t t;
+ static ktime_t prev_time;
+ u8 ktype;
+@@ -1649,14 +1678,16 @@ static void imon_incoming_packet(struct
+ spin_lock_irqsave(&ictx->kc_lock, flags);
+
+ t = ktime_get();
+- /* KEY_MUTE repeats from knob need to be suppressed */
+- if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) {
+- msec = ktime_ms_delta(t, prev_time);
+- if (msec < ictx->idev->rep[REP_DELAY]) {
++ /* KEY repeats from knob and panel that need to be suppressed */
++ if (ictx->kc == KEY_MUTE ||
++ ictx->dev_descr->flags & IMON_SUPPRESS_REPEATED_KEYS) {
++ if (ictx->kc == ictx->last_keycode &&
++ ktime_ms_delta(t, prev_time) < ictx->idev->rep[REP_DELAY]) {
+ spin_unlock_irqrestore(&ictx->kc_lock, flags);
+ return;
+ }
+ }
++
+ prev_time = t;
+ kc = ictx->kc;
+
+@@ -1844,6 +1875,14 @@ static void imon_get_ffdc_type(struct im
+ dev_info(ictx->dev, "0xffdc iMON Inside, iMON IR");
+ ictx->display_supported = false;
+ break;
++ /* Soundgraph iMON UltraBay */
++ case 0x98:
++ dev_info(ictx->dev, "0xffdc iMON UltraBay, LCD + IR");
++ detected_display_type = IMON_DISPLAY_TYPE_LCD;
++ allowed_protos = RC_PROTO_BIT_IMON | RC_PROTO_BIT_RC6_MCE;
++ ictx->dev_descr = &ultrabay_table;
++ break;
++
+ default:
+ dev_info(ictx->dev, "Unknown 0xffdc device, defaulting to VFD and iMON IR");
+ detected_display_type = IMON_DISPLAY_TYPE_VFD;
+@@ -1975,10 +2014,12 @@ out:
+
+ static struct input_dev *imon_init_idev(struct imon_context *ictx)
+ {
+- struct imon_panel_key_table *key_table = ictx->dev_descr->key_table;
++ const struct imon_panel_key_table *key_table;
+ struct input_dev *idev;
+ int ret, i;
+
++ key_table = ictx->dev_descr->key_table;
++
+ idev = input_allocate_device();
+ if (!idev)
+ goto out;
--- /dev/null
+From stable+bounces-183335-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:49 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 16:55:37 -0400
+Subject: media: rc: fix races with imon_disconnect()
+To: stable@vger.kernel.org
+Cc: Larshin Sergey <Sergey.Larshin@kaspersky.com>, syzbot+f1a69784f6efe748c3bf@syzkaller.appspotmail.com, Sean Young <sean@mess.org>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251003205537.3386848-4-sashal@kernel.org>
+
+From: Larshin Sergey <Sergey.Larshin@kaspersky.com>
+
+[ Upstream commit fa0f61cc1d828178aa921475a9b786e7fbb65ccb ]
+
+Syzbot reports a KASAN issue as below:
+BUG: KASAN: use-after-free in __create_pipe include/linux/usb.h:1945 [inline]
+BUG: KASAN: use-after-free in send_packet+0xa2d/0xbc0 drivers/media/rc/imon.c:627
+Read of size 4 at addr ffff8880256fb000 by task syz-executor314/4465
+
+CPU: 2 PID: 4465 Comm: syz-executor314 Not tainted 6.0.0-rc1-syzkaller #0
+Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.14.0-2 04/01/2014
+Call Trace:
+ <TASK>
+__dump_stack lib/dump_stack.c:88 [inline]
+dump_stack_lvl+0xcd/0x134 lib/dump_stack.c:106
+print_address_description mm/kasan/report.c:317 [inline]
+print_report.cold+0x2ba/0x6e9 mm/kasan/report.c:433
+kasan_report+0xb1/0x1e0 mm/kasan/report.c:495
+__create_pipe include/linux/usb.h:1945 [inline]
+send_packet+0xa2d/0xbc0 drivers/media/rc/imon.c:627
+vfd_write+0x2d9/0x550 drivers/media/rc/imon.c:991
+vfs_write+0x2d7/0xdd0 fs/read_write.c:576
+ksys_write+0x127/0x250 fs/read_write.c:631
+do_syscall_x64 arch/x86/entry/common.c:50 [inline]
+do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
+entry_SYSCALL_64_after_hwframe+0x63/0xcd
+
+The iMON driver improperly releases the usb_device reference in
+imon_disconnect without coordinating with active users of the
+device.
+
+Specifically, the fields usbdev_intf0 and usbdev_intf1 are not
+protected by the users counter (ictx->users). During probe,
+imon_init_intf0 or imon_init_intf1 increments the usb_device
+reference count depending on the interface. However, during
+disconnect, usb_put_dev is called unconditionally, regardless of
+actual usage.
+
+As a result, if vfd_write or other operations are still in
+progress after disconnect, this can lead to a use-after-free of
+the usb_device pointer.
+
+Thread 1 vfd_write Thread 2 imon_disconnect
+ ...
+ if
+ usb_put_dev(ictx->usbdev_intf0)
+ else
+ usb_put_dev(ictx->usbdev_intf1)
+...
+while
+ send_packet
+ if
+ pipe = usb_sndintpipe(
+ ictx->usbdev_intf0) UAF
+ else
+ pipe = usb_sndctrlpipe(
+ ictx->usbdev_intf0, 0) UAF
+
+Guard access to usbdev_intf0 and usbdev_intf1 after disconnect by
+checking ictx->disconnected in all writer paths. Add early return
+with -ENODEV in send_packet(), vfd_write(), lcd_write() and
+display_open() if the device is no longer present.
+
+Set and read ictx->disconnected under ictx->lock to ensure memory
+synchronization. Acquire the lock in imon_disconnect() before setting
+the flag to synchronize with any ongoing operations.
+
+Ensure writers exit early and safely after disconnect before the USB
+core proceeds with cleanup.
+
+Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
+
+Reported-by: syzbot+f1a69784f6efe748c3bf@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=f1a69784f6efe748c3bf
+Fixes: 21677cfc562a ("V4L/DVB: ir-core: add imon driver")
+Cc: stable@vger.kernel.org
+
+Signed-off-by: Larshin Sergey <Sergey.Larshin@kaspersky.com>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/rc/imon.c | 27 ++++++++++++++++++++-------
+ 1 file changed, 20 insertions(+), 7 deletions(-)
+
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -536,7 +536,9 @@ static int display_open(struct inode *in
+
+ mutex_lock(&ictx->lock);
+
+- if (!ictx->display_supported) {
++ if (ictx->disconnected) {
++ retval = -ENODEV;
++ } else if (!ictx->display_supported) {
+ pr_err("display not supported by device\n");
+ retval = -ENODEV;
+ } else if (ictx->display_isopen) {
+@@ -600,6 +602,9 @@ static int send_packet(struct imon_conte
+
+ lockdep_assert_held(&ictx->lock);
+
++ if (ictx->disconnected)
++ return -ENODEV;
++
+ /* Check if we need to use control or interrupt urb */
+ if (!ictx->tx_control) {
+ pipe = usb_sndintpipe(ictx->usbdev_intf0,
+@@ -956,12 +961,14 @@ static ssize_t vfd_write(struct file *fi
+ static const unsigned char vfd_packet6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+
+- if (ictx->disconnected)
+- return -ENODEV;
+-
+ if (mutex_lock_interruptible(&ictx->lock))
+ return -ERESTARTSYS;
+
++ if (ictx->disconnected) {
++ retval = -ENODEV;
++ goto exit;
++ }
++
+ if (!ictx->dev_present_intf0) {
+ pr_err_ratelimited("no iMON device present\n");
+ retval = -ENODEV;
+@@ -1036,11 +1043,13 @@ static ssize_t lcd_write(struct file *fi
+ int retval = 0;
+ struct imon_context *ictx = file->private_data;
+
+- if (ictx->disconnected)
+- return -ENODEV;
+-
+ mutex_lock(&ictx->lock);
+
++ if (ictx->disconnected) {
++ retval = -ENODEV;
++ goto exit;
++ }
++
+ if (!ictx->display_supported) {
+ pr_err_ratelimited("no iMON display present\n");
+ retval = -ENODEV;
+@@ -2496,7 +2505,11 @@ static void imon_disconnect(struct usb_i
+ int ifnum;
+
+ ictx = usb_get_intfdata(interface);
++
++ mutex_lock(&ictx->lock);
+ ictx->disconnected = true;
++ mutex_unlock(&ictx->lock);
++
+ dev = ictx->dev;
+ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+
--- /dev/null
+From stable+bounces-183345-greg=kroah.com@vger.kernel.org Sat Oct 4 02:40:23 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 20:40:15 -0400
+Subject: media: tuner: xc5000: Fix use-after-free in xc5000_release
+To: stable@vger.kernel.org
+Cc: Duoming Zhou <duoming@zju.edu.cn>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251004004015.4039827-2-sashal@kernel.org>
+
+From: Duoming Zhou <duoming@zju.edu.cn>
+
+[ Upstream commit 40b7a19f321e65789612ebaca966472055dab48c ]
+
+The original code uses cancel_delayed_work() in xc5000_release(), which
+does not guarantee that the delayed work item timer_sleep has fully
+completed if it was already running. This leads to use-after-free scenarios
+where xc5000_release() may free the xc5000_priv while timer_sleep is still
+active and attempts to dereference the xc5000_priv.
+
+A typical race condition is illustrated below:
+
+CPU 0 (release thread) | CPU 1 (delayed work callback)
+xc5000_release() | xc5000_do_timer_sleep()
+ cancel_delayed_work() |
+ hybrid_tuner_release_state(priv) |
+ kfree(priv) |
+ | priv = container_of() // UAF
+
+Replace cancel_delayed_work() with cancel_delayed_work_sync() to ensure
+that the timer_sleep is properly canceled before the xc5000_priv memory
+is deallocated.
+
+A deadlock concern was considered: xc5000_release() is called in a process
+context and is not holding any locks that the timer_sleep work item might
+also need. Therefore, the use of the _sync() variant is safe here.
+
+This bug was initially identified through static analysis.
+
+Fixes: f7a27ff1fb77 ("[media] xc5000: delay tuner sleep to 5 seconds")
+Cc: stable@vger.kernel.org
+Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+[hverkuil: fix typo in Subject: tunner -> tuner]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/tuners/xc5000.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/tuners/xc5000.c
++++ b/drivers/media/tuners/xc5000.c
+@@ -1304,7 +1304,7 @@ static void xc5000_release(struct dvb_fr
+ mutex_lock(&xc5000_list_mutex);
+
+ if (priv) {
+- cancel_delayed_work(&priv->timer_sleep);
++ cancel_delayed_work_sync(&priv->timer_sleep);
+ hybrid_tuner_release_state(priv);
+ }
+
--- /dev/null
+From stable+bounces-183346-greg=kroah.com@vger.kernel.org Sat Oct 4 02:40:25 2025
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 3 Oct 2025 20:40:14 -0400
+Subject: media: tunner: xc5000: Refactor firmware load
+To: stable@vger.kernel.org
+Cc: Ricardo Ribalda <ribalda@chromium.org>, Shuah Khan <shuah.kh@samsung.com>, Hans Verkuil <hverkuil-cisco@xs4all.nl>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20251004004015.4039827-1-sashal@kernel.org>
+
+From: Ricardo Ribalda <ribalda@chromium.org>
+
+[ Upstream commit 8e1f5da59dd4a1966f859639860b803a7e8b8bfb ]
+
+Make sure the firmware is released when we leave
+xc_load_fw_and_init_tuner()
+
+This change makes smatch happy:
+drivers/media/tuners/xc5000.c:1213 xc_load_fw_and_init_tuner() warn: 'fw' from request_firmware() not released on lines: 1213.
+
+Cc: Shuah Khan <shuah.kh@samsung.com>
+Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Stable-dep-of: 40b7a19f321e ("media: tuner: xc5000: Fix use-after-free in xc5000_release")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/tuners/xc5000.c | 41 ++++++++++++++++++-----------------------
+ 1 file changed, 18 insertions(+), 23 deletions(-)
+
+--- a/drivers/media/tuners/xc5000.c
++++ b/drivers/media/tuners/xc5000.c
+@@ -58,7 +58,7 @@ struct xc5000_priv {
+ struct dvb_frontend *fe;
+ struct delayed_work timer_sleep;
+
+- const struct firmware *firmware;
++ bool inited;
+ };
+
+ /* Misc Defines */
+@@ -1110,23 +1110,19 @@ static int xc_load_fw_and_init_tuner(str
+ if (!force && xc5000_is_firmware_loaded(fe) == 0)
+ return 0;
+
+- if (!priv->firmware) {
+- ret = request_firmware(&fw, desired_fw->name,
+- priv->i2c_props.adap->dev.parent);
+- if (ret) {
+- pr_err("xc5000: Upload failed. rc %d\n", ret);
+- return ret;
+- }
+- dprintk(1, "firmware read %zu bytes.\n", fw->size);
+-
+- if (fw->size != desired_fw->size) {
+- pr_err("xc5000: Firmware file with incorrect size\n");
+- release_firmware(fw);
+- return -EINVAL;
+- }
+- priv->firmware = fw;
+- } else
+- fw = priv->firmware;
++ ret = request_firmware(&fw, desired_fw->name,
++ priv->i2c_props.adap->dev.parent);
++ if (ret) {
++ pr_err("xc5000: Upload failed. rc %d\n", ret);
++ return ret;
++ }
++ dprintk(1, "firmware read %zu bytes.\n", fw->size);
++
++ if (fw->size != desired_fw->size) {
++ pr_err("xc5000: Firmware file with incorrect size\n");
++ release_firmware(fw);
++ return -EINVAL;
++ }
+
+ /* Try up to 5 times to load firmware */
+ for (i = 0; i < 5; i++) {
+@@ -1204,6 +1200,7 @@ static int xc_load_fw_and_init_tuner(str
+ }
+
+ err:
++ release_firmware(fw);
+ if (!ret)
+ printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n",
+ desired_fw->name);
+@@ -1274,7 +1271,7 @@ static int xc5000_resume(struct dvb_fron
+
+ /* suspended before firmware is loaded.
+ Avoid firmware load in resume path. */
+- if (!priv->firmware)
++ if (!priv->inited)
+ return 0;
+
+ return xc5000_set_params(fe);
+@@ -1293,6 +1290,8 @@ static int xc5000_init(struct dvb_fronte
+ if (debug)
+ xc_debug_dump(priv);
+
++ priv->inited = true;
++
+ return 0;
+ }
+
+@@ -1306,10 +1305,6 @@ static void xc5000_release(struct dvb_fr
+
+ if (priv) {
+ cancel_delayed_work(&priv->timer_sleep);
+- if (priv->firmware) {
+- release_firmware(priv->firmware);
+- priv->firmware = NULL;
+- }
+ hybrid_tuner_release_state(priv);
+ }
+
scsi-target-target_core_configfs-add-length-check-to-avoid-buffer-overflow.patch
media-b2c2-fix-use-after-free-causing-by-irq_check_work-in-flexcop_pci_remove.patch
udp-fix-memory-accounting-leak.patch
+media-tunner-xc5000-refactor-firmware-load.patch
+media-tuner-xc5000-fix-use-after-free-in-xc5000_release.patch
+media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch
+media-rc-add-support-for-another-imon-0xffdc-device.patch
+media-imon-reorganize-serialization.patch
+media-imon-grab-lock-earlier-in-imon_ir_change_protocol.patch
+media-rc-fix-races-with-imon_disconnect.patch