Re: [PATCH RFC] usb: gadgetfs: fix use-after-free in ep_open()

Aleksandr Nogikh <[email protected]> Fri, 31 Jul 2026 15:21:31 +0200
Newsgroups dev.linux.lists.syzbot
Message-ID <CANp29Y5woqC4_8Awxuv0FU1sbK97+LFhHyLUn2uHku3nJ93b5g@mail.gmail.com>
#syz upstream

On Fri, Jul 31, 2026 at 10:13=E2=80=AFAM 'syzbot' via
syzkaller-upstream-moderation
<[email protected]> wrote:
>
> A race condition exists between opening a gadgetfs endpoint file and the
> destruction of the endpoint files, leading to a use-after-free of the
> ep_data object.
>
> When an endpoint file is opened, the VFS looks up the dentry and calls
> ep_open(). ep_open() retrieves the ep_data pointer from inode->i_private
> and attempts to lock its mutex. Concurrently, if the gadget is unbound,
> destroy_ep_files() iterates over the endpoints, unhashes their dentries,
> and immediately calls put_ep(). This drops the initial reference to the
> ep_data object, freeing it. If another thread is already in the process o=
f
> opening the file, it holds a reference to the dentry and the inode. The
> inode remains alive, and its i_private pointer still points to the
> now-freed ep_data. When ep_open() proceeds to lock data->lock, it accesse=
s
> freed memory.
>
> BUG: KASAN: slab-use-after-free in __mutex_lock_common
> kernel/locking/mutex.c:625 [inline]
> BUG: KASAN: slab-use-after-free in __mutex_lock+0x130/0xf14
> kernel/locking/mutex.c:821
> Read of size 8 at addr ffff0000f43acc58 by task syz-executor/5900
>
> Call trace:
>  __mutex_lock_common kernel/locking/mutex.c:625 [inline]
>  __mutex_lock+0x130/0xf14 kernel/locking/mutex.c:821
>  mutex_lock_interruptible_nested+0x24/0x30 kernel/locking/mutex.c:899
>  ep_open+0x58/0x25c drivers/usb/gadget/legacy/inode.c:823
>  do_dentry_open+0x5c4/0xfc0 fs/open.c:947
>  vfs_open+0x44/0x2dc fs/open.c:1052
>
> Freed by task 6138:
>  kfree+0x188/0x690 mm/slub.c:6692
>  put_ep+0xd0/0x144 drivers/usb/gadget/legacy/inode.c:225
>  destroy_ep_files+0x244/0x2dc drivers/usb/gadget/legacy/inode.c:1582
>  gadgetfs_unbind+0x114/0x1f0 drivers/usb/gadget/legacy/inode.c:1655
>  gadget_unbind_driver+0x188/0x790 drivers/usb/gadget/udc/core.c:1724
>
> To fix this, tie the base reference of ep_data to the lifetime of the ino=
de
> rather than manually dropping it in destroy_ep_files(). Add an .evict_ino=
de
> callback to gadget_fs_operations that drops the reference when the inode =
is
> finally destroyed. Remove the premature put_ep() call from
> destroy_ep_files(). Additionally, to prevent a double-free in the error
> path of activate_ep_files(), clear inode->i_private in
> gadgetfs_create_file() before calling iput() if simple_start_creating()
> fails.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
> Reported-by: [email protected]
> Closes: https://syzkaller.appspot.com/bug?extid=3Ddf9e891bf8ea586f846b
> Link: https://syzkaller.appspot.com/ai_job?id=3Dae49a838-5eb9-4227-93a3-5=
6ad2313483a
> To: "Greg Kroah-Hartman" <[email protected]>
> To: <[email protected]>
> To: "Linus Torvalds" <[email protected]>
> Cc: "Christian Brauner" <[email protected]>
> Cc: "Jan Kara" <[email protected]>
> Cc: "Kees Cook" <[email protected]>
> Cc: <[email protected]>
> Cc: "Mateusz Guzik" <[email protected]>
> Cc: "Al Viro" <[email protected]>
>
> ---
> diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legac=
y/inode.c
> index d87a8ab51..a03062986 100644
> --- a/drivers/usb/gadget/legacy/inode.c
> +++ b/drivers/usb/gadget/legacy/inode.c
> @@ -1579,8 +1579,6 @@ static void destroy_ep_files (struct dev_data *dev)
>                 /* break link to dcache */
>                 simple_remove_by_name(dev->sb->s_root, ep->name, NULL);
>
> -               put_ep (ep);
> -
>                 spin_lock_irq (&dev->lock);
>         }
>         spin_unlock_irq (&dev->lock);
> @@ -1996,6 +1994,7 @@ static int gadgetfs_create_file (struct super_block=
 *sb, char const *name,
>
>         dentry =3D simple_start_creating(sb->s_root, name);
>         if (IS_ERR(dentry)) {
> +               inode->i_private =3D NULL;
>                 iput(inode);
>                 return PTR_ERR(dentry);
>         }
> @@ -2006,9 +2005,18 @@ static int gadgetfs_create_file (struct super_bloc=
k *sb, char const *name,
>         return 0;
>  }
>
> +static void gadgetfs_evict_inode(struct inode *inode)
> +{
> +       truncate_inode_pages_final(&inode->i_data);
> +       clear_inode(inode);
> +       if (inode->i_private && inode->i_fop =3D=3D &ep_io_operations)
> +               put_ep(inode->i_private);
> +}
> +
>  static const struct super_operations gadget_fs_operations =3D {
>         .statfs =3D       simple_statfs,
>         .drop_inode =3D   inode_just_drop,
> +       .evict_inode =3D  gadgetfs_evict_inode,
>  };
>
>  static int
>
>
> base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
> --
> This is an AI-generated patch subject to moderation.
> Reply with '#syz upstream' to Sign-off the patch as a human author
> and send it to the upstream kernel mailing lists.
> Reply with '#syz reject' to reject it ('#syz unreject' to undo).
>
> See https://goo.gle/syzbot-ai-patches for information about AI-generated =
patches.
> You can comment on the patch as usual, syzbot will try to address
> the comments and send a new version of the patch if necessary.
> syzbot engineers can be reached at [email protected].
>
> --
> You received this message because you are subscribed to the Google Groups=
 "syzkaller-upstream-moderation" group.
> To unsubscribe from this group and stop receiving emails from it, send an=
 email to [email protected].
> To view this discussion visit https://groups.google.com/d/msgid/syzkaller=
-upstream-moderation/a6f4f066-bd64-4a53-b3e0-0d71b34bf773%40mail.kernel.org=
.