The GPIO IRQ can queue irq_work_fall and irq_work_rise, and the PCI IRQ can also queue irq_work_fall. Neither work is drained during removal, so its callback can access the gadget driver or controller state after teardown has started, or the device structure after it has been freed. Disable and drain both works before unregistering the gadget. Disabling also prevents the IRQ handlers from requeuing them while their IRQs are still registered. Initialize both works unconditionally before requesting the GPIO IRQ so removal can disable them even without a GPIO IRQ. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: dd63180b758d ("usb: gadget: pch_udc: Detecting VBUS through GPIO") Fixes: 637b78eb31e0 ("usb: gadget: pch_udc: Detecting VBUS through GPIO with interrupt") Cc: stable@vger.kernel.org # 6.10+ Assisted-by: LLM Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak --- drivers/usb/gadget/udc/pch_udc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 99b3ce28210f..20018c09f6f3 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1372,6 +1372,8 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev) dev->vbus_gpio.port = NULL; dev->vbus_gpio.intr = 0; + INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); + INIT_WORK(&dev->vbus_gpio.irq_work_rise, pch_vbus_gpio_work_rise); /* Retrieve the GPIO line from the USB gadget device */ gpiod = devm_gpiod_get_optional(d, NULL, GPIOD_IN); @@ -1380,7 +1382,6 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev) gpiod_set_consumer_name(gpiod, "pch_vbus"); dev->vbus_gpio.port = gpiod; - INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); irq_num = gpiod_to_irq(gpiod); if (irq_num > 0) { @@ -1389,8 +1390,6 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev) "vbus_detect", dev); if (!err) { dev->vbus_gpio.intr = irq_num; - INIT_WORK(&dev->vbus_gpio.irq_work_rise, - pch_vbus_gpio_work_rise); } else { pr_err("%s: can't request irq %d, err: %d\n", __func__, irq_num, err); @@ -2982,6 +2981,9 @@ static void pch_udc_remove(struct pci_dev *pdev) { struct pch_udc_dev *dev = pci_get_drvdata(pdev); + disable_work_sync(&dev->vbus_gpio.irq_work_fall); + disable_work_sync(&dev->vbus_gpio.irq_work_rise); + usb_del_gadget_udc(&dev->gadget); /* gadget driver must not be registered */ base-commit: df2908090cda368b01ff43709f51890076c56157 -- 2.47.1