public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] usb: gadget: f_uvc: fix NULL pointer dereference during unbind race
@ 2026-02-24  8:39 Jimmy Hu
  2026-02-24 15:46 ` Alan Stern
  2026-03-09  5:31 ` [PATCH v2] " Jimmy Hu
  0 siblings, 2 replies; 7+ messages in thread
From: Jimmy Hu @ 2026-02-24  8:39 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Laurent Pinchart
  Cc: Dan Vacura, Jimmy Hu, Xu Yang, Frank Li, Hans Verkuil, linux-usb,
	linux-kernel, stable, badhri

Commit b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly
shutdown") introduced two stages of synchronization waits totaling 1500ms
in uvc_function_unbind() to prevent several types of kernel panics.
However, this timing-based approach is insufficient during power
management (PM) transitions.

When the PM subsystem starts freezing user space processes, the
wait_event_interruptible_timeout() is aborted early, which allows the
unbind thread to proceed and nullify the gadget pointer
(cdev->gadget = NULL):

[  814.123447][  T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind()
[  814.178583][ T3173] PM: suspend entry (deep)
[  814.192487][ T3173] Freezing user space processes
[  814.197668][  T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind no clean disconnect, wait for release

When the PM subsystem resumes or aborts the suspend and tasks are
restarted, the V4L2 release path is executed and attempts to access the
already nullified gadget pointer, triggering a kernel panic:

[  814.292597][    C0] PM: pm_system_irq_wakeup: 479 triggered dhdpcie_host_wake
[  814.386727][ T3173] Restarting tasks ...
[  814.403522][ T4558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030
[  814.404021][ T4558] pc : usb_gadget_deactivate+0x14/0xf4
[  814.404031][ T4558] lr : usb_function_deactivate+0x54/0x94
[  814.404078][ T4558] Call trace:
[  814.404080][ T4558]  usb_gadget_deactivate+0x14/0xf4
[  814.404083][ T4558]  usb_function_deactivate+0x54/0x94
[  814.404087][ T4558]  uvc_function_disconnect+0x1c/0x5c
[  814.404092][ T4558]  uvc_v4l2_release+0x44/0xac
[  814.404095][ T4558]  v4l2_release+0xcc/0x130

The fix introduces a 'func_unbinding' flag in struct uvc_device to protect
critical sections:
1. In uvc_function_disconnect(), it prevents accessing the nullified
   cdev->gadget pointer.
2. In uvc_v4l2_release(), it ensures uvcg_free_buffers() is skipped
   if unbind is already in progress, avoiding races with concurrent
   bind operations or use-after-free on the video queue memory.

Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown")
Cc: <stable@vger.kernel.org>
Signed-off-by: Jimmy Hu <hhhuuu@google.com>
---
 drivers/usb/gadget/function/f_uvc.c    | 7 +++++++
 drivers/usb/gadget/function/uvc.h      | 1 +
 drivers/usb/gadget/function/uvc_v4l2.c | 6 ++++++
 3 files changed, 14 insertions(+)

diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index a96476507d2f..f8c609ad1a43 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -413,6 +413,11 @@ uvc_function_disconnect(struct uvc_device *uvc)
 {
 	int ret;
 
+	if (uvc->func_unbinding) {
+		pr_info("uvc: unbinding, skipping function deactivate\n");
+		return;
+	}
+
 	if ((ret = usb_function_deactivate(&uvc->func)) < 0)
 		uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
 }
@@ -659,6 +664,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 	int ret = -EINVAL;
 
 	uvcg_info(f, "%s()\n", __func__);
+	uvc->func_unbinding = false;
 
 	opts = fi_to_f_uvc_opts(f->fi);
 	/* Sanity check the streaming endpoint module parameters. */
@@ -994,6 +1000,7 @@ static void uvc_function_unbind(struct usb_configuration *c,
 	long wait_ret = 1;
 
 	uvcg_info(f, "%s()\n", __func__);
+	uvc->func_unbinding = true;
 
 	kthread_cancel_work_sync(&video->hw_submit);
 
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 676419a04976..7ca56ff737a4 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -155,6 +155,7 @@ struct uvc_device {
 	enum uvc_state state;
 	struct usb_function func;
 	struct uvc_video video;
+	bool func_unbinding;
 	bool func_connected;
 	wait_queue_head_t func_connected_queue;
 
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index fd4b998ccd16..a8a15b584648 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -594,7 +594,13 @@ static void uvc_v4l2_disable(struct uvc_device *uvc)
 {
 	uvc_function_disconnect(uvc);
 	uvcg_video_disable(&uvc->video);
+	if (uvc->func_unbinding) {
+		pr_info("uvc: unbinding, skipping buffer cleanup\n");
+		goto skip_buffer_cleanup;
+	}
 	uvcg_free_buffers(&uvc->video.queue);
+
+skip_buffer_cleanup:
 	uvc->func_connected = false;
 	wake_up_interruptible(&uvc->func_connected_queue);
 }

base-commit: 24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7
-- 
2.53.0.371.g1d285c8824-goog


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

end of thread, other threads:[~2026-03-19  8:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-24  8:39 [PATCH] usb: gadget: f_uvc: fix NULL pointer dereference during unbind race Jimmy Hu
2026-02-24 15:46 ` Alan Stern
2026-02-25  2:31   ` Jimmy Hu (xWF)
2026-02-25 15:38     ` Alan Stern
2026-03-09  5:31 ` [PATCH v2] " Jimmy Hu
2026-03-11 12:46   ` Greg Kroah-Hartman
2026-03-19  8:53     ` Jimmy Hu (xWF)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox