usb: gadget: f_tcm: fix deadlock in usbg_make_tpg()

usbg_make_tpg() held dep_lock while calling
configfs_depend_item_unlocked(), which acquires the configfs root
inode lock when operating across subsystems. This creates a circular
lock dependency with configfs_rmdir():

  dep_lock -> configfs root inode lock -> su_mutex -> dep_lock

In usbg_make_tpg(), dep_lock only serialized the read of opts->ready,
which is a monotonic flag that transitions from false to true exactly
once (in tcm_set_name()) and never reverts. Remove dep_lock from
usbg_make_tpg() entirely and use READ_ONCE/WRITE_ONCE to access
opts->ready locklessly instead.

Reported-by: syzbot+c9f9d646b08f3b6032fe@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c9f9d646b08f3b6032fe
Fixes: 4bb8548df6 ("usb: gadget: f_tcm: add configfs support")
Cc: stable@vger.kernel.org
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Link: https://patch.msgid.link/20260731081151.285599-1-yun.zhou@windriver.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Yun Zhou 2026-07-31 16:11:51 +08:00 committed by Greg Kroah-Hartman
parent bf1e90189a
commit 9dbf74f402

View File

@ -1675,19 +1675,25 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts,
func_inst);
mutex_lock(&opts->dep_lock);
if (!opts->ready)
goto unlock_dep;
if (!READ_ONCE(opts->ready))
goto unlock_inst;
if (opts->has_dep) {
if (!try_module_get(opts->dependent))
goto unlock_dep;
goto unlock_inst;
} else {
/*
* configfs_depend_item_unlocked() may acquire the configfs
* root inode lock when the target belongs to a different
* subsystem. Calling it under dep_lock would create a
* circular dependency:
* dep_lock -> configfs inode lock -> su_mutex -> dep_lock
*/
ret = configfs_depend_item_unlocked(
wwn->wwn_group.cg_subsys,
&opts->func_inst.group.cg_item);
if (ret)
goto unlock_dep;
goto unlock_inst;
}
tpg = kzalloc_obj(struct usbg_tpg);
@ -1714,7 +1720,6 @@ 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_unlock(&opts->dep_lock);
mutex_unlock(&tpg_instances_lock);
return &tpg->se_tpg;
@ -1727,8 +1732,6 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
module_put(opts->dependent);
else
configfs_undepend_item_unlocked(&opts->func_inst.group.cg_item);
unlock_dep:
mutex_unlock(&opts->dep_lock);
unlock_inst:
mutex_unlock(&tpg_instances_lock);
@ -2666,9 +2669,7 @@ static int tcm_set_name(struct usb_function_instance *f, const char *name)
pr_debug("tcm: Activating %s\n", name);
mutex_lock(&opts->dep_lock);
opts->ready = true;
mutex_unlock(&opts->dep_lock);
WRITE_ONCE(opts->ready, true);
return 0;
}