FAILED: patch "[PATCH] usb: gadget: udc: fix use-after-free in usb_gadget_state_work" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x baeb66fbd4201d1c4325074e78b1f557dff89b5b
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025120123-borax-cut-5c52@gregkh' --subject-prefix 'PATCH 5.15.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From baeb66fbd4201d1c4325074e78b1f557dff89b5b Mon Sep 17 00:00:00 2001
From: Jimmy Hu <[email protected]>
Date: Thu, 23 Oct 2025 05:49:45 +0000
Subject: [PATCH] usb: gadget: udc: fix use-after-free in usb_gadget_state_work
A race condition during gadget teardown can lead to a use-after-free in usb_gadget_state_work(), as reported by KASAN:
BUG: KASAN: invalid-access in sysfs_notify+0x2c/0xd0 Workqueue: events usb_gadget_state_work
The fundamental race occurs because a concurrent event (e.g., an interrupt) can call usb_gadget_set_state() and schedule gadget->work at any time during the cleanup process in usb_del_gadget().
Commit 399a45e5237c ("usb: gadget: core: flush gadget workqueue after device removal") attempted to fix this by moving flush_work() to after device_del(). However, this does not fully solve the race, as a new work item can still be scheduled *after* flush_work() completes but before the gadget's memory is freed, leading to the same use-after-free.
This patch fixes the race condition robustly by introducing a 'teardown' flag and a 'state_lock' spinlock to the usb_gadget struct. The flag is set during cleanup in usb_del_gadget() *before* calling flush_work() to prevent any new work from being scheduled once cleanup has commenced. The scheduling site, usb_gadget_set_state(), now checks this flag under the lock before queueing the work, thus safely closing the race window.
drivers/usb/gadget/udc/core.c +16 -1
@@ -1126,8 +1126,13 @@ static void usb_gadget_state_work(struct work_struct *work)void usb_gadget_set_state (struct usb_gadget *gadget,enum usb_device_state state) { +unsigned long flags; + +spin_lock_irqsave (&gadget->state_lock, flags); gadget->state = state; -schedule_work (&gadget->work); +if (!gadget->teardown) +schedule_work (&gadget->work); +spin_unlock_irqrestore (&gadget->state_lock, flags);trace_usb_gadget_set_state (gadget,0 ); }EXPORT_SYMBOL_GPL (usb_gadget_set_state);
@@ -1361,6 +1366,8 @@ static void usb_udc_nop_release(struct device *dev)void usb_initialize_gadget (struct device *parent,struct usb_gadget *gadget,void (*release)(struct device *dev)) { +spin_lock_init (&gadget->state_lock); + gadget->teardown = false;INIT_WORK (&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent;
@@ -1535,6 +1542,7 @@ EXPORT_SYMBOL_GPL(usb_add_gadget_udc);void usb_del_gadget (struct usb_gadget *gadget) {struct usb_udc *udc = gadget->udc; +unsigned long flags;if (!udc)return ;
@@ -1548,6 +1556,13 @@ void usb_del_gadget(struct usb_gadget *gadget)kobject_uevent (&udc->dev.kobj,KOBJ_REMOVE );sysfs_remove_link (&udc->dev.kobj,"gadget" );device_del (&gadget->dev); +/* +* Set the teardown flag before flushing the work to prevent new work +* from being scheduled while we are cleaning up. +*/ +spin_lock_irqsave (&gadget->state_lock, flags); + gadget->teardown = true; +spin_unlock_irqrestore (&gadget->state_lock, flags);flush_work (&gadget->work);ida_free (&gadget_id_numbers, gadget->id_number);cancel_work_sync (&udc->vbus_work);
include/linux/usb/gadget.h +5 -0
@@ -376,6 +376,9 @@ struct usb_gadget_ops {* can handle. The UDC must support this and all slower speeds and lower * number of lanes. * @state: the state we are now (attached, suspended, configured, etc) +* @state_lock: Spinlock protecting the `state` and `teardown` members. +* @teardown: True if the device is undergoing teardown, used to prevent +* new work from being scheduled during cleanup. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device.
@@ -451,6 +454,8 @@ struct usb_gadget {enum usb_ssp_rate max_ssp_rate;enum usb_device_state state; +spinlock_t state_lock; +bool teardown;const char *name;struct device dev;unsigned isoch_delay;
Sasha Levin
maintainerPATCH 1/1 usb: gadget: udc: fix use-after-free in usb_gadget_state_work
From: Jimmy Hu <[email protected]>
[ Upstream commit baeb66fbd4201d1c4325074e78b1f557dff89b5b ]
A race condition during gadget teardown can lead to a use-after-free in usb_gadget_state_work(), as reported by KASAN:
BUG: KASAN: invalid-access in sysfs_notify+0x2c/0xd0 Workqueue: events usb_gadget_state_work
The fundamental race occurs because a concurrent event (e.g., an interrupt) can call usb_gadget_set_state() and schedule gadget->work at any time during the cleanup process in usb_del_gadget().
Commit 399a45e5237c ("usb: gadget: core: flush gadget workqueue after device removal") attempted to fix this by moving flush_work() to after device_del(). However, this does not fully solve the race, as a new work item can still be scheduled *after* flush_work() completes but before the gadget's memory is freed, leading to the same use-after-free.
This patch fixes the race condition robustly by introducing a 'teardown' flag and a 'state_lock' spinlock to the usb_gadget struct. The flag is set during cleanup in usb_del_gadget() *before* calling flush_work() to prevent any new work from being scheduled once cleanup has commenced. The scheduling site, usb_gadget_set_state(), now checks this flag under the lock before queueing the work, thus safely closing the race window.
--- drivers/usb/gadget/udc/core.c | 17 ++++++++++++++++- include/linux/usb/gadget.h | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-)
drivers/usb/gadget/udc/core.c +16 -1
@@ -1066,8 +1066,13 @@ static void usb_gadget_state_work(struct work_struct *work)void usb_gadget_set_state (struct usb_gadget *gadget,enum usb_device_state state) { +unsigned long flags; + +spin_lock_irqsave (&gadget->state_lock, flags); gadget->state = state; -schedule_work (&gadget->work); +if (!gadget->teardown) +schedule_work (&gadget->work); +spin_unlock_irqrestore (&gadget->state_lock, flags); }EXPORT_SYMBOL_GPL (usb_gadget_set_state);
@@ -1298,6 +1303,8 @@ void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) {dev_set_name (&gadget->dev,"gadget" ); +spin_lock_init (&gadget->state_lock); + gadget->teardown = false;INIT_WORK (&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent;
@@ -1478,6 +1485,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)void usb_del_gadget (struct usb_gadget *gadget) {struct usb_udc *udc = gadget->udc; +unsigned long flags;if (!udc)return ;
@@ -1496,6 +1504,13 @@ void usb_del_gadget(struct usb_gadget *gadget)mutex_unlock (&udc_lock);kobject_uevent (&udc->dev.kobj,KOBJ_REMOVE ); +/* +* Set the teardown flag before flushing the work to prevent new work +* from being scheduled while we are cleaning up. +*/ +spin_lock_irqsave (&gadget->state_lock, flags); + gadget->teardown = true; +spin_unlock_irqrestore (&gadget->state_lock, flags);flush_work (&gadget->work);device_unregister (&udc->dev);device_del (&gadget->dev);
include/linux/usb/gadget.h +5 -0
@@ -375,6 +375,9 @@ struct usb_gadget_ops {* can handle. The UDC must support this and all slower speeds and lower * number of lanes. * @state: the state we are now (attached, suspended, configured, etc) +* @state_lock: Spinlock protecting the `state` and `teardown` members. +* @teardown: True if the device is undergoing teardown, used to prevent +* new work from being scheduled during cleanup. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device.
@@ -449,6 +452,8 @@ struct usb_gadget {enum usb_ssp_rate max_ssp_rate;enum usb_device_state state; +spinlock_t state_lock; +bool teardown;const char *name;struct device dev;unsigned isoch_delay;
Signature · 1 line
2.51.0