From: Greg Kroah-Hartman Date: Mon, 6 Oct 2025 10:23:28 +0000 (+0200) Subject: 5.4-stable patches X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=41d9f8eb6235082ed67f8e97a8840da09253d206;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch media-imon-grab-lock-earlier-in-imon_ir_change_protocol.patch media-imon-reorganize-serialization.patch media-rc-add-support-for-another-imon-0xffdc-device.patch media-rc-fix-races-with-imon_disconnect.patch media-tuner-xc5000-fix-use-after-free-in-xc5000_release.patch media-tunner-xc5000-refactor-firmware-load.patch --- diff --git a/queue-5.4/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch b/queue-5.4/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch new file mode 100644 index 0000000000..a760726030 --- /dev/null +++ b/queue-5.4/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch @@ -0,0 +1,149 @@ +From stable+bounces-183326-greg=kroah.com@vger.kernel.org Fri Oct 3 22:43:33 2025 +From: Sasha Levin +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 , Hans Verkuil , Sasha Levin +Message-ID: <20251003204323.3377744-1-sashal@kernel.org> + +From: Duoming Zhou + +[ 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: + + 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 + +... + +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 +Signed-off-by: Hans Verkuil +[ replaced del_timer() instead of timer_delete() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/queue-5.4/media-imon-grab-lock-earlier-in-imon_ir_change_protocol.patch b/queue-5.4/media-imon-grab-lock-earlier-in-imon_ir_change_protocol.patch new file mode 100644 index 0000000000..11f0eae88a --- /dev/null +++ b/queue-5.4/media-imon-grab-lock-earlier-in-imon_ir_change_protocol.patch @@ -0,0 +1,59 @@ +From stable+bounces-183334-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:48 2025 +From: Sasha Levin +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 , Sean Young , Hans Verkuil , Sasha Levin +Message-ID: <20251003205537.3386848-3-sashal@kernel.org> + +From: Tetsuo Handa + +[ 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 +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +Stable-dep-of: fa0f61cc1d82 ("media: rc: fix races with imon_disconnect()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/queue-5.4/media-imon-reorganize-serialization.patch b/queue-5.4/media-imon-reorganize-serialization.patch new file mode 100644 index 0000000000..a54fd8818f --- /dev/null +++ b/queue-5.4/media-imon-reorganize-serialization.patch @@ -0,0 +1,343 @@ +From stable+bounces-183333-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:46 2025 +From: Sasha Levin +Date: Fri, 3 Oct 2025 16:55:35 -0400 +Subject: media: imon: reorganize serialization +To: stable@vger.kernel.org +Cc: Tetsuo Handa , syzbot , Alan Stern , Sean Young , Mauro Carvalho Chehab , Sasha Levin +Message-ID: <20251003205537.3386848-2-sashal@kernel.org> + +From: Tetsuo Handa + +[ 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 +Signed-off-by: Tetsuo Handa +Tested-by: syzbot +Cc: Alan Stern +Signed-off-by: Sean Young +Signed-off-by: Mauro Carvalho Chehab +Stable-dep-of: fa0f61cc1d82 ("media: rc: fix races with imon_disconnect()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } diff --git a/queue-5.4/media-rc-add-support-for-another-imon-0xffdc-device.patch b/queue-5.4/media-rc-add-support-for-another-imon-0xffdc-device.patch new file mode 100644 index 0000000000..389fc5d828 --- /dev/null +++ b/queue-5.4/media-rc-add-support-for-another-imon-0xffdc-device.patch @@ -0,0 +1,157 @@ +From stable+bounces-183332-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:46 2025 +From: Sasha Levin +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 , Chris Vandomelen , Sean Young , Mauro Carvalho Chehab , Sasha Levin +Message-ID: <20251003205537.3386848-1-sashal@kernel.org> + +From: Flavius Georgescu + +[ 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 +Co-developed-by: Chris Vandomelen +Signed-off-by: Chris Vandomelen +Signed-off-by: Sean Young +Signed-off-by: Mauro Carvalho Chehab +Stable-dep-of: fa0f61cc1d82 ("media: rc: fix races with imon_disconnect()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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; diff --git a/queue-5.4/media-rc-fix-races-with-imon_disconnect.patch b/queue-5.4/media-rc-fix-races-with-imon_disconnect.patch new file mode 100644 index 0000000000..ec70befc65 --- /dev/null +++ b/queue-5.4/media-rc-fix-races-with-imon_disconnect.patch @@ -0,0 +1,164 @@ +From stable+bounces-183335-greg=kroah.com@vger.kernel.org Fri Oct 3 22:55:49 2025 +From: Sasha Levin +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 , syzbot+f1a69784f6efe748c3bf@syzkaller.appspotmail.com, Sean Young , Hans Verkuil , Sasha Levin +Message-ID: <20251003205537.3386848-4-sashal@kernel.org> + +From: Larshin Sergey + +[ 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: + +__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 +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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; + diff --git a/queue-5.4/media-tuner-xc5000-fix-use-after-free-in-xc5000_release.patch b/queue-5.4/media-tuner-xc5000-fix-use-after-free-in-xc5000_release.patch new file mode 100644 index 0000000000..8bf16a9154 --- /dev/null +++ b/queue-5.4/media-tuner-xc5000-fix-use-after-free-in-xc5000_release.patch @@ -0,0 +1,59 @@ +From stable+bounces-183345-greg=kroah.com@vger.kernel.org Sat Oct 4 02:40:23 2025 +From: Sasha Levin +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 , Hans Verkuil , Sasha Levin +Message-ID: <20251004004015.4039827-2-sashal@kernel.org> + +From: Duoming Zhou + +[ 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 +Signed-off-by: Hans Verkuil +[hverkuil: fix typo in Subject: tunner -> tuner] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } + diff --git a/queue-5.4/media-tunner-xc5000-refactor-firmware-load.patch b/queue-5.4/media-tunner-xc5000-refactor-firmware-load.patch new file mode 100644 index 0000000000..4f516003d1 --- /dev/null +++ b/queue-5.4/media-tunner-xc5000-refactor-firmware-load.patch @@ -0,0 +1,113 @@ +From stable+bounces-183346-greg=kroah.com@vger.kernel.org Sat Oct 4 02:40:25 2025 +From: Sasha Levin +Date: Fri, 3 Oct 2025 20:40:14 -0400 +Subject: media: tunner: xc5000: Refactor firmware load +To: stable@vger.kernel.org +Cc: Ricardo Ribalda , Shuah Khan , Hans Verkuil , Sasha Levin +Message-ID: <20251004004015.4039827-1-sashal@kernel.org> + +From: Ricardo Ribalda + +[ 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 +Signed-off-by: Ricardo Ribalda +Signed-off-by: Hans Verkuil +Stable-dep-of: 40b7a19f321e ("media: tuner: xc5000: Fix use-after-free in xc5000_release") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } + diff --git a/queue-5.4/series b/queue-5.4/series index ca3b214cdf..0040022a6b 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -1,3 +1,10 @@ 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