[PATCH 2/3] usb: gadget: f_tcm: detach gadget when dropping an enabled TPG
From: Slawomir Stepien
Date: Thu Oct 22 2026 - 09:56:05 EST
Removing a TPG directory via configfs rmdir ends up in usbg_drop_tpg()
through target_fabric_tpg_release(). The target core does not disable
the TPG first, so if the TPG is still enabled and the gadget is bound to
a UDC, usbg_drop_tpg() destroys the TPG workqueue and frees the TPG
while the bound function still references it through fu->tpg.
Subsequent USB traffic (tcm_setup(), bot_submit_command(),
usbg_submit_command(), ...) then dereferences freed memory and queues
work onto a destroyed workqueue.
Disable the TPG through usbg_enable_tpg() before tearing it down, so the
gadget is unbound in the same way as when writing 0 to the enable
attribute. Only do so when the gadget is connected: for the legacy
g_target driver, detaching a never-attached TPG would call
usb_composite_unregister() on a driver that was never registered.
Fixes: c52661d60f63 ("usb-gadget: Initial merge of target module for UASP + BOT")
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Slawomir Stepien <sst@xxxxxxxxx>
---
drivers/usb/gadget/function/f_tcm.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index cc75d7e9792a..bcf11f4405ae 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1742,6 +1742,7 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
}
static int tcm_usbg_drop_nexus(struct usbg_tpg *);
+static int usbg_enable_tpg(struct se_portal_group *se_tpg, bool enable);
static void usbg_drop_tpg(struct se_portal_group *se_tpg)
{
@@ -1750,6 +1751,9 @@ static void usbg_drop_tpg(struct se_portal_group *se_tpg)
unsigned i;
struct f_tcm_opts *opts;
+ if (tpg->gadget_connect)
+ usbg_enable_tpg(se_tpg, false);
+
tcm_usbg_drop_nexus(tpg);
core_tpg_deregister(se_tpg);
destroy_workqueue(tpg->workqueue);
--
2.56.0