On Tue Jul 29 13:13:32 2025 +0300, Larshin Sergey wrote:
> 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: [email protected]
> Closes: https://syzkaller.appspot.com/bug?extid=f1a69784f6efe748c3bf
> Fixes: 21677cfc562a ("V4L/DVB: ir-core: add imon driver")
> Cc: [email protected]
> 
> Signed-off-by: Larshin Sergey <[email protected]>
> Signed-off-by: Sean Young <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/rc/imon.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

---

diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 91d05aadced3..35b9e07003d8 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -531,7 +531,9 @@ static int display_open(struct inode *inode, struct file 
*file)
 
        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) {
@@ -595,6 +597,9 @@ static int send_packet(struct imon_context *ictx)
 
        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,
@@ -949,12 +954,14 @@ static ssize_t vfd_write(struct file *file, const char 
__user *buf,
        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;
@@ -1029,11 +1036,13 @@ static ssize_t lcd_write(struct file *file, const char 
__user *buf,
        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;
@@ -2507,7 +2516,11 @@ static void imon_disconnect(struct usb_interface 
*interface)
        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;
 

Reply via email to