* [PATCH 0/4] usb: stop timers/works left armed on teardown
@ 2026-11-12 16:04 Chengyu Liu
2026-11-12 16:04 ` [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove() Chengyu Liu
` (4 more replies)
0 siblings, 5 replies; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:04 UTC (permalink / raw)
To: linux-usb
Cc: b-liu, gregkh, nkapron, tony, bigeasy, balbi, shimoda.yoshihiro,
robert.jarzmik, Chengyu Liu
The teardown paths in these four drivers do not quiesce every producer
of asynchronous work before draining it: an interrupt handler may still
queue or re-arm a timer or work item after the cancellation, while the
corresponding object is being torn down. In three of them the IRQ is
devm-managed and freed only at detach, after remove() returns; in musb
the plain free_irq() happens later in the same remove path. Either way
a late interrupt re-arms the callback, which then runs on freed memory:
[1/4] musb core: musb_stage0_irq() can schedule irq_work while
handling stage-0 USB interrupts, and the work can also re-arm
itself from the session recheck path; musb_remove() cancels it
before masking the interrupts and before free_irq().
[2/4] musb dsps glue: the devm VBUS interrupt outlives the child
teardown that deletes musb->dev_timer; its handler re-arms
the timer via dsps_mod_timer().
[3/4] r8a66597: r8a66597_remove() deletes the timer while the devm
IRQ can still re-arm it from the VBINT path.
[4/4] gpio-vbus: gpio_vbus_remove() cancels the work while the devm
GPIO interrupt can still re-arm it (100 ms debounce delay).
Two further instances of this defect class found in the same sweep -
pch_udc and lpc32xx_udc - are being handled in Hyunjung Ko's
"usb: gadget: pch_udc: drain vbus GPIO works before freeing the
device" and "usb: gadget: lpc32xx_udc: drain the pullup work on
removal" threads, so they are not part of this series.
Testing:
[1-3/4] build-tested on x86_64 (COMPILE_TEST where needed); they
require hardware we do not have, so no runtime reproducer.
[4/4] additionally confirmed with a KASAN reproducer: the driver
probed on a gpio-sim chip in QEMU, slab-use-after-free write
in __run_timer_base() after unregister (5/5 runs), clean with
the fix (0/5). The KASAN report and the reproducer kit follow
in this thread.
All found by static analysis. Fixes written with AI assistance.
Chengyu Liu (4):
usb: musb: cancel irq_work after masking interrupts in musb_remove()
usb: musb: dsps: free the VBUS interrupt before the child teardown
usb: gadget: r8a66597: free the IRQ before deleting the timer
usb: phy: gpio-vbus: free the interrupt before cancelling the work
drivers/usb/gadget/udc/r8a66597-udc.c | 8 ++++++++
drivers/usb/gadget/udc/r8a66597-udc.h | 1 +
drivers/usb/musb/musb_core.c | 10 +++++++++-
drivers/usb/musb/musb_dsps.c | 11 +++++++++++
drivers/usb/phy/phy-gpio-vbus-usb.c | 8 ++++++++
5 files changed, 37 insertions(+), 1 deletion(-)
Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
--
2.25.1
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove()
2026-11-12 16:04 [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
@ 2026-11-12 16:04 ` Chengyu Liu
2026-11-12 16:23 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:04 ` [PATCH 2/4] usb: musb: dsps: free the VBUS interrupt before the child teardown Chengyu Liu
` (3 subsequent siblings)
4 siblings, 2 replies; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:04 UTC (permalink / raw)
To: linux-usb
Cc: b-liu, gregkh, nkapron, tony, bigeasy, balbi, shimoda.yoshihiro,
robert.jarzmik, Chengyu Liu, stable
musb_stage0_irq() can schedule irq_work while handling stage-0 USB
interrupts, and the work can also re-arm itself from the session
recheck path (musb_pm_runtime_check_session()). musb_remove()
cancels irq_work while the interrupt is still enabled and the IRQ
line not yet freed: an interrupt landing after the cancel re-arms
the work, and musb_irq_work() then runs after musb_free() has
released the struct.
Move the cancel behind musb_disable_interrupts() and synchronize the
IRQ line first, so no handler can be running or re-arm the work past
the cancel.
Found by static analysis.
Fixes: c5340bd14336 ("usb: musb: cancel work on removal")
Cc: stable@vger.kernel.org
Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
---
Build-tested on x86_64: musb_core.o compiles clean
(USB_MUSB_HDRC). No runtime reproducer: requires MUSB-based OTG
hardware. Fix written with AI assistance.
drivers/usb/musb/musb_core.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 9b63b56edc45..eea43a20a1d0 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -2630,7 +2630,6 @@ static void musb_remove(struct platform_device *pdev)
*/
musb_exit_debugfs(musb);
- cancel_delayed_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
pm_runtime_get_sync(musb->controller);
@@ -2642,6 +2641,15 @@ static void musb_remove(struct platform_device *pdev)
musb_disable_interrupts(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
spin_unlock_irqrestore(&musb->lock, flags);
+
+ /*
+ * musb_stage0_irq() arms irq_work, so it can only be cancelled
+ * for good once interrupts are masked and any in-flight handler
+ * has completed.
+ */
+ synchronize_irq(musb->nIrq);
+ cancel_delayed_work_sync(&musb->irq_work);
+
musb_platform_exit(musb);
pm_runtime_dont_use_autosuspend(musb->controller);
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/4] usb: musb: dsps: free the VBUS interrupt before the child teardown
2026-11-12 16:04 [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
2026-11-12 16:04 ` [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove() Chengyu Liu
@ 2026-11-12 16:04 ` Chengyu Liu
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:04 ` [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer Chengyu Liu
` (2 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:04 UTC (permalink / raw)
To: linux-usb
Cc: b-liu, gregkh, nkapron, tony, bigeasy, balbi, shimoda.yoshihiro,
robert.jarzmik, Chengyu Liu, stable
dsps_remove() unregisters the musb child, which deletes
musb->dev_timer and frees the structure, while the glue's VBUS
interrupt is devm-managed and only freed after dsps_remove() returns.
dsps_vbus_threaded_irq() re-arms the timer via dsps_mod_timer(), so
a VBUS event in between re-arms dev_timer past its deletion and
otg_timer() runs on freed memory.
Free the VBUS interrupt first, which also waits for any in-flight
handler, so nothing can re-arm the timer once the child teardown
runs.
Found by static analysis.
Fixes: 369469a92393 ("usb: musb: Add support for optional VBUS irq to dsps glue layer")
Cc: stable@vger.kernel.org
Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
---
Build-tested on x86_64: musb_dsps.o compiles clean
(USB_MUSB_DSPS). No runtime reproducer: requires AM33xx/DSPS
hardware. Fix written with AI assistance.
drivers/usb/musb/musb_dsps.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 91d8712efa41..69c8b4b3ffa7 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -942,6 +942,17 @@ static void dsps_remove(struct platform_device *pdev)
{
struct dsps_glue *glue = platform_get_drvdata(pdev);
+ /*
+ * The VBUS interrupt handler re-arms musb->dev_timer via
+ * dsps_mod_timer(); the IRQ is devm-managed and would only be
+ * freed after this function returns, while the child teardown
+ * below deletes the timer and frees the musb structure. Free
+ * the interrupt first so no VBUS event can re-arm the timer
+ * past that point.
+ */
+ if (glue->vbus_irq)
+ devm_free_irq(glue->dev, glue->vbus_irq, glue);
+
platform_device_unregister(glue->musb);
pm_runtime_disable(&pdev->dev);
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer
2026-11-12 16:04 [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
2026-11-12 16:04 ` [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove() Chengyu Liu
2026-11-12 16:04 ` [PATCH 2/4] usb: musb: dsps: free the VBUS interrupt before the child teardown Chengyu Liu
@ 2026-11-12 16:04 ` Chengyu Liu
2026-11-12 16:18 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:04 ` [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work Chengyu Liu
2026-11-12 16:10 ` [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
4 siblings, 2 replies; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:04 UTC (permalink / raw)
To: linux-usb
Cc: b-liu, gregkh, nkapron, tony, bigeasy, balbi, shimoda.yoshihiro,
robert.jarzmik, Chengyu Liu, stable
r8a66597_remove() deletes r8a66597->timer while the interrupt is
still live: the IRQ is devm-managed and only freed at detach, and
r8a66597_irq() re-arms the timer when sampling VBUS (VBINT). An
interrupt landing after timer_delete_sync() re-arms the timer, and
r8a66597_timer() then runs on freed memory.
Store the requested IRQ number at probe time and free it before
deleting the timer; freeing also waits for any running handler, so
nothing can re-arm the timer past the delete.
Found by static analysis.
Fixes: c41442474a26 ("usb: gadget: R8A66597 peripheral controller support.")
Cc: stable@vger.kernel.org
Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
---
Build-tested on x86_64: r8a66597-udc.o compiles clean
(USB_R8A66597 + COMPILE_TEST). No runtime reproducer: requires
R8A66597 hardware. Fix written with AI assistance.
drivers/usb/gadget/udc/r8a66597-udc.c | 8 ++++++++
drivers/usb/gadget/udc/r8a66597-udc.h | 1 +
2 files changed, 9 insertions(+)
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 469075b064ba..bfccc6565e0a 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1810,6 +1810,13 @@ static void r8a66597_remove(struct platform_device *pdev)
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
usb_del_gadget_udc(&r8a66597->gadget);
+
+ /*
+ * The interrupt handler re-arms the timer on VBUS changes; the
+ * IRQ is devm-managed and freed only at detach, so free it
+ * before deleting the timer.
+ */
+ devm_free_irq(&pdev->dev, r8a66597->irq, r8a66597);
timer_delete_sync(&r8a66597->timer);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
@@ -1896,6 +1903,7 @@ static int r8a66597_probe(struct platform_device *pdev)
udc_name, r8a66597);
if (ret < 0)
goto clean_up2;
+ r8a66597->irq = irq;
INIT_LIST_HEAD(&r8a66597->gadget.ep_list);
r8a66597->gadget.ep0 = &r8a66597->ep[0].ep;
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h
index 9a115caba661..08acabe02421 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.h
+++ b/drivers/usb/gadget/udc/r8a66597-udc.h
@@ -110,6 +110,7 @@ struct r8a66597 {
unsigned char interrupt;
unsigned char isochronous;
unsigned char num_dma;
+ int irq;
unsigned irq_sense_low:1;
};
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work
2026-11-12 16:04 [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
` (2 preceding siblings ...)
2026-11-12 16:04 ` [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer Chengyu Liu
@ 2026-11-12 16:04 ` Chengyu Liu
2026-11-12 16:16 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:10 ` [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
4 siblings, 2 replies; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:04 UTC (permalink / raw)
To: linux-usb
Cc: b-liu, gregkh, nkapron, tony, bigeasy, balbi, shimoda.yoshihiro,
robert.jarzmik, Chengyu Liu, stable
gpio_vbus_remove() cancels gpio_vbus->work while the VBUS sensing GPIO
interrupt is still live: it is devm-managed and freed only at detach,
and gpio_vbus_irq() schedules the work with a 100 ms debounce delay.
A plug event landing after the cancel re-arms the work, and its timer
then fires on the freed gpio_vbus_data.
Free the interrupt first, which also waits for any running handler,
so nothing can re-arm the work past the cancel.
Found by static analysis and confirmed with a KASAN reproducer:
slab-use-after-free write in __run_timer_base() within one debounce
interval of unregistering the device while its VBUS GPIO is toggled
(object allocated by gpio_vbus_probe() via devm_kmalloc(), freed by
devres_release_all() at unbind).
Fixes: c2344f13b59e ("USB: gpio_vbus: add delayed vbus_session calls")
Cc: stable@vger.kernel.org
Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
---
The reproducer instantiates the unmodified driver on a gpio-sim
chip in QEMU and toggles the line while the device is unregistered;
it widens the remove()-to-detach window with a 500 ms sleep, logic
otherwise unmodified. Repeated runs: 5/5 KASAN splats without the
fix, 0/5 with it. The KASAN report and the reproducer kit follow
in this thread. Also build-tested on x86_64 (USB_GPIO_VBUS).
Fix written with AI assistance.
drivers/usb/phy/phy-gpio-vbus-usb.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 60b7c1219dd2..82d2301c3804 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -325,6 +325,14 @@ static void gpio_vbus_remove(struct platform_device *pdev)
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
+
+ /*
+ * The VBUS sensing GPIO interrupt is devm-managed and freed only
+ * at detach, and its handler schedules the work with a 100ms
+ * delay; free the interrupt before cancelling the work so a
+ * late plug event cannot re-arm it past the cancel.
+ */
+ devm_free_irq(&pdev->dev, gpio_vbus->irq, pdev);
cancel_delayed_work_sync(&gpio_vbus->work);
usb_remove_phy(&gpio_vbus->phy);
--
2.25.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 0/4] usb: stop timers/works left armed on teardown
2026-11-12 16:04 [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
` (3 preceding siblings ...)
2026-11-12 16:04 ` [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work Chengyu Liu
@ 2026-11-12 16:10 ` Chengyu Liu
4 siblings, 0 replies; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:10 UTC (permalink / raw)
To: linux-usb
Cc: b-liu, gregkh, nkapron, tony, bigeasy, balbi, shimoda.yoshihiro,
robert.jarzmik, Chengyu Liu
The KASAN report and the reproducer kit for the gpio-vbus instance
[4/4], as promised in the cover letter.
Environment: QEMU x86_64, generic KASAN kernel (v7.3-rc7 plus unrelated
local commits; config: KASAN/KASAN_GENERIC=y, GPIOLIB=y, GPIO_SIM=y,
CONFIGFS_FS=y, USB_GADGET=m, USB_GPIO_VBUS=m),
phy-gpio-vbus-usb built as a module. The driver under test is
unmodified except for the disclosed 500 ms sleep widening the
remove()-to-detach window.
Setup: a one-line gpio-sim chip labelled "vbus-sim" stands in for the
VBUS sense GPIO; writing its "pull" sysfs attribute generates real GPIO
edge interrupts. The vbus_rig module registers a gpiod lookup table and
the "gpio-vbus" platform device, so the real driver probes it, and
attaches a dummy usb_gadget to phy->otg->gadget so gpio_vbus_irq() takes
its normal schedule_delayed_work() path (a UDC provides this on real
hardware); the module fails to load if the phy cannot be attached, so a
clean run cannot be a setup artifact. rmmod vbus_rig runs the real
gpio_vbus_remove() while the line is being toggled.
Results over repeated runs of the same script:
vulnerable kernel: 5/5 runs, KASAN slab-use-after-free each time
patched kernel: 5/5 runs, clean
Representative vulnerable splat (VBUS edges toggle at max rate while the
device is unregistered; an interrupt lands after the
cancel_delayed_work_sync(), re-arms the 100 ms debounce work, and the
work's timer fires after the devm release has freed gpio_vbus_data):
[ 3.571772] BUG: KASAN: slab-use-after-free in __run_timer_base.part.0+0x80c/0x900
[ 3.571779] Write of size 8 at addr ffff8880033291d8 by task sh/89
[ 3.571781]
[ 3.571784] CPU: 0 UID: 0 PID: 89 Comm: sh Tainted: G W O 7.3.0-rc7-00036-gf9532d351e12-dirty #29 PREEMPT(lazy)
[ 3.571788] Tainted: [W]=WARN, [O]=OOT_MODULE
[ 3.571788] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
[ 3.571790] Call Trace:
[ 3.571791] <IRQ>
[ 3.571792] dump_stack_lvl+0x55/0x70
[ 3.571808] print_report+0x10d/0x5d0
[ 3.571811] ? __run_timer_base.part.0+0x80c/0x900
[ 3.571813] kasan_report+0x96/0xd0
[ 3.571815] ? __run_timer_base.part.0+0x80c/0x900
[ 3.571817] __run_timer_base.part.0+0x80c/0x900
[ 3.571819] ? __pfx___run_timer_base.part.0+0x10/0x10
[ 3.571821] ? __pfx___hrtimer_get_next_event+0x10/0x10
[ 3.571825] ? kvm_clock_read+0x15/0x30
[ 3.571827] ? __hrtimer_run_queues+0x2aa/0x580
[ 3.571829] ? clockevents_program_event+0x2b0/0x7c0
[ 3.571833] ? hrtimer_update_next_event+0x8a/0x100
[ 3.571835] run_timer_softirq+0xd6/0x190
[ 3.571837] handle_softirqs+0x184/0x5c0
[ 3.571840] ? __hrtimer_rearm_deferred+0x15d/0x460
[ 3.571841] irq_exit_rcu+0x6e/0x150
[ 3.571843] sysvec_apic_timer_interrupt+0x70/0x80
[ 3.571845] </IRQ>
[ 3.571846] <TASK>
[ 3.571846] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 3.571849] RIP: 0010:stack_depot_save_flags+0x61/0x830
[ 3.571852] Code: 70 05 05 00 74 16 48 83 c4 30 44 89 e8 5b 5d 41 5c 41 5d 41 5e 41 5f c3 cc cc cc cc 89 c1 48 01 c9 89 cf 8d 04 8d 7b 71 f5 75 <83> f9 03 0f 86 36 01 00 00 89 c3 89 c1 4c 89 fe 03 5e 08 03 06 83
[ 3.571854] RSP: 0018:ffff888002207a08 EFLAGS: 00000216
[ 3.571856] RAX: 0000000075f571cb RBX: ffff888003880988 RCX: 0000000000000014
[ 3.571858] RDX: ffffffffb3000130 RSI: 000000000000000a RDI: 0000000000000014
[ 3.571859] RBP: ffff888001041640 R08: 0000000000000000 R09: ffffffffb87baec6
[ 3.571859] R10: ffff888002207978 R11: ffff888002207f50 R12: 0000000000000001
[ 3.571860] R13: 0000000000000000 R14: 000000000000000a R15: ffff888002207a70
[ 3.571862] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3.571864] ? stack_depot_save_flags+0x2d/0x830
[ 3.571866] ? kernfs_fop_write_iter+0x168/0x480
[ 3.571868] kasan_save_stack+0x34/0x50
[ 3.571870] ? kasan_save_stack+0x24/0x50
[ 3.571871] ? kasan_save_track+0x14/0x30
[ 3.571872] ? kasan_save_free_info+0x3a/0x60
[ 3.571874] ? __kasan_slab_free+0x43/0x70
[ 3.571875] ? kfree+0x18e/0x3c0
[ 3.571877] ? kernfs_fop_write_iter+0x168/0x480
[ 3.571878] ? vfs_write+0x87a/0xd40
[ 3.571880] ? ksys_write+0xf3/0x1c0
[ 3.571881] ? do_syscall_64+0xee/0x500
[ 3.571883] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3.571885] ? do_sys_openat2+0x308/0x450
[ 3.571886] ? kasan_save_stack+0x34/0x50
[ 3.571888] ? kasan_save_stack+0x24/0x50
[ 3.571889] ? kasan_save_track+0x14/0x30
[ 3.571890] ? kasan_save_free_info+0x3a/0x60
[ 3.571892] ? __kasan_slab_free+0x43/0x70
[ 3.571893] ? kmem_cache_free+0x1d4/0x3c0
[ 3.571895] ? do_sys_openat2+0x308/0x450
[ 3.571896] ? do_sys_open+0x8d/0xe0
[ 3.571898] ? __x64_sys_openat+0x9d/0x110
[ 3.571899] ? do_syscall_64+0xee/0x500
[ 3.571901] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3.571902] ? __pfx_path_openat+0x10/0x10
[ 3.571905] ? kasan_save_stack+0x34/0x50
[ 3.571906] ? kasan_save_stack+0x24/0x50
[ 3.571907] ? mutex_unlock+0x82/0xd0
[ 3.571909] ? __pfx_mutex_unlock+0x10/0x10
[ 3.571911] ? _raw_spin_lock_irqsave+0x8b/0xf0
[ 3.571913] ? gpio_sim_apply_pull+0x1c8/0x4e0
[ 3.571915] ? __pfx_gpio_sim_apply_pull+0x10/0x10
[ 3.571917] ? kasan_save_track+0x14/0x30
[ 3.571918] kasan_save_track+0x14/0x30
[ 3.571920] kasan_save_free_info+0x3a/0x60
[ 3.571921] __kasan_slab_free+0x43/0x70
[ 3.571923] kfree+0x18e/0x3c0
[ 3.571925] ? __pfx_sysfs_kf_write+0x10/0x10
[ 3.571926] kernfs_fop_write_iter+0x168/0x480
[ 3.571928] vfs_write+0x87a/0xd40
[ 3.571929] ? alloc_fd+0x2c6/0x5d0
[ 3.571932] ? __pfx_vfs_write+0x10/0x10
[ 3.571933] ? __do_sys_wait4+0xf2/0x100
[ 3.571935] ? fdget_pos+0x1cd/0x4c0
[ 3.571937] ksys_write+0xf3/0x1c0
[ 3.571939] ? __pfx_ksys_write+0x10/0x10
[ 3.571940] do_syscall_64+0xee/0x500
[ 3.571942] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3.571944] RIP: 0033:0x49a227
[ 3.571945] Code: 64 89 02 48 c7 c0 ff ff ff ff eb bb 0f 1f 80 00 00 00 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24
[ 3.571947] RSP: 002b:00007ffd89cad1e8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[ 3.571948] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 000000000049a227
[ 3.571949] RDX: 000000000000000a RSI: 000000003e0afe50 RDI: 0000000000000001
[ 3.571951] RBP: 000000003e0afe50 R08: 000000003e0afe50 R09: 006e776f642d6c6c
[ 3.571952] R10: 0000000000000000 R11: 0000000000000246 R12: 000000000000000a
[ 3.571953] R13: 000000003e0ad8a0 R14: 0000000000609c01 R15: 00007ffd89cad250
[ 3.571955] </TASK>
[ 3.571955]
[ 3.571956] Allocated by task 86:
[ 3.571957] kasan_save_stack+0x24/0x50
[ 3.571959] kasan_save_track+0x14/0x30
[ 3.571960] __kasan_kmalloc+0x7f/0x90
[ 3.571961] __kmalloc_node_track_caller_noprof+0x1d4/0x4a0
[ 3.571963] devm_kmalloc+0x81/0x240
[ 3.571965] gpio_vbus_probe+0x30/0x670 [phy_gpio_vbus_usb]
[ 3.571968] platform_probe+0x9e/0x140
[ 3.571970] really_probe+0x1cf/0x5c0
[ 3.571972] __driver_probe_device+0x1a1/0x3b0
[ 3.571974] driver_probe_device+0x49/0xd0
[ 3.571975] __device_attach_driver+0x183/0x200
[ 3.571977] bus_for_each_drv+0x105/0x190
[ 3.571979] __device_attach+0x20b/0x380
[ 3.571981] device_initial_probe+0x7d/0xa0
[ 3.571982] bus_probe_device+0x5a/0x130
[ 3.571983] device_add+0xd6f/0x14c0
[ 3.571984] platform_device_add+0x253/0x5a0
[ 3.571986] platform_device_register_full+0x3c3/0x560
[ 3.571988] 0xffffffffc02250aa
[ 3.571989] do_one_initcall+0x89/0x350
[ 3.571990] do_init_module+0x2a1/0x890
[ 3.571992] load_module+0x5166/0x6750
[ 3.571994] init_module_from_file+0x162/0x190
[ 3.571996] idempotent_init_module+0x325/0x680
[ 3.571997] __x64_sys_finit_module+0xc2/0x130
[ 3.571999] do_syscall_64+0xee/0x500
[ 3.572001] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3.572002]
[ 3.572002] Freed by task 91:
[ 3.572003] kasan_save_stack+0x24/0x50
[ 3.572004] kasan_save_track+0x14/0x30
[ 3.572005] kasan_save_free_info+0x3a/0x60
[ 3.572007] __kasan_slab_free+0x43/0x70
[ 3.572008] kfree+0x18e/0x3c0
[ 3.572009] release_nodes+0xbe/0x120
[ 3.572011] devres_release_all+0x127/0x1a0
[ 3.572012] device_unbind_cleanup+0x76/0x260
[ 3.572014] device_release_driver_internal+0x163/0x210
[ 3.572015] bus_remove_device+0x256/0x3e0
[ 3.572017] device_del+0x3a8/0x960
[ 3.572019] platform_device_del.part.0+0x1f/0x1e0
[ 3.572020] platform_device_unregister+0x20/0x40
[ 3.572022] 0xffffffffc022008c
[ 3.572023] __x64_sys_delete_module+0x32c/0x4a0
[ 3.572025] do_syscall_64+0xee/0x500
[ 3.572026] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3.572027]
[ 3.572028] Last potentially related work creation:
[ 3.572028] kasan_save_stack+0x24/0x50
[ 3.572029] kasan_record_aux_stack+0x89/0xa0
[ 3.572031] insert_work+0x2d/0x240
[ 3.572033] __queue_work+0x44e/0xd70
[ 3.572034] call_timer_fn+0x27/0x210
[ 3.572035] __run_timer_base.part.0+0x3cc/0x900
[ 3.572037] run_timer_softirq+0xd6/0x190
[ 3.572039] handle_softirqs+0x184/0x5c0
[ 3.572040] irq_exit_rcu+0x6e/0x150
[ 3.572041] sysvec_apic_timer_interrupt+0x70/0x80
[ 3.572042] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 3.572044]
[ 3.572044] Second to last potentially related work creation:
[ 3.572045] kasan_save_stack+0x24/0x50
[ 3.572046] kasan_record_aux_stack+0x89/0xa0
[ 3.572047] insert_work+0x2d/0x240
[ 3.572048] __queue_work+0x44e/0xd70
[ 3.572050] call_timer_fn+0x27/0x210
[ 3.572051] __run_timer_base.part.0+0x3cc/0x900
[ 3.572052] run_timer_softirq+0xd6/0x190
[ 3.572054] handle_softirqs+0x184/0x5c0
[ 3.572055] irq_exit_rcu+0x6e/0x150
[ 3.572056] sysvec_apic_timer_interrupt+0x39/0x80
[ 3.572058] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 3.572059]
[ 3.572059] The buggy address belongs to the object at ffff888003329000
[ 3.572059] which belongs to the cache kmalloc-1k of size 1024
[ 3.572061] The buggy address is located 472 bytes inside of
[ 3.572061] freed 1024-byte region [ffff888003329000, ffff888003329400)
[ 3.572062]
[ 3.572063] The buggy address belongs to the physical page:
[ 3.572064] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888003328000 pfn:0x3328
[ 3.572066] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 3.572067] flags: 0x100000000000240(workingset|head|node=0|zone=1)
[ 3.572069] page_type: f5(slab)
[ 3.572071] raw: 0100000000000240 ffff888001041dc0 ffff888001040590 ffffea0000110610
[ 3.572073] raw: ffff888003328000 000000000010000f 00000000f5000000 0000000000000000
[ 3.572075] head: 0100000000000240 ffff888001041dc0 ffff888001040590 ffffea0000110610
[ 3.572076] head: ffff888003328000 000000000010000f 00000000f5000000 0000000000000000
[ 3.572077] head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
[ 3.572078] head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[ 3.572078] page dumped because: kasan: bad access detected
[ 3.572079]
[ 3.572079] Memory state around the buggy address:
[ 3.572080] ffff888003329080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 3.572081] ffff888003329100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 3.572082] >ffff888003329180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 3.572083] ^
[ 3.572084] ffff888003329200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 3.572084] ffff888003329280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 3.572085] ==================================================================
(The "gpiod_get_value can_sleep" WARNINGs earlier in the boot are a
gpio-sim artifact - simulated chips set can_sleep - and are unrelated
to the bug. One of the five runs faulted in gpio_vbus_work() itself
instead of __run_timer_base(); same object, same free site.)
Reproducer kit (against the KASAN kernel tree above):
vbus_rig.c:
// SPDX-License-Identifier: GPL-2.0
/*
* vbus_rig - PoC rig for the gpio-vbus teardown UAF (F-138).
*
* Instantiates a "gpio-vbus" platform device wired via a gpiod lookup
* table to line 0 of a gpio-sim chip labelled "vbus-sim", so the real
* phy-gpio-vbus-usb platform driver probes it. Attaches a dummy
* usb_gadget to phy->otg->gadget so the driver's IRQ handler takes its
* normal schedule_delayed_work() path (it skips scheduling when no
* gadget is attached - on real hardware a UDC provides the gadget).
* rmmod vbus_rig runs the real gpio_vbus_remove() path. Contains no
* vulnerable logic itself.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/machine.h>
#include <linux/usb/phy.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
static struct gpiod_lookup_table vbus_lookup = {
.dev_id = "gpio-vbus.0",
.table = {
GPIO_LOOKUP("vbus-sim", 0, "vbus", GPIO_ACTIVE_HIGH),
{ }
},
};
static const struct usb_gadget_ops vbus_rig_gadget_ops; /* all NULL */
static struct usb_gadget vbus_rig_gadget = {
.ops = (struct usb_gadget_ops *)&vbus_rig_gadget_ops,
.name = "vbus_rig",
};
static struct platform_device *vbus_pdev;
static struct usb_phy *rig_phy;
static int __init vbus_rig_init(void)
{
gpiod_add_lookup_table(&vbus_lookup);
vbus_pdev = platform_device_register_simple("gpio-vbus", 0, NULL, 0);
if (IS_ERR(vbus_pdev)) {
gpiod_remove_lookup_table(&vbus_lookup);
return PTR_ERR(vbus_pdev);
}
rig_phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR(rig_phy)) {
pr_err("vbus_rig: no phy found (%ld) - setup broken\n",
PTR_ERR(rig_phy));
goto fail;
}
if (!rig_phy->otg) {
pr_err("vbus_rig: phy has no otg - setup broken\n");
usb_put_phy(rig_phy);
goto fail;
}
rig_phy->otg->gadget = &vbus_rig_gadget;
pr_info("vbus_rig: gpio-vbus.0 registered, phy attached\n");
return 0;
fail:
platform_device_unregister(vbus_pdev);
gpiod_remove_lookup_table(&vbus_lookup);
return -ENODEV;
}
static void __exit vbus_rig_exit(void)
{
/*
* Drop our phy reference first; the driver module stays put via
* the bound device. Keep the gadget attached across the
* unregister: the driver's IRQ handler must keep taking its
* normal schedule_delayed_work() path all the way through
* remove().
*/
usb_put_phy(rig_phy);
platform_device_unregister(vbus_pdev);
gpiod_remove_lookup_table(&vbus_lookup);
pr_info("vbus_rig: gpio-vbus.0 unregistered\n");
}
module_init(vbus_rig_init);
module_exit(vbus_rig_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("gpio-vbus teardown UAF PoC rig");
Makefile:
obj-m += vbus_rig.o
guest init script (packed into a busybox initramfs with udc-core.ko,
phy-gpio-vbus-usb.ko and vbus_rig.ko):
#!/bin/sh
# PoC init: gpio-vbus teardown re-arm UAF (F-138)
#
# mode=vuln : kernel has the unpatched gpio_vbus_remove() + a 500ms
# POC-WITNESS msleep() after cancel_delayed_work_sync().
# mode=fixed : kernel has the fix (devm_free_irq before the cancel)
# plus the same witness sleep.
#
# A busy toggler flips the gpio-sim line pull (= VBUS edges) while
# rmmod runs the real remove path. In vuln mode an edge lands after
# the cancel, re-arms the 100ms debounce work, and the work fires on
# the freed gpio_vbus_data -> KASAN slab-use-after-free.
#
# The toggler prints nothing; the console between "rmmod starts" and
# the verdict carries only kernel output. If a run comes out clean,
# just run it again (IRQ delivery timing varies).
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs dev /dev
mount -t configfs none /sys/kernel/config
# 1. one-line gpio-sim chip labelled "vbus-sim"
SIM=/sys/kernel/config/gpio-sim/sim0
mkdir -p $SIM/bank0
echo vbus-sim > $SIM/bank0/label
echo 1 > $SIM/bank0/num_lines
echo 1 > $SIM/live
PULL=$(echo /sys/devices/platform/gpio-sim.0/gpiochip*/sim_gpio0/pull)
# 2. load the gadget core, the real driver + rig (probe runs now)
insmod /udc-core.ko || { echo "SETUP FAIL: udc-core"; poweroff -f; }
insmod /phy-gpio-vbus-usb.ko || { echo "SETUP FAIL: phy module"; poweroff -f; }
insmod /vbus_rig.ko || { echo "SETUP FAIL: rig (phy attach broken)"; poweroff -f; }
ls /sys/bus/platform/drivers/gpio-vbus/
grep -i vbus /proc/interrupts || { echo "SETUP FAIL: no vbus irq"; poweroff -f; }
# 3. toggle VBUS edges at max rate, then run the real remove path
( while :; do echo pull-up > $PULL; echo pull-down > $PULL; done ) &
T=$!
sleep 1
echo "=== rmmod vbus_rig: remove path ==="
rmmod vbus_rig
sleep 2
kill $T
# 4. verdict
if dmesg | grep -q 'BUG: KASAN'; then
echo "=== RESULT: KASAN SPLAT (vuln confirmed) ==="
else
echo "=== RESULT: clean (no KASAN splat) ==="
fi
poweroff -f
run.sh (QEMU wrapper; its first argument is only a log label, the
vulnerable/fixed state comes from which witness diff was applied at
build time):
#!/bin/bash
# run.sh -- boot the PoC in QEMU. usage: run.sh <label> [logfile]
#
# NOTE: <label> is only a name for the log; it does NOT switch kernel
# state. The vulnerable/fixed state comes from which witness diff is
# applied to the kernel tree when the modules and initramfs were last
# built (see README.md: vuln = witness-vuln.diff, fixed =
# witness-fixed.diff).
set -o pipefail
cd "$(dirname "$0")"
K=${KDIR:-$HOME/patternmine/worktrees/vm-hdlc}
MODE=${1:-vuln}
LOG=${2:-/tmp/f138-$MODE.log}
timeout 120 qemu-system-x86_64 -enable-kvm -cpu host -smp 2 -m 1024 \
-kernel "$K/arch/x86/boot/bzImage" \
-initrd initramfs.cpio \
-append "console=ttyS0 loglevel=8 panic_on_oops=0" \
-nographic -no-reboot 2>&1 | tee "$LOG"
matrix.sh (repeated runs):
#!/bin/bash
# matrix.sh -- run N VMs with the current build, print the tally.
# usage: matrix.sh <N> <label> (label is only for log naming; the
# vulnerable/fixed state comes from which witness diff was applied at
# build time -- see README.md)
cd "$(dirname "$0")"
N=${1:-5}
MODE=${2:-vuln}
HIT=0
for i in $(seq 1 "$N"); do
./run.sh "$MODE" "/tmp/f138-matrix-$MODE-$i.log" >/dev/null 2>&1
if grep -q 'BUG: KASAN' "/tmp/f138-matrix-$MODE-$i.log"; then
HIT=$((HIT+1))
elif grep -q 'SETUP FAIL' "/tmp/f138-matrix-$MODE-$i.log"; then
echo "run $i: SETUP FAIL - do not count"; continue
fi
echo "run $i: $(grep -m1 'RESULT' "/tmp/f138-matrix-$MODE-$i.log" | sed 's/.*=== //;s/ ===//')"
done
echo "== $MODE: $HIT/$N KASAN splats =="
witness diff applied to the vulnerable kernel (window widening only,
logic unchanged):
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index ce09e789afd8..f3e1c3f3b125 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -332,6 +332,12 @@ static void gpio_vbus_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0);
cancel_delayed_work_sync(&gpio_vbus->work);
+ /* POC-WITNESS: widen the re-arm window so the reproducer's
+ * interrupt lands reliably between the cancel and the devm
+ * release. Not part of the bug; logic unchanged.
+ */
+ msleep(500);
+
usb_remove_phy(&gpio_vbus->phy);
}
and for the patched control run, the fix plus the same window:
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index ce09e789afd8..25e0dcd36b34 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -330,8 +330,15 @@ static void gpio_vbus_remove(struct platform_device *pdev)
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
+
+ devm_free_irq(&pdev->dev, gpio_vbus->irq, pdev);
cancel_delayed_work_sync(&gpio_vbus->work);
+ /* POC-WITNESS: same artificial window as the vuln run, to show
+ * the fix closes it.
+ */
+ msleep(500);
+
usb_remove_phy(&gpio_vbus->phy);
}
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work
2026-11-12 16:04 ` [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work Chengyu Liu
@ 2026-11-12 16:16 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
1 sibling, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-11-12 16:16 UTC (permalink / raw)
To: Chengyu Liu, linux-usb
> gpio_vbus_remove() cancels gpio_vbus->work while the VBUS sensing GPIO
> interrupt is still live: it is devm-managed and freed only at detach,
> and gpio_vbus_irq() schedules the work with a 100 ms debounce delay.
> A plug event landing after the cancel re-arms the work, and its timer
> then fires on the freed gpio_vbus_data.
>
> Free the interrupt first, which also waits for any running handler,
> so nothing can re-arm the work past the cancel.
>
> Found by static analysis and confirmed with a KASAN reproducer:
> slab-use-after-free write in __run_timer_base() within one debounce
> interval of unregistering the device while its VBUS GPIO is toggled
> (object allocated by gpio_vbus_probe() via devm_kmalloc(), freed by
> devres_release_all() at unbind).
>
> Fixes: c2344f13b59e ("USB: gpio_vbus: add delayed vbus_session calls")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261112160448.41566-1-lui2012qwq@gmail.com?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer
2026-11-12 16:04 ` [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer Chengyu Liu
@ 2026-11-12 16:18 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
1 sibling, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-11-12 16:18 UTC (permalink / raw)
To: Chengyu Liu, linux-usb
> r8a66597_remove() deletes r8a66597->timer while the interrupt is
> still live: the IRQ is devm-managed and only freed at detach, and
> r8a66597_irq() re-arms the timer when sampling VBUS (VBINT). An
> interrupt landing after timer_delete_sync() re-arms the timer, and
> r8a66597_timer() then runs on freed memory.
>
> Store the requested IRQ number at probe time and free it before
> deleting the timer; freeing also waits for any running handler, so
> nothing can re-arm the timer past the delete.
>
> Found by static analysis.
>
> Fixes: c41442474a26 ("usb: gadget: R8A66597 peripheral controller support.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261112160448.41566-1-lui2012qwq@gmail.com?part=3
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove()
2026-11-12 16:04 ` [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove() Chengyu Liu
@ 2026-11-12 16:23 ` sashiko-bot
2026-11-12 16:33 ` Chengyu Liu
2026-11-12 19:18 ` Neill Kapron
1 sibling, 1 reply; 14+ messages in thread
From: sashiko-bot @ 2026-11-12 16:23 UTC (permalink / raw)
To: Chengyu Liu, linux-usb
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] usb: musb: inaccurate Fixes trailer in commit message for
musb_remove()
--
commit e08f859cc70a9eed1b7e88c3628f7b1178dcabc5
Author: Chengyu Liu <lui2012qwq@gmail.com>
> usb: musb: cancel irq_work after masking interrupts in musb_remove()
>
> musb_stage0_irq() can schedule irq_work while handling stage-0 USB
> interrupts, and the work can also re-arm itself from the session
> recheck path (musb_pm_runtime_check_session()). musb_remove()
> cancels irq_work while the interrupt is still enabled and the IRQ
> line not yet freed: an interrupt landing after the cancel re-arms
> the work, and musb_irq_work() then runs after musb_free() has
> released the struct.
>
> Move the cancel behind musb_disable_interrupts() and synchronize the
> IRQ line first, so no handler can be running or re-arm the work past
> the cancel.
>
> Found by static analysis.
>
> Fixes: c5340bd14336 ("usb: musb: cancel work on removal")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
[Severity: Low]
In c5340bd14336 ("usb: musb: cancel work on removal"), cancel_work_sync()
was placed in musb_free() immediately after free_irq() and after
musb_shutdown() had disabled interrupts. 66fadea5b79c ("usb: musb: only
cancel work if it is initialized") later moved the cancel into
musb_remove() ahead of free_irq(), and f730f205cc51 ("usb: musb: Split PM
runtime between wrapper IP and musb core") moved it ahead of
musb_disable_interrupts().
Should the Fixes: tag reference f730f205cc51 ("usb: musb: Split PM runtime
between wrapper IP and musb core") instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261112160448.41566-1-lui2012qwq@gmail.com?part=1
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove()
2026-11-12 16:23 ` sashiko-bot
@ 2026-11-12 16:33 ` Chengyu Liu
0 siblings, 0 replies; 14+ messages in thread
From: Chengyu Liu @ 2026-11-12 16:33 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-usb
Thanks, f730f205cc51 is the right one. I'll carry the corrected Fixes
tag if the series needs a respin.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove()
2026-11-12 16:04 ` [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove() Chengyu Liu
2026-11-12 16:23 ` sashiko-bot
@ 2026-11-12 19:18 ` Neill Kapron
1 sibling, 0 replies; 14+ messages in thread
From: Neill Kapron @ 2026-11-12 19:18 UTC (permalink / raw)
To: Chengyu Liu
Cc: linux-usb, b-liu, gregkh, nkapron, tony, bigeasy, balbi,
shimoda.yoshihiro, robert.jarzmik, stable
> musb_stage0_irq() can schedule irq_work while handling stage-0 USB
> interrupts, and the work can also re-arm itself from the session
> recheck path (musb_pm_runtime_check_session()). musb_remove()
> cancels irq_work while the interrupt is still enabled and the IRQ
> line not yet freed: an interrupt landing after the cancel re-arms
> the work, and musb_irq_work() then runs after musb_free() has
> released the struct.
>
> Move the cancel behind musb_disable_interrupts() and synchronize the
> IRQ line first, so no handler can be running or re-arm the work past
> the cancel.
>
> Found by static analysis.
>
> Fixes: c5340bd14336 ("usb: musb: cancel work on removal")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chengyu Liu <lui2012qwq@gmail.com>
> ---
> Build-tested on x86_64: musb_core.o compiles clean
> (USB_MUSB_HDRC). No runtime reproducer: requires MUSB-based OTG
> hardware. Fix written with AI assistance.
>
> drivers/usb/musb/musb_core.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
> index 73ac25f53698..c71d0c7f1bcb 100644
> --- a/drivers/usb/musb/musb_core.c
> +++ b/drivers/usb/musb/musb_core.c
> @@ -2630,7 +2630,6 @@ static void musb_remove(struct platform_device *pdev)
> */
> musb_exit_debugfs(musb);
>
> - cancel_delayed_work_sync(&musb->irq_work);
> cancel_delayed_work_sync(&musb->finish_resume_work);
> cancel_delayed_work_sync(&musb->deassert_reset_work);
Shouldn't all three cancel_delayed_work_sync calls be moved to after
synchronize_irq? Seems like these other two work queues are subject to
similar style issues.
--
Neill Kapron <nkapron@google.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/4] usb: musb: dsps: free the VBUS interrupt before the child teardown
2026-11-12 16:04 ` [PATCH 2/4] usb: musb: dsps: free the VBUS interrupt before the child teardown Chengyu Liu
@ 2026-11-12 19:18 ` Neill Kapron
0 siblings, 0 replies; 14+ messages in thread
From: Neill Kapron @ 2026-11-12 19:18 UTC (permalink / raw)
To: Chengyu Liu
Cc: linux-usb, b-liu, gregkh, nkapron, tony, bigeasy, balbi,
shimoda.yoshihiro, robert.jarzmik, stable
On Fri, 13 Nov 2026 00:04:46 +0800, Chengyu Liu <lui2012qwq@gmail.com> wrote:
> dsps_remove() unregisters the musb child, which deletes
> musb->dev_timer and frees the structure, while the glue's VBUS
> interrupt is devm-managed and only freed after dsps_remove() returns.
> dsps_vbus_threaded_irq() re-arms the timer via dsps_mod_timer(), so
> a VBUS event in between re-arms dev_timer past its deletion and
> otg_timer() runs on freed memory.
>
> [...]
Reviewed-by: Neill Kapron <nkapron@google.com>
--
Neill Kapron <nkapron@google.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer
2026-11-12 16:04 ` [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer Chengyu Liu
2026-11-12 16:18 ` sashiko-bot
@ 2026-11-12 19:18 ` Neill Kapron
1 sibling, 0 replies; 14+ messages in thread
From: Neill Kapron @ 2026-11-12 19:18 UTC (permalink / raw)
To: Chengyu Liu
Cc: linux-usb, b-liu, gregkh, nkapron, tony, bigeasy, balbi,
shimoda.yoshihiro, robert.jarzmik, stable
On Fri, 13 Nov 2026 00:04:47 +0800, Chengyu Liu <lui2012qwq@gmail.com> wrote:
> r8a66597_remove() deletes r8a66597->timer while the interrupt is
> still live: the IRQ is devm-managed and only freed at detach, and
> r8a66597_irq() re-arms the timer when sampling VBUS (VBINT). An
> interrupt landing after timer_delete_sync() re-arms the timer, and
> r8a66597_timer() then runs on freed memory.
>
> Store the requested IRQ number at probe time and free it before
> deleting the timer; freeing also waits for any running handler, so
> nothing can re-arm the timer past the delete.
>
> [...]
Reviewed-by: Neill Kapron <nkapron@google.com>
--
Neill Kapron <nkapron@google.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work
2026-11-12 16:04 ` [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work Chengyu Liu
2026-11-12 16:16 ` sashiko-bot
@ 2026-11-12 19:18 ` Neill Kapron
1 sibling, 0 replies; 14+ messages in thread
From: Neill Kapron @ 2026-11-12 19:18 UTC (permalink / raw)
To: Chengyu Liu
Cc: linux-usb, b-liu, gregkh, nkapron, tony, bigeasy, balbi,
shimoda.yoshihiro, robert.jarzmik, stable
On Fri, 13 Nov 2026 00:04:48 +0800, Chengyu Liu <lui2012qwq@gmail.com> wrote:
> gpio_vbus_remove() cancels gpio_vbus->work while the VBUS sensing GPIO
> interrupt is still live: it is devm-managed and freed only at detach,
> and gpio_vbus_irq() schedules the work with a 100 ms debounce delay.
> A plug event landing after the cancel re-arms the work, and its timer
> then fires on the freed gpio_vbus_data.
>
> Free the interrupt first, which also waits for any running handler,
> so nothing can re-arm the work past the cancel.
>
> [...]
Reviewed-by: Neill Kapron <nkapron@google.com>
--
Neill Kapron <nkapron@google.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-11-12 19:18 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-12 16:04 [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
2026-11-12 16:04 ` [PATCH 1/4] usb: musb: cancel irq_work after masking interrupts in musb_remove() Chengyu Liu
2026-11-12 16:23 ` sashiko-bot
2026-11-12 16:33 ` Chengyu Liu
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:04 ` [PATCH 2/4] usb: musb: dsps: free the VBUS interrupt before the child teardown Chengyu Liu
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:04 ` [PATCH 3/4] usb: gadget: r8a66597: free the IRQ before deleting the timer Chengyu Liu
2026-11-12 16:18 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:04 ` [PATCH 4/4] usb: phy: gpio-vbus: free the interrupt before cancelling the work Chengyu Liu
2026-11-12 16:16 ` sashiko-bot
2026-11-12 19:18 ` Neill Kapron
2026-11-12 16:10 ` [PATCH 0/4] usb: stop timers/works left armed on teardown Chengyu Liu
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.