Archive-only list for syzbot
 help / color / mirror / Atom feed
From: Slawomir Stepien <sst@poczta.fm>
To: syzbot <syzbot@kernel.org>
Cc: syzkaller-upstream-moderation@googlegroups.com, syzbot@lists.linux.dev
Subject: Re: [PATCH RFC] usb: gadget: f_tcm: defer TPG assignment to fix circular locking
Date: Thu, 15 Oct 2026 09:37:51 +0200	[thread overview]
Message-ID: <atCCz_-iwbVFC5w_@nr200> (raw)
In-Reply-To: <b1646de9-ae67-4626-89f3-76a04e181581@mail.kernel.org>

On paź 06, 2026 00:20, 'syzbot' via syzkaller-upstream-moderation wrote:
> A circular locking dependency exists between tpg_instances_lock, the
> configfs directory inode_lock, and the gadget info lock gi->lock when
> configuring TCM gadget functions through configfs.
> 
>   WARNING: possible circular locking dependency detected
>   ======================================================
>   task is trying to acquire lock:
>   ffffffff8f24ae58 (tpg_instances_lock){+.+.}-{4:4}, at:
>   tcm_alloc+0x21/0x2f0 drivers/usb/gadget/function/f_tcm.c:2734
> 
>   but task is already holding lock:
>   ffff888192990388 (&gi->lock){+.+.}-{4:4}, at:
>   config_usb_cfg_link+0x59/0x260 drivers/usb/gadget/configfs.c:439
> 
>   which lock already depends on the new lock.
> 
>   the existing dependency chain (in reverse order) is:
> 
>    -> #2 (&gi->lock){+.+.}-{4:4}:
>          config_usb_cfg_link+0x59/0x260 drivers/usb/gadget/configfs.c:439
>          configfs_symlink+0x74e/0x11d0 fs/configfs/symlink.c:211
>          vfs_symlink+0x18b/0x330 fs/namei.c:5794
> 
>    -> #1 (&sb->s_type->i_mutex_key#24){+.+.}-{4:4}:
>          inode_lock include/linux/fs.h:1024 [inline]
>          configfs_depend_item_unlocked+0x153/0x420 fs/configfs/dir.c:1268
>          usbg_make_tpg+0x1e0/0x550 drivers/usb/gadget/function/f_tcm.c:1692
>          target_fabric_make_tpg+0xa8/0x6d0
>          drivers/target/target_core_fabric_configfs.c:947
>          configfs_mkdir+0x4f6/0x9e0 fs/configfs/dir.c:1369
> 
>    -> #0 (tpg_instances_lock){+.+.}-{4:4}:
>          tcm_alloc+0x21/0x2f0 drivers/usb/gadget/function/f_tcm.c:2734
>          usb_get_function+0x63/0xb0 drivers/usb/gadget/functions.c:61
>          config_usb_cfg_link+0x148/0x260 drivers/usb/gadget/configfs.c:469
>          configfs_symlink+0x74e/0x11d0 fs/configfs/symlink.c:211
> 
> The deadlock arises from two conflicting lock acquisition paths across
> configfs:
> 
> 1. When creating a Target Portal Group (TPG) directory in configfs (e.g.
> target/usb_gadget/.../tpgt_1), VFS takes the directory inode_lock. In
> usbg_make_tpg(), tpg_instances_lock is acquired. While holding
> tpg_instances_lock, usbg_make_tpg() calls configfs_depend_item_unlocked()
> to depend on the gadget function item, which acquires the configfs root
> directory inode_lock across subsystems (tpg_instances_lock -> inode_lock).
> 
> 2. When linking the TCM function to a gadget configuration (e.g. symlink
> functions/tcm.usb0 configs/c.1/tcm.usb0), VFS acquires the configuration
> directory inode_lock. The callback configfs_symlink() calls
> config_usb_cfg_link(), acquiring gi->lock. While holding gi->lock,
> config_usb_cfg_link() calls usb_get_function(), invoking tcm_alloc(). In
> tcm_alloc(), tpg_instances_lock was acquired to search the tpg_instances
> array and set fu->tpg (inode_lock -> gi->lock -> tpg_instances_lock).
> 
> In addition to the deadlock, initializing fu->tpg in tcm_alloc() introduces
> a race condition. If the user symlinks the function into a gadget
> configuration before creating the TPG directory, tpg_instances[i].tpg is
> NULL, causing tcm_alloc() to leave fu->tpg as NULL. When the TPG is later
> created and enabled, tcm_bind() succeeds because opts->can_attach is true,
> but fu->tpg remains NULL. When USB transfers are subsequently processed,
> dereferencing fu->tpg results in a NULL pointer dereference crash.
> Furthermore, removing an enabled TPG via rmdir calls usbg_drop_tpg()
> directly without detaching, leaving opts->can_attach set to true and
> permitting subsequent binds against a destroyed TPG.
> 
> Fix these issues by storing the TPG pointer in struct f_tcm_opts, removing
> the tpg_instances_lock acquisition from tcm_alloc(), and deferring fu->tpg
> assignment until tcm_bind(). The function instance is already valid for the
> item's lifetime, making the search in tcm_alloc() redundant; removing it
> breaks the circular lock dependency cycle. In tcm_bind(), fu->tpg is
> assigned under opts->dep_lock after validating that both opts->can_attach
> and opts->tpg are valid. In usbg_make_tpg(), opts->tpg is set under
> opts->dep_lock. In usbg_drop_tpg(), both opts->can_attach is reset to false
> and opts->tpg is cleared under opts->dep_lock, ensuring consistent state
> and preventing NULL pointer dereferences.
> 
> Fixes: 4bb8548df632 ("usb: gadget: f_tcm: add configfs support")
> Assisted-by: Gemini:gemini-3.8-flash Gemini:gemini-3.1-pro-preview syzbot
> Reported-by: syzbot+2799146a53c356f8017d@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=2799146a53c356f8017d
> Link: https://syzkaller.appspot.com/ai_job?id=a5b86f29-4d77-48c4-90c7-2467ea27765f
> To: "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>
> To: <linux-usb@vger.kernel.org>
> To: "Andrzej Pietrasiewicz" <andrzej.p@samsung.com>
> Cc: "Jiasheng Jiang" <jiashengjiangcool@gmail.com>
> Cc: <linux-kernel@vger.kernel.org>
> Cc: "Yun Zhou" <yun.zhou@windriver.com>
> Cc: "Cen Zhang" <zzzccc427@gmail.com>
> 
> ---
> diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
> index 9e6d4f399..c7bdedd46 100644
> --- a/drivers/usb/gadget/function/f_tcm.c
> +++ b/drivers/usb/gadget/function/f_tcm.c
> @@ -1720,6 +1720,9 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
>  
>  	tpg_instances[i].tpg = tpg;
>  	tpg->fi = tpg_instances[i].func_inst;
> +	mutex_lock(&opts->dep_lock);
> +	opts->tpg = tpg;
> +	mutex_unlock(&opts->dep_lock);
>  	mutex_unlock(&tpg_instances_lock);
>  	return &tpg->se_tpg;
>  
> @@ -1760,6 +1763,8 @@ static void usbg_drop_tpg(struct se_portal_group *se_tpg)
>  		opts = container_of(tpg_instances[i].func_inst,
>  			struct f_tcm_opts, func_inst);
>  		mutex_lock(&opts->dep_lock);
> +		opts->can_attach = false;
> +		opts->tpg = NULL;
>  		if (opts->has_dep)
>  			module_put(opts->dependent);
>  		else
> @@ -2300,10 +2305,11 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
>  	opts = container_of(f->fi, struct f_tcm_opts, func_inst);
>  
>  	mutex_lock(&opts->dep_lock);
> -	if (!opts->can_attach) {
> +	if (!opts->can_attach || !opts->tpg) {
>  		mutex_unlock(&opts->dep_lock);
>  		return -ENODEV;
>  	}
> +	fu->tpg = opts->tpg;
>  	mutex_unlock(&opts->dep_lock);
>  	us = usb_gstrings_attach(c->cdev, tcm_strings,
>  		ARRAY_SIZE(tcm_us_strings));
> @@ -2729,22 +2735,10 @@ static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
>  static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
>  {
>  	struct f_uas *fu;
> -	unsigned i;
> -
> -	mutex_lock(&tpg_instances_lock);
> -	for (i = 0; i < TPG_INSTANCES; ++i)
> -		if (tpg_instances[i].func_inst == fi)
> -			break;
> -	if (i == TPG_INSTANCES) {
> -		mutex_unlock(&tpg_instances_lock);
> -		return ERR_PTR(-ENODEV);
> -	}
>  
>  	fu = kzalloc_obj(*fu);
> -	if (!fu) {
> -		mutex_unlock(&tpg_instances_lock);
> +	if (!fu)
>  		return ERR_PTR(-ENOMEM);
> -	}
>  
>  	fu->function.name = "Target Function";
>  	fu->function.bind = tcm_bind;
> @@ -2754,12 +2748,10 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
>  	fu->function.setup = tcm_setup;
>  	fu->function.disable = tcm_disable;
>  	fu->function.free_func = tcm_free;
> -	fu->tpg = tpg_instances[i].tpg;
>  	INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt);
>  	spin_lock_init(&fu->delayed_set_alt_lock);
>  
>  	hash_init(fu->stream_hash);
> -	mutex_unlock(&tpg_instances_lock);
>  
>  	return &fu->function;
>  }
> diff --git a/drivers/usb/gadget/function/u_tcm.h b/drivers/usb/gadget/function/u_tcm.h
> index 2cd15d9a1..1376b6ebc 100644
> --- a/drivers/usb/gadget/function/u_tcm.h
> +++ b/drivers/usb/gadget/function/u_tcm.h
> @@ -15,6 +15,8 @@
>  
>  #include <linux/usb/composite.h>
>  
> +struct usbg_tpg;
> +
>  /**
>   * @dependent: optional dependent module. Meant for legacy gadget.
>   * If non-null its refcount will be increased when a tpg is created and
> @@ -23,10 +25,12 @@
>   * @ready: true if the dependent module information is set.
>   * @can_attach: true a function can be bound to gadget
>   * @has_dep: true if there is a dependent module
> + * @tpg: target portal group

This new comment is lacking a bit in content. Explain a bit more what @tpg member holds in this
structure.

>   *
>   */
>  struct f_tcm_opts {
>  	struct usb_function_instance	func_inst;
> +	struct usbg_tpg			*tpg;
>  	struct module			*dependent;
>  	struct mutex			dep_lock;
>  	bool				ready;
> 
> 
> base-commit: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a

-- 
Slawomir Stepien

      reply	other threads:[~2026-10-15  7:38 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-06  0:20 [PATCH RFC] usb: gadget: f_tcm: defer TPG assignment to fix circular locking syzbot
2026-10-15  7:37 ` Slawomir Stepien [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=atCCz_-iwbVFC5w_@nr200 \
    --to=sst@poczta.fm \
    --cc=syzbot@kernel.org \
    --cc=syzbot@lists.linux.dev \
    --cc=syzkaller-upstream-moderation@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox