type_c_detect_irq() can cancel and reschedule delayed_work. The remove callback currently cancels the work before disabling and freeing the IRQ, so an interrupt can reschedule it after the final drain. The device-managed type_c allocation may then be released while the work is still pending. In three ARM64 KASAN runs, dispatching the real IRQ after the final drain made the delayed callback access the freed type_c object. All three runs with the IRQ stopped before the drain completed without a kernel diagnostic. Mask the hardware interrupt source first, release and synchronize the IRQ, and only then cancel the delayed work. Also use devm_free_irq() to match the devm_request_irq() allocation and remove its devres entry. Fixes: 8a590d7371f0 ("extcon: add Realtek DHC RTD SoC Type-C driver") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Yibo Tan --- drivers/extcon/extcon-rtk-type-c.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c index 82b60b927e41a..9d77ce5c7ee50 100644 --- a/drivers/extcon/extcon-rtk-type-c.c +++ b/drivers/extcon/extcon-rtk-type-c.c @@ -1409,17 +1409,6 @@ static void extcon_rtk_type_c_remove(struct platform_device *pdev) u32 default_ctrl; unsigned long flags; - remove_debug_files(type_c); - - if (type_c->port) { - typec_unregister_port(type_c->port); - type_c->port = NULL; - } - - cancel_delayed_work_sync(&type_c->delayed_work); - flush_delayed_work(&type_c->delayed_work); - WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); - spin_lock_irqsave(&type_c->lock, flags); /* disable interrupt */ default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) & @@ -1431,12 +1420,22 @@ static void extcon_rtk_type_c_remove(struct platform_device *pdev) writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0); spin_unlock_irqrestore(&type_c->lock, flags); + devm_free_irq(dev, type_c->irq, type_c); + + cancel_delayed_work_sync(&type_c->delayed_work); + flush_delayed_work(&type_c->delayed_work); + WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work)); + + remove_debug_files(type_c); + + if (type_c->port) { + typec_unregister_port(type_c->port); + type_c->port = NULL; + } if (type_c->rd_ctrl_gpio_desc) gpiod_put(type_c->rd_ctrl_gpio_desc); type_c->rd_ctrl_gpio_desc = NULL; - - free_irq(type_c->irq, type_c); } static const struct type_c_cfg rtd1295_type_c_cfg = { base-commit: 40288c9206c17eb66a603262e06a58d300d0f279 -- 2.39.5