* [PATCH 1/2] usb: musb: sunxi: Unregister the generic PHY after dropping its reference
2026-11-10 7:08 [PATCH 0/2] usb: musb: sunxi: Fix unbinding and rebinding Rajan Yadav via B4 Relay
@ 2026-11-10 7:08 ` Rajan Yadav via B4 Relay
2026-11-10 7:13 ` sashiko-bot
2026-11-10 7:08 ` [PATCH 2/2] usb: musb: sunxi: Unregister the extcon notifier in sunxi_musb_exit() Rajan Yadav via B4 Relay
1 sibling, 1 reply; 5+ messages in thread
From: Rajan Yadav via B4 Relay @ 2026-11-10 7:08 UTC (permalink / raw)
To: Bin Liu, Greg Kroah-Hartman, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland
Cc: regressions, Zijun Hu, linux-usb, linux-arm-kernel, linux-sunxi,
linux-kernel, Rajan Yadav, stable
From: Rajan Yadav <rajanyadav0307@gmail.com>
Unbinding musb-sunxi oopses on v7.4-rc1 (KickPi K5C, Allwinner A133,
KASAN enabled, no out-of-tree modules):
# echo 5100000.usb > /sys/bus/platform/drivers/musb-sunxi/unbind
BUG: KASAN: slab-use-after-free in devm_usb_phy_release+0x2c/0x68
Read of size 8 at addr ffff0000057ef080 by task sh/329
...
Allocated by task 48:
...
devm_kmalloc+0x5c/0x140
usb_phy_generic_probe+0x30/0x184
...
usb_phy_generic_register+0x9c/0xd8
sunxi_musb_probe+0x310/0x5c8
...
Freed by task 329:
...
platform_device_unregister+0x24/0x40
usb_phy_generic_unregister+0x10/0x1c
sunxi_musb_remove+0x44/0x54
...
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010
...
pc : devm_usb_phy_release+0x44/0x68
sunxi_musb_remove() unregisters the usb_phy_generic device. That unbinds
its driver, which frees the struct usb_phy_generic holding the struct
usb_phy. The reference that sunxi_musb_probe() took with
devm_usb_get_phy() is only dropped after that, when devres runs, and
usb_put_phy() then reads x->dev->driver->owner from the freed PHY with
dev->driver already NULL. The probe error paths unregister the generic
PHY with the devres reference still held in the same way.
Commit 6ed05c68cbca ("usb: musb: sunxi: Explicitly release USB PHY on
exit") fixed this oops in 2017 by calling devm_usb_put_phy() in
sunxi_musb_exit(). Commit 498dbd9aea20 ("usb: musb: sunxi: Fix
accessing an released usb phy") removed that call again, because
sunxi_musb_exit() also runs when only the musb-hdrc child is unbound,
and the PHY must stay valid for the next bind. That brought the oops
back.
Keep the reference for the lifetime of the glue device, as 498dbd9aea20
intended, and let devres unregister the generic PHY device as well. The
action is added before devm_usb_get_phy(), so devres drops the
reference first, on unbind and on the probe error paths.
Tested on the K5C with v7.4-rc1 and KASAN: unbinding musb-sunxi gives
no KASAN report and no oops, the generic PHY device, musb-hdrc and the
UDC are removed and come back on bind, and 11 unbind/bind cycles stayed
clean. The probe error paths were not exercised, and no USB gadget
traffic was tested (no data connection to a host).
Claude Code (Anthropic) found the cause from the oops, wrote this fix
and the commit message, and helped with the tests; I reviewed the fix
and ran the tests on the board.
Fixes: 498dbd9aea20 ("usb: musb: sunxi: Fix accessing an released usb phy")
Cc: stable@vger.kernel.org
Signed-off-by: Rajan Yadav <rajanyadav0307@gmail.com>
---
drivers/usb/musb/sunxi.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index a6bd3e968cc..d880c3758c9 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -674,6 +674,11 @@ static const struct musb_hdrc_config sunxi_musb_hdrc_config_4eps = {
.ram_bits = SUNXI_MUSB_RAM_BITS,
};
+static void sunxi_musb_unregister_usb_phy(void *usb_phy)
+{
+ usb_phy_generic_unregister(usb_phy);
+}
+
static int sunxi_musb_probe(struct platform_device *pdev)
{
struct musb_hdrc_platform_data pdata;
@@ -768,11 +773,21 @@ static int sunxi_musb_probe(struct platform_device *pdev)
return PTR_ERR(glue->usb_phy);
}
+ /*
+ * Registered before devm_usb_get_phy(), so that devres drops the
+ * reference to the PHY before the PHY device is unregistered.
+ */
+ ret = devm_add_action_or_reset(&pdev->dev,
+ sunxi_musb_unregister_usb_phy,
+ glue->usb_phy);
+ if (ret)
+ return ret;
+
glue->xceiv = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(glue->xceiv)) {
ret = PTR_ERR(glue->xceiv);
dev_err(&pdev->dev, "Error getting usb-phy %d\n", ret);
- goto err_unregister_usb_phy;
+ return ret;
}
platform_set_drvdata(pdev, glue);
@@ -792,23 +807,17 @@ static int sunxi_musb_probe(struct platform_device *pdev)
if (IS_ERR(glue->musb_pdev)) {
ret = PTR_ERR(glue->musb_pdev);
dev_err(&pdev->dev, "Error registering musb dev: %d\n", ret);
- goto err_unregister_usb_phy;
+ return ret;
}
return 0;
-
-err_unregister_usb_phy:
- usb_phy_generic_unregister(glue->usb_phy);
- return ret;
}
static void sunxi_musb_remove(struct platform_device *pdev)
{
struct sunxi_glue *glue = platform_get_drvdata(pdev);
- struct platform_device *usb_phy = glue->usb_phy;
platform_device_unregister(glue->musb_pdev);
- usb_phy_generic_unregister(usb_phy);
}
static const struct sunxi_musb_cfg sun4i_a10_musb_cfg = {
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/2] usb: musb: sunxi: Unregister the extcon notifier in sunxi_musb_exit()
2026-11-10 7:08 [PATCH 0/2] usb: musb: sunxi: Fix unbinding and rebinding Rajan Yadav via B4 Relay
2026-11-10 7:08 ` [PATCH 1/2] usb: musb: sunxi: Unregister the generic PHY after dropping its reference Rajan Yadav via B4 Relay
@ 2026-11-10 7:08 ` Rajan Yadav via B4 Relay
2026-11-10 7:26 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Rajan Yadav via B4 Relay @ 2026-11-10 7:08 UTC (permalink / raw)
To: Bin Liu, Greg Kroah-Hartman, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland
Cc: regressions, Zijun Hu, linux-usb, linux-arm-kernel, linux-sunxi,
linux-kernel, Rajan Yadav, stable
From: Rajan Yadav <rajanyadav0307@gmail.com>
Binding the musb-hdrc child again after it was unbound fails on v7.4-rc1
(KickPi K5C, Allwinner A133):
# echo musb-hdrc.1.auto > /sys/bus/platform/drivers/musb-hdrc/unbind
# echo musb-hdrc.1.auto > /sys/bus/platform/drivers/musb-hdrc/bind
notifier callback sunxi_musb_host_notifier already registered
WARNING: kernel/notifier.c:24 at notifier_chain_register+0x98/0xf8, CPU#1: sh/312
...
notifier_chain_register+0x98/0xf8 (P)
raw_notifier_chain_register+0x14/0x20
extcon_register_notifier+0xa4/0xe0
devm_extcon_register_notifier+0x64/0xe0
sunxi_musb_init+0x170/0x280
musb_probe+0x330/0x125c
...
musb-hdrc musb-hdrc.1.auto: error -EEXIST: musb_init_controller failed
musb-hdrc musb-hdrc.1.auto: probe with driver musb-hdrc failed with error -17
sunxi_musb_init() runs each time the child is probed, but it registers
the extcon notifier with devm_extcon_register_notifier() on the glue
device. The notifier stays registered after the child is unbound, and
the next sunxi_musb_init() registers the same notifier_block again. It
also stays registered while no musb exists, so an extcon event can
still queue glue->work after sunxi_musb_exit() cancelled it.
Commit bb1d1ce8c74e ("usb: musb: sunxi: Uses the resource-managed
extcon API when registering extcon notifier") introduced this; before
it, the notifier was unregistered in sunxi_musb_exit() and on the
sunxi_musb_init() error path. Restore that, and unregister it before
cancel_work_sync() so that the notifier cannot queue the work again.
Tested on the K5C with v7.4-rc1 and KASAN. The bind failed as above
both without and with the previous patch. With both patches, three
unbind/bind cycles of musb-hdrc bring back the UDC each time, without
warnings or KASAN reports, and unbinding the glue device afterwards is
clean as well. This patch was not tested without the previous one, and
the sunxi_musb_init() error path was not exercised.
Claude Code (Anthropic) found the cause, wrote this fix and the commit
message, and helped with the tests; I reviewed the fix and ran the
tests on the board.
Fixes: bb1d1ce8c74e ("usb: musb: sunxi: Uses the resource-managed extcon API when registering extcon notifier")
Cc: stable@vger.kernel.org
Signed-off-by: Rajan Yadav <rajanyadav0307@gmail.com>
---
drivers/usb/musb/sunxi.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index d880c3758c9..e9107ae9d34 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -246,15 +246,19 @@ static int sunxi_musb_init(struct musb *musb)
writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
- /* Register notifier before calling phy_init() */
- ret = devm_extcon_register_notifier(glue->dev, glue->extcon,
- EXTCON_USB_HOST, &glue->host_nb);
+ /*
+ * Register notifier before calling phy_init(). It is unregistered in
+ * sunxi_musb_exit(), as this runs again each time the musb-hdrc child
+ * is bound.
+ */
+ ret = extcon_register_notifier(glue->extcon, EXTCON_USB_HOST,
+ &glue->host_nb);
if (ret)
goto error_reset_assert;
ret = phy_init(glue->phy);
if (ret)
- goto error_reset_assert;
+ goto error_unregister_notifier;
musb->isr = sunxi_musb_interrupt;
@@ -263,6 +267,9 @@ static int sunxi_musb_init(struct musb *musb)
return 0;
+error_unregister_notifier:
+ extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
+ &glue->host_nb);
error_reset_assert:
if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags))
reset_control_assert(glue->rst);
@@ -280,6 +287,9 @@ static int sunxi_musb_exit(struct musb *musb)
pm_runtime_put(musb->controller);
+ /* Before cancel_work_sync(), so the notifier cannot requeue the work */
+ extcon_unregister_notifier(glue->extcon, EXTCON_USB_HOST,
+ &glue->host_nb);
cancel_work_sync(&glue->work);
if (test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags))
phy_power_off(glue->phy);
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread