All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] usb: chipidea: stop asynchronous work before destroying roles
@ 2026-11-12  9:25 Xu Yang
  2026-11-12  9:50 ` sashiko-bot
  0 siblings, 1 reply; 3+ messages in thread
From: Xu Yang @ 2026-11-12  9:25 UTC (permalink / raw)
  To: peter.chen, gregkh
  Cc: alexander.shishkin, linux-usb, linux-kernel, jun.li, imx

From: Xu Yang <xu.yang_2@nxp.com>

Role teardown currently removes the gadget and host roles while OTG
interrupts, FSM processing and work items may still be active. Such
asynchronous processing can queue or execute role-switch operations
against partially destroyed roles. In particular, a gadget instance
may remain registered after the controller has been removed.

Disable power_lost_work before starting role teardown. During OTG
destruction, disable the IRQ and ci->work, clear canceled ID and
B-session-valid events, and remove the OTG FSM before destroying the
workqueue.

Finally, destroy the gadget and host roles only after the OTG machinery
has been stopped. This establishes a teardown order in which event
sources are quiesced before their target resources are released.

Fixes: cbec6bd55a45 ("usb: chipidea: move otg related things to otg file")
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
 drivers/usb/chipidea/core.c |  5 +++--
 drivers/usb/chipidea/otg.c  | 17 +++++++++++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3461dd763a6e..eb7b9c0746d8 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -942,10 +942,11 @@ EXPORT_SYMBOL_GPL(ci_hdrc_query_available_role);
 
 static inline void ci_role_destroy(struct ci_hdrc *ci)
 {
-	ci_hdrc_gadget_destroy(ci);
-	ci_hdrc_host_destroy(ci);
+	disable_work_sync(&ci->power_lost_work);
 	if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
 		ci_hdrc_otg_destroy(ci);
+	ci_hdrc_gadget_destroy(ci);
+	ci_hdrc_host_destroy(ci);
 }
 
 static void ci_get_otg_capable(struct ci_hdrc *ci)
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 89a6a37815f0..8ac8969a45f0 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -272,12 +272,21 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci)
  */
 void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
 {
-	if (ci->wq)
-		destroy_workqueue(ci->wq);
+	disable_irq(ci->irq);
+	if (disable_work_sync(&ci->work)) {
+		ci->id_event = false;
+		ci->b_sess_valid_event = false;
+		/* Balance disable_irq_nosync() performed in ci_otg_queue_work() */
+		enable_irq(ci->irq);
+	}
+
+	if (ci_otg_is_fsm_mode(ci))
+		ci_hdrc_otg_fsm_remove(ci);
 
 	/* Disable all OTG irq and clear status */
 	hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
 						OTGSC_INT_STATUS_BITS);
-	if (ci_otg_is_fsm_mode(ci))
-		ci_hdrc_otg_fsm_remove(ci);
+	if (ci->wq)
+		destroy_workqueue(ci->wq);
+	enable_irq(ci->irq);
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] usb: chipidea: stop asynchronous work before destroying roles
  2026-11-12  9:25 [PATCH] usb: chipidea: stop asynchronous work before destroying roles Xu Yang
@ 2026-11-12  9:50 ` sashiko-bot
  2026-11-12 10:31   ` Xu Yang
  0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-11-12  9:50 UTC (permalink / raw)
  To: Xu Yang; +Cc: linux-usb, imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] usb: chipidea: stale OTG event flags and teardown race in
  ci_hdrc_otg_destroy()
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [Critical] usb: chipidea: uncanceled otg_fsm_hrtimer in
  ci_hdrc_otg_destroy() [1]
--

commit b8def951ac8ca82d212a5f915b8e9c3017f3be77
Author: Xu Yang <xu.yang_2@nxp.com>

usb: chipidea: stop asynchronous work before destroying roles

This commit reorders role and OTG teardown so that asynchronous work items,
interrupts, and the OTG FSM are stopped before the gadget and host roles and
workqueue are destroyed.

> diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
> index fecc7d7e2f0d7..ec11056dddfaf 100644
> --- a/drivers/usb/chipidea/otg.c
> +++ b/drivers/usb/chipidea/otg.c
> @@ -264,12 +264,21 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci)
>   */
>  void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
>  {
> -	if (ci->wq)
> -		destroy_workqueue(ci->wq);
> +	disable_irq(ci->irq);
> +	if (disable_work_sync(&ci->work)) {
> +		ci->id_event = false;
> +		ci->b_sess_valid_event = false;
> +		/* Balance disable_irq_nosync() performed in ci_otg_queue_work() */
> +		enable_irq(ci->irq);
> +	}
> +
> +	if (ci_otg_is_fsm_mode(ci))
> +		ci_hdrc_otg_fsm_remove(ci);

[Severity: Critical]
This problem wasn't introduced by this patch, but can ci->otg_fsm_hrtimer
fire after teardown [1] because neither ci_hdrc_otg_fsm_remove() nor
ci_hdrc_otg_destroy() cancels it?

In OTG FSM mode, ci_otg_add_timer() arms ci->otg_fsm_hrtimer with
ci_otg_hrtimer_func(), while ci_hdrc_otg_fsm_remove() only removes the sysfs
group.  If an armed timer expires during or after teardown,
ci_otg_hrtimer_func() dereferences the freed struct ci_hdrc and queues work on
the destroyed ci->wq workqueue, causing a use-after-free.

>  	/* Disable all OTG irq and clear status */
>  	hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
>  						OTGSC_INT_STATUS_BITS);
> -	if (ci_otg_is_fsm_mode(ci))
> -		ci_hdrc_otg_fsm_remove(ci);
> +	if (ci->wq)
> +		destroy_workqueue(ci->wq);
> +	enable_irq(ci->irq);
>  }

[Severity: Medium]
Can leaving ci->id_event or ci->b_sess_valid_event set while ci->work is
disabled cause an unhandled interrupt storm after enable_irq(ci->irq)?

ci_hdrc_otg_destroy() only clears ci->id_event and ci->b_sess_valid_event when
disable_work_sync(&ci->work) cancels a pending work item, and it disables
ci->work before hw_write_otgsc() clears cable->enabled.  An extcon event
arriving via ci_cable_notifier() -> ci_irq() before hw_write_otgsc() runs
(which disable_irq(ci->irq) does not block), or an earlier OTG FSM transition
past OTG_STATE_A_IDLE in ci_otg_fsm_work(), leaves ci->id_event or
ci->b_sess_valid_event set with ci->work disabled:

drivers/usb/chipidea/core.c:ci_irq_handler() {
	if (ci->is_otg) {
		...
		if (ci->id_event || ci->b_sess_valid_event) {
			ci_otg_queue_work(ci);
			return IRQ_HANDLED;
		}
	}

	/* Handle device/host interrupt */
	if (ci->role != CI_ROLE_END)
		ret = ci_role(ci)->irq(ci);
}

Once enable_irq(ci->irq) runs at the end of ci_hdrc_otg_destroy(), any host or
gadget interrupt during role teardown takes that early return in
ci_irq_handler().  Because ci->work is disabled, queue_work() in
ci_otg_queue_work() returns false and immediately calls enable_irq(ci->irq) in
hardirq context without ever calling ci_role(ci)->irq(ci) to clear the
hardware interrupt status.

Disabling OTG interrupts with hw_write_otgsc() before disable_work_sync() and
unconditionally clearing ci->id_event and ci->b_sess_valid_event after
disable_work_sync() would avoid this.

[1] https://sashiko.dev/#/bug/linux-8569f327-701f-4376-8f59-b4c911b172bb

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261112092556.2747348-1-xu.yang_2@oss.nxp.com?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] usb: chipidea: stop asynchronous work before destroying roles
  2026-11-12  9:50 ` sashiko-bot
@ 2026-11-12 10:31   ` Xu Yang
  0 siblings, 0 replies; 3+ messages in thread
From: Xu Yang @ 2026-11-12 10:31 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-usb, imx, Frank.Li

Hi,

On Thu, Nov 12, 2026 at 09:50:13AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue to consider:
> - [Medium] usb: chipidea: stale OTG event flags and teardown race in
>   ci_hdrc_otg_destroy()

It's related to extcon notification. The issue is introduced by commit 3f991aa0b665
("usb: chipdata: Replace the extcon API") in 2016 which switched the driver to use
the devm_extcon_* APIs.

Reverting that commit avoids the issue. But I would perfer to leave it as is, given
that it has existed for almost 10 years without any reported issue.


> It also noticed 1 related pre-existing issue this patch does not
> introduce:
> - [Critical] usb: chipidea: uncanceled otg_fsm_hrtimer in
>   ci_hdrc_otg_destroy() [1]

It's a pre-existing FSM issue and not introduced by this patch, we don't plan
to fix it as FSM is fading out.

Thanks,
Xu Yang

> --
> 
> commit b8def951ac8ca82d212a5f915b8e9c3017f3be77
> Author: Xu Yang <xu.yang_2@nxp.com>
> 
> usb: chipidea: stop asynchronous work before destroying roles
> 
> This commit reorders role and OTG teardown so that asynchronous work items,
> interrupts, and the OTG FSM are stopped before the gadget and host roles and
> workqueue are destroyed.
> 
> > diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
> > index fecc7d7e2f0d7..ec11056dddfaf 100644
> > --- a/drivers/usb/chipidea/otg.c
> > +++ b/drivers/usb/chipidea/otg.c
> > @@ -264,12 +264,21 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci)
> >   */
> >  void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
> >  {
> > -	if (ci->wq)
> > -		destroy_workqueue(ci->wq);
> > +	disable_irq(ci->irq);
> > +	if (disable_work_sync(&ci->work)) {
> > +		ci->id_event = false;
> > +		ci->b_sess_valid_event = false;
> > +		/* Balance disable_irq_nosync() performed in ci_otg_queue_work() */
> > +		enable_irq(ci->irq);
> > +	}
> > +
> > +	if (ci_otg_is_fsm_mode(ci))
> > +		ci_hdrc_otg_fsm_remove(ci);
> 
> [Severity: Critical]
> This problem wasn't introduced by this patch, but can ci->otg_fsm_hrtimer
> fire after teardown [1] because neither ci_hdrc_otg_fsm_remove() nor
> ci_hdrc_otg_destroy() cancels it?
> 
> In OTG FSM mode, ci_otg_add_timer() arms ci->otg_fsm_hrtimer with
> ci_otg_hrtimer_func(), while ci_hdrc_otg_fsm_remove() only removes the sysfs
> group.  If an armed timer expires during or after teardown,
> ci_otg_hrtimer_func() dereferences the freed struct ci_hdrc and queues work on
> the destroyed ci->wq workqueue, causing a use-after-free.
> 
> >  	/* Disable all OTG irq and clear status */
> >  	hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
> >  						OTGSC_INT_STATUS_BITS);
> > -	if (ci_otg_is_fsm_mode(ci))
> > -		ci_hdrc_otg_fsm_remove(ci);
> > +	if (ci->wq)
> > +		destroy_workqueue(ci->wq);
> > +	enable_irq(ci->irq);
> >  }
> 
> [Severity: Medium]
> Can leaving ci->id_event or ci->b_sess_valid_event set while ci->work is
> disabled cause an unhandled interrupt storm after enable_irq(ci->irq)?
> 
> ci_hdrc_otg_destroy() only clears ci->id_event and ci->b_sess_valid_event when
> disable_work_sync(&ci->work) cancels a pending work item, and it disables
> ci->work before hw_write_otgsc() clears cable->enabled.  An extcon event
> arriving via ci_cable_notifier() -> ci_irq() before hw_write_otgsc() runs
> (which disable_irq(ci->irq) does not block), or an earlier OTG FSM transition
> past OTG_STATE_A_IDLE in ci_otg_fsm_work(), leaves ci->id_event or
> ci->b_sess_valid_event set with ci->work disabled:
> 
> drivers/usb/chipidea/core.c:ci_irq_handler() {
> 	if (ci->is_otg) {
> 		...
> 		if (ci->id_event || ci->b_sess_valid_event) {
> 			ci_otg_queue_work(ci);
> 			return IRQ_HANDLED;
> 		}
> 	}
> 
> 	/* Handle device/host interrupt */
> 	if (ci->role != CI_ROLE_END)
> 		ret = ci_role(ci)->irq(ci);
> }
> 
> Once enable_irq(ci->irq) runs at the end of ci_hdrc_otg_destroy(), any host or
> gadget interrupt during role teardown takes that early return in
> ci_irq_handler().  Because ci->work is disabled, queue_work() in
> ci_otg_queue_work() returns false and immediately calls enable_irq(ci->irq) in
> hardirq context without ever calling ci_role(ci)->irq(ci) to clear the
> hardware interrupt status.
> 
> Disabling OTG interrupts with hw_write_otgsc() before disable_work_sync() and
> unconditionally clearing ci->id_event and ci->b_sess_valid_event after
> disable_work_sync() would avoid this.
> 
> [1] https://sashiko.dev/#/bug/linux-8569f327-701f-4376-8f59-b4c911b172bb
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20261112092556.2747348-1-xu.yang_2@oss.nxp.com?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-11-12 10:34 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-12  9:25 [PATCH] usb: chipidea: stop asynchronous work before destroying roles Xu Yang
2026-11-12  9:50 ` sashiko-bot
2026-11-12 10:31   ` Xu Yang

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.