From: sashiko-bot@kernel.org
To: "Qianhui Xu" <xiaqinghuan@mail.ustc.edu.cn>, linux-usb@vger.kernel.org
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus
Date: Tue, 13 Oct 2026 11:02:55 +0000 [thread overview]
Message-ID: <sashiko-outbox-169577@kernel.org> (raw)
In-Reply-To: <20261013102927.1141696-4-xiaqinghuan@mail.ustc.edu.cn>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] pm: Unbalanced pm_runtime_put_autosuspend on error path in __dwc3_set_mode
- [High] usb: dwc3: Double-free/UAF of gadget/host resources on GPIO failure in __dwc3_set_mode
- [High] usb: dwc3: deadlock during suspend due to flush_work() on freezable wq
- [Medium] usb: dwc3: Role selection logic implements static priority, contradicting "oldest wins" design
--
commit e9f771770a6e130d4f1859d1ec3e53526b8f1f97
Author: Xu Qianhui <xiaqinghuan@mail.ustc.edu.cn>
Subject: usb: dwc3: support two connectors sharing one HS data bus
This commit introduces support for a single DWC3 controller to drive two
receptacles by muxing the High Speed data lines. It registers one USB role
switch per connector and serializes requests using a mutex, intending to
grant the role to the oldest connector.
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index fd5c2cd36c595..76e9e5d1808f0 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -174,8 +178,15 @@ static void __dwc3_set_mode(struct work_struct *work)
> mutex_lock(&dwc->mutex);
> spin_lock_irqsave(&dwc->lock, flags);
> desired_dr_role = dwc->desired_dr_role;
> + if (mux) {
> + mux_port = mux->desired_port;
> + mux_role = mux->desired_role;
> + }
> spin_unlock_irqrestore(&dwc->lock, flags);
>
> + if (mux && READ_ONCE(mux->error))
> + goto out;
[Severity: High]
If this early error check evaluates to true, it jumps to the out label
before pm_runtime_get_sync() is called. The out label then unconditionally
calls pm_runtime_put_autosuspend().
Does this drop a Runtime PM reference that was never acquired, potentially
underflowing the usage count and powering down hardware that is still
actively in use?
> +
> pm_runtime_get_sync(dwc->dev);
>
> if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
[ ... ]
> @@ -233,6 +245,22 @@ static void __dwc3_set_mode(struct work_struct *work)
> dwc3_writel(dwc, DWC3_GCTL, reg);
> }
>
> + /*
> + * The previous host/gadget has been torn down above, so it is safe to
> + * move the shared HS data path to the requesting connector.
> + */
> + if (mux && mux_port != mux->active_port) {
> + ret = gpiod_direction_output(mux->select, mux_port);
> + if (ret) {
> + WRITE_ONCE(mux->error, ret);
> + goto out;
[Severity: High]
When gpiod_direction_output() fails, this jumps to the out label after
tearing down the current role, but before dwc3_set_prtcap() is called to
update dwc->current_dr_role.
During subsequent role switches or driver removal, will dwc->current_dr_role
still evaluate to the old role? If so, this could cause dwc3_gadget_exit()
or dwc3_host_exit() to execute a second time, resulting in a double-free of
setup buffers or a use-after-free on device structures.
> + }
> +
> + spin_lock_irqsave(&dwc->lock, flags);
[ ... ]
> diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
> index f3e37d383627a..4bf0497e902a7 100644
> --- a/drivers/usb/dwc3/drd.c
> +++ b/drivers/usb/dwc3/drd.c
> @@ -501,10 +502,178 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
> return role;
> }
>
> +static int dwc3_mux_role_set(struct usb_role_switch *sw, enum usb_role role)
> +{
> + struct dwc3_role_port *port = usb_role_switch_get_drvdata(sw);
> + struct dwc3 *dwc = port->dwc;
> + struct dwc3_role_mux *mux = dwc->role_mux;
> + enum usb_role selected_role;
> + unsigned long flags;
> + int selected, ret;
> + u32 mode;
> +
> + if (role < USB_ROLE_NONE || role > USB_ROLE_DEVICE)
> + return -EINVAL;
> +
> + mutex_lock(&mux->lock);
> + if (mux->stopping) {
> + ret = -ESHUTDOWN;
> + goto out;
> + }
> + if (READ_ONCE(mux->error)) {
> + ret = READ_ONCE(mux->error);
> + goto out;
> + }
> +
> + port->role = role;
> + if (mux->ports[0].role != USB_ROLE_NONE)
> + selected = 0;
> + else if (mux->ports[1].role != USB_ROLE_NONE)
> + selected = 1;
[Severity: Medium]
The commit message states that "requests from the two switches are
serialised by a mutex, the oldest connector holding a role wins".
However, this static conditional structure seems to prioritize port 0.
If port 1 is the oldest connector actively holding a role, and port 0
subsequently requests a role, won't this check evaluate port 0 first?
Setting selected = 0 here appears to abruptly preempt port 1 and give
the role to the newer connector, breaking the intended arbitration logic.
> + else
> + selected = mux->desired_port;
> + selected_role = mux->ports[selected].role;
> + if (selected == mux->desired_port && selected_role == mux->desired_role) {
> + ret = 0;
> + goto out;
> + }
> +
> + if (selected_role == USB_ROLE_HOST ||
> + (selected_role == USB_ROLE_NONE &&
> + dwc->role_switch_default_mode == USB_DR_MODE_HOST))
> + mode = DWC3_GCTL_PRTCAP_HOST;
> + else
> + mode = DWC3_GCTL_PRTCAP_DEVICE;
> +
> + spin_lock_irqsave(&dwc->lock, flags);
> + mux->desired_port = selected;
> + mux->desired_role = selected_role;
> + dwc->desired_dr_role = mode;
> + spin_unlock_irqrestore(&dwc->lock, flags);
> + queue_work(system_freezable_wq, &dwc->drd_work);
> + flush_work(&dwc->drd_work);
[Severity: High]
This code queues drd_work on system_freezable_wq and synchronously blocks
on it using flush_work().
Since role switch events can be triggered from non-freezable contexts
(such as ucsi_handle_connector_change running on a non-freezable workqueue),
what happens if a role switch occurs during system suspend after freezable
workqueues have been frozen?
Could flush_work() block indefinitely since drd_work cannot run, leading to
a complete system suspend deadlock?
> + ret = READ_ONCE(mux->error);
> +out:
> + mutex_unlock(&mux->lock);
> + return ret;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013102927.1141696-1-xiaqinghuan@mail.ustc.edu.cn?part=3
prev parent reply other threads:[~2026-10-13 11:02 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 10:29 [PATCH 0/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-13 10:29 ` [PATCH 1/3] dt-bindings: usb: snps,dwc3: document usb-psy-name Qianhui Xu
2026-10-13 10:35 ` sashiko-bot
2026-10-13 10:29 ` [PATCH 2/3] dt-bindings: usb: snps,dwc3: describe the two-connector HS data path Qianhui Xu
2026-10-13 10:45 ` sashiko-bot
2026-10-13 10:29 ` [PATCH 3/3] usb: dwc3: support two connectors sharing one HS data bus Qianhui Xu
2026-10-13 11:02 ` sashiko-bot [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=sashiko-outbox-169577@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=xiaqinghuan@mail.ustc.edu.cn \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.