]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 8 Apr 2026 12:12:41 +0000 (14:12 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 8 Apr 2026 12:12:41 +0000 (14:12 +0200)
added patches:
usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch
usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch
usb-gadget-f_uac1_legacy-validate-control-request-size.patch
usb-gadget-u_ether-fix-race-between-gether_disconnect-and-eth_stop.patch
usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch

queue-6.6/series
queue-6.6/usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch [new file with mode: 0644]
queue-6.6/usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch [new file with mode: 0644]
queue-6.6/usb-gadget-f_uac1_legacy-validate-control-request-size.patch [new file with mode: 0644]
queue-6.6/usb-gadget-u_ether-fix-race-between-gether_disconnect-and-eth_stop.patch [new file with mode: 0644]
queue-6.6/usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch [new file with mode: 0644]

index 7f9b32cca694971ab5a7671ef9bf88617d79c8e5..dc3cd76e74c4392a6447f333f7a028788277e33f 100644 (file)
@@ -139,3 +139,8 @@ btrfs-do-not-free-data-reservation-in-fallback-from-.patch
 gfs2-improve-gfs2_consist_inode-usage.patch
 gfs2-validate-i_depth-for-exhash-directories.patch
 loongarch-vdso-emit-gnu_eh_frame-correctly.patch
+usb-gadget-u_ether-fix-race-between-gether_disconnect-and-eth_stop.patch
+usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch
+usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch
+usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch
+usb-gadget-f_uac1_legacy-validate-control-request-size.patch
diff --git a/queue-6.6/usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch b/queue-6.6/usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch
new file mode 100644 (file)
index 0000000..623324b
--- /dev/null
@@ -0,0 +1,49 @@
+From 8d8c68b1fc06ece60cf43e1306ff0f4ac121547e Mon Sep 17 00:00:00 2001
+From: Kuen-Han Tsai <khtsai@google.com>
+Date: Fri, 20 Mar 2026 16:54:45 +0800
+Subject: usb: gadget: f_rndis: Protect RNDIS options with mutex
+
+From: Kuen-Han Tsai <khtsai@google.com>
+
+commit 8d8c68b1fc06ece60cf43e1306ff0f4ac121547e upstream.
+
+The class/subclass/protocol options are suspectible to race conditions
+as they can be accessed concurrently through configfs.
+
+Use existing mutex to protect these options. This issue was identified
+during code inspection.
+
+Fixes: 73517cf49bd4 ("usb: gadget: add RNDIS configfs options for class/subclass/protocol")
+Cc: stable@vger.kernel.org
+Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
+Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-2-4886b578161b@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_rndis.c |    9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_rndis.c
++++ b/drivers/usb/gadget/function/f_rndis.c
+@@ -11,6 +11,7 @@
+ /* #define VERBOSE_DEBUG */
++#include <linux/cleanup.h>
+ #include <linux/slab.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+@@ -678,9 +679,11 @@ rndis_bind(struct usb_configuration *c,
+                       return -ENOMEM;
+       }
+-      rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
+-      rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
+-      rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
++      scoped_guard(mutex, &rndis_opts->lock) {
++              rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
++              rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
++              rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
++      }
+       /*
+        * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
diff --git a/queue-6.6/usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch b/queue-6.6/usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch
new file mode 100644 (file)
index 0000000..deb2b5e
--- /dev/null
@@ -0,0 +1,48 @@
+From caa27923aacd8a5869207842f2ab1657c6c0c7bc Mon Sep 17 00:00:00 2001
+From: Kuen-Han Tsai <khtsai@google.com>
+Date: Fri, 20 Mar 2026 16:54:44 +0800
+Subject: usb: gadget: f_subset: Fix unbalanced refcnt in geth_free
+
+From: Kuen-Han Tsai <khtsai@google.com>
+
+commit caa27923aacd8a5869207842f2ab1657c6c0c7bc upstream.
+
+geth_alloc() increments the reference count, but geth_free() fails to
+decrement it. This prevents the configuration of attributes via configfs
+after unlinking the function.
+
+Decrement the reference count in geth_free() to ensure proper cleanup.
+
+Fixes: 02832e56f88a ("usb: gadget: f_subset: add configfs support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
+Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-1-4886b578161b@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_subset.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/usb/gadget/function/f_subset.c
++++ b/drivers/usb/gadget/function/f_subset.c
+@@ -6,6 +6,7 @@
+  * Copyright (C) 2008 Nokia Corporation
+  */
++#include <linux/cleanup.h>
+ #include <linux/slab.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+@@ -449,8 +450,13 @@ static struct usb_function_instance *get
+ static void geth_free(struct usb_function *f)
+ {
+       struct f_gether *eth;
++      struct f_gether_opts *opts;
++
++      opts = container_of(f->fi, struct f_gether_opts, func_inst);
+       eth = func_to_geth(f);
++      scoped_guard(mutex, &opts->lock)
++              opts->refcnt--;
+       kfree(eth);
+ }
diff --git a/queue-6.6/usb-gadget-f_uac1_legacy-validate-control-request-size.patch b/queue-6.6/usb-gadget-f_uac1_legacy-validate-control-request-size.patch
new file mode 100644 (file)
index 0000000..a87e0d1
--- /dev/null
@@ -0,0 +1,92 @@
+From 6e0e34d85cd46ceb37d16054e97a373a32770f6c Mon Sep 17 00:00:00 2001
+From: Taegu Ha <hataegu0826@gmail.com>
+Date: Thu, 2 Apr 2026 04:13:11 +0900
+Subject: usb: gadget: f_uac1_legacy: validate control request size
+
+From: Taegu Ha <hataegu0826@gmail.com>
+
+commit 6e0e34d85cd46ceb37d16054e97a373a32770f6c upstream.
+
+f_audio_complete() copies req->length bytes into a 4-byte stack
+variable:
+
+  u32 data = 0;
+  memcpy(&data, req->buf, req->length);
+
+req->length is derived from the host-controlled USB request path,
+which can lead to a stack out-of-bounds write.
+
+Validate req->actual against the expected payload size for the
+supported control selectors and decode only the expected amount
+of data.
+
+This avoids copying a host-influenced length into a fixed-size
+stack object.
+
+Signed-off-by: Taegu Ha <hataegu0826@gmail.com>
+Cc: stable <stable@kernel.org>
+Link: https://patch.msgid.link/20260401191311.3604898-1-hataegu0826@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_uac1_legacy.c |   47 ++++++++++++++++++++++------
+ 1 file changed, 37 insertions(+), 10 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_uac1_legacy.c
++++ b/drivers/usb/gadget/function/f_uac1_legacy.c
+@@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struc
+ static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
+ {
+       struct f_audio *audio = req->context;
+-      int status = req->status;
+-      u32 data = 0;
+       struct usb_ep *out_ep = audio->out_ep;
+-      switch (status) {
+-
+-      case 0:                         /* normal completion? */
+-              if (ep == out_ep)
++      switch (req->status) {
++      case 0:
++              if (ep == out_ep) {
+                       f_audio_out_ep_complete(ep, req);
+-              else if (audio->set_con) {
+-                      memcpy(&data, req->buf, req->length);
+-                      audio->set_con->set(audio->set_con, audio->set_cmd,
+-                                      le16_to_cpu(data));
++              } else if (audio->set_con) {
++                      struct usb_audio_control *con = audio->set_con;
++                      u8 type = con->type;
++                      u32 data;
++                      bool valid_request = false;
++
++                      switch (type) {
++                      case UAC_FU_MUTE: {
++                              u8 value;
++
++                              if (req->actual == sizeof(value)) {
++                                      memcpy(&value, req->buf, sizeof(value));
++                                      data = value;
++                                      valid_request = true;
++                              }
++                              break;
++                      }
++                      case UAC_FU_VOLUME: {
++                              __le16 value;
++
++                              if (req->actual == sizeof(value)) {
++                                      memcpy(&value, req->buf, sizeof(value));
++                                      data = le16_to_cpu(value);
++                                      valid_request = true;
++                              }
++                              break;
++                      }
++                      }
++
++                      if (valid_request)
++                              con->set(con, audio->set_cmd, data);
++                      else
++                              usb_ep_set_halt(ep);
++
+                       audio->set_con = NULL;
+               }
+               break;
diff --git a/queue-6.6/usb-gadget-u_ether-fix-race-between-gether_disconnect-and-eth_stop.patch b/queue-6.6/usb-gadget-u_ether-fix-race-between-gether_disconnect-and-eth_stop.patch
new file mode 100644 (file)
index 0000000..c0bf46b
--- /dev/null
@@ -0,0 +1,77 @@
+From e1eabb072c75681f78312c484ccfffb7430f206e Mon Sep 17 00:00:00 2001
+From: Kuen-Han Tsai <khtsai@google.com>
+Date: Wed, 11 Mar 2026 17:12:15 +0800
+Subject: usb: gadget: u_ether: Fix race between gether_disconnect and eth_stop
+
+From: Kuen-Han Tsai <khtsai@google.com>
+
+commit e1eabb072c75681f78312c484ccfffb7430f206e upstream.
+
+A race condition between gether_disconnect() and eth_stop() leads to a
+NULL pointer dereference. Specifically, if eth_stop() is triggered
+concurrently while gether_disconnect() is tearing down the endpoints,
+eth_stop() attempts to access the cleared endpoint descriptor, causing
+the following NPE:
+
+  Unable to handle kernel NULL pointer dereference
+  Call trace:
+   __dwc3_gadget_ep_enable+0x60/0x788
+   dwc3_gadget_ep_enable+0x70/0xe4
+   usb_ep_enable+0x60/0x15c
+   eth_stop+0xb8/0x108
+
+Because eth_stop() crashes while holding the dev->lock, the thread
+running gether_disconnect() fails to acquire the same lock and spins
+forever, resulting in a hardlockup:
+
+  Core - Debugging Information for Hardlockup core(7)
+  Call trace:
+   queued_spin_lock_slowpath+0x94/0x488
+   _raw_spin_lock+0x64/0x6c
+   gether_disconnect+0x19c/0x1e8
+   ncm_set_alt+0x68/0x1a0
+   composite_setup+0x6a0/0xc50
+
+The root cause is that the clearing of dev->port_usb in
+gether_disconnect() is delayed until the end of the function.
+
+Move the clearing of dev->port_usb to the very beginning of
+gether_disconnect() while holding dev->lock. This cuts off the link
+immediately, ensuring eth_stop() will see dev->port_usb as NULL and
+safely bail out.
+
+Fixes: 2b3d942c4878 ("usb ethernet gadget: split out network core")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
+Link: https://patch.msgid.link/20260311-gether-disconnect-npe-v1-1-454966adf7c7@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/u_ether.c |   10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/gadget/function/u_ether.c
++++ b/drivers/usb/gadget/function/u_ether.c
+@@ -1200,6 +1200,11 @@ void gether_disconnect(struct gether *li
+       DBG(dev, "%s\n", __func__);
++      spin_lock(&dev->lock);
++      dev->port_usb = NULL;
++      link->is_suspend = false;
++      spin_unlock(&dev->lock);
++
+       netif_stop_queue(dev->net);
+       netif_carrier_off(dev->net);
+@@ -1237,11 +1242,6 @@ void gether_disconnect(struct gether *li
+       dev->header_len = 0;
+       dev->unwrap = NULL;
+       dev->wrap = NULL;
+-
+-      spin_lock(&dev->lock);
+-      dev->port_usb = NULL;
+-      link->is_suspend = false;
+-      spin_unlock(&dev->lock);
+ }
+ EXPORT_SYMBOL_GPL(gether_disconnect);
diff --git a/queue-6.6/usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch b/queue-6.6/usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch
new file mode 100644 (file)
index 0000000..2dd7238
--- /dev/null
@@ -0,0 +1,213 @@
+From eba2936bbe6b752a31725a9eb5c674ecbf21ee7d Mon Sep 17 00:00:00 2001
+From: Jimmy Hu <hhhuuu@google.com>
+Date: Fri, 20 Mar 2026 14:54:27 +0800
+Subject: usb: gadget: uvc: fix NULL pointer dereference during unbind race
+
+From: Jimmy Hu <hhhuuu@google.com>
+
+commit eba2936bbe6b752a31725a9eb5c674ecbf21ee7d upstream.
+
+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
+
+Address the race condition and NULL pointer dereference by:
+
+1. State Synchronization (flag + mutex)
+Introduce a 'func_unbound' flag in struct uvc_device. This allows
+uvc_function_disconnect() to safely skip accessing the nullified
+cdev->gadget pointer. As suggested by Alan Stern, this flag is protected
+by a new mutex (uvc->lock) to ensure proper memory ordering and prevent
+instruction reordering or speculative loads. This mutex is also used to
+protect 'func_connected' for consistent state management.
+
+2. Explicit Synchronization (completion)
+Use a completion to synchronize uvc_function_unbind() with the
+uvc_vdev_release() callback. This prevents Use-After-Free (UAF) by
+ensuring struct uvc_device is freed after all video device resources
+are released.
+
+Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown")
+Cc: stable <stable@kernel.org>
+Suggested-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Jimmy Hu <hhhuuu@google.com>
+Link: https://patch.msgid.link/20260320065427.1374555-1-hhhuuu@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_uvc.c    |   39 ++++++++++++++++++++++++++++++---
+ drivers/usb/gadget/function/uvc.h      |    3 ++
+ drivers/usb/gadget/function/uvc_v4l2.c |    5 +++-
+ 3 files changed, 43 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_uvc.c
++++ b/drivers/usb/gadget/function/f_uvc.c
+@@ -410,6 +410,12 @@ uvc_function_disconnect(struct uvc_devic
+ {
+       int ret;
++      guard(mutex)(&uvc->lock);
++      if (uvc->func_unbound) {
++              dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n");
++              return;
++      }
++
+       if ((ret = usb_function_deactivate(&uvc->func)) < 0)
+               uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
+ }
+@@ -428,6 +434,15 @@ static ssize_t function_name_show(struct
+ static DEVICE_ATTR_RO(function_name);
++static void uvc_vdev_release(struct video_device *vdev)
++{
++      struct uvc_device *uvc = video_get_drvdata(vdev);
++
++      /* Signal uvc_function_unbind() that the video device has been released */
++      if (uvc->vdev_release_done)
++              complete(uvc->vdev_release_done);
++}
++
+ static int
+ uvc_register_video(struct uvc_device *uvc)
+ {
+@@ -440,7 +455,7 @@ uvc_register_video(struct uvc_device *uv
+       uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
+       uvc->vdev.fops = &uvc_v4l2_fops;
+       uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
+-      uvc->vdev.release = video_device_release_empty;
++      uvc->vdev.release = uvc_vdev_release;
+       uvc->vdev.vfl_dir = VFL_DIR_TX;
+       uvc->vdev.lock = &uvc->video.mutex;
+       uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+@@ -653,6 +668,8 @@ uvc_function_bind(struct usb_configurati
+       int ret = -EINVAL;
+       uvcg_info(f, "%s()\n", __func__);
++      scoped_guard(mutex, &uvc->lock)
++              uvc->func_unbound = false;
+       opts = fi_to_f_uvc_opts(f->fi);
+       /* Sanity check the streaming endpoint module parameters. */
+@@ -975,12 +992,19 @@ static void uvc_free(struct usb_function
+ static void uvc_function_unbind(struct usb_configuration *c,
+                               struct usb_function *f)
+ {
++      DECLARE_COMPLETION_ONSTACK(vdev_release_done);
+       struct usb_composite_dev *cdev = c->cdev;
+       struct uvc_device *uvc = to_uvc(f);
+       struct uvc_video *video = &uvc->video;
+       long wait_ret = 1;
++      bool connected;
+       uvcg_info(f, "%s()\n", __func__);
++      scoped_guard(mutex, &uvc->lock) {
++              uvc->func_unbound = true;
++              uvc->vdev_release_done = &vdev_release_done;
++              connected = uvc->func_connected;
++      }
+       if (video->async_wq)
+               destroy_workqueue(video->async_wq);
+@@ -991,7 +1015,7 @@ static void uvc_function_unbind(struct u
+        * though the video device removal uevent. Allow some time for the
+        * application to close out before things get deleted.
+        */
+-      if (uvc->func_connected) {
++      if (connected) {
+               uvcg_dbg(f, "waiting for clean disconnect\n");
+               wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
+                               uvc->func_connected == false, msecs_to_jiffies(500));
+@@ -1002,7 +1026,10 @@ static void uvc_function_unbind(struct u
+       video_unregister_device(&uvc->vdev);
+       v4l2_device_unregister(&uvc->v4l2_dev);
+-      if (uvc->func_connected) {
++      scoped_guard(mutex, &uvc->lock)
++              connected = uvc->func_connected;
++
++      if (connected) {
+               /*
+                * Wait for the release to occur to ensure there are no longer any
+                * pending operations that may cause panics when resources are cleaned
+@@ -1014,6 +1041,10 @@ static void uvc_function_unbind(struct u
+               uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
+       }
++      /* Wait for the video device to be released */
++      wait_for_completion(&vdev_release_done);
++      uvc->vdev_release_done = NULL;
++
+       usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
+       kfree(uvc->control_buf);
+@@ -1032,6 +1063,8 @@ static struct usb_function *uvc_alloc(st
+               return ERR_PTR(-ENOMEM);
+       mutex_init(&uvc->video.mutex);
++      mutex_init(&uvc->lock);
++      uvc->func_unbound = true;
+       uvc->state = UVC_STATE_DISCONNECTED;
+       init_waitqueue_head(&uvc->func_connected_queue);
+       opts = fi_to_f_uvc_opts(fi);
+--- a/drivers/usb/gadget/function/uvc.h
++++ b/drivers/usb/gadget/function/uvc.h
+@@ -131,6 +131,9 @@ struct uvc_device {
+       enum uvc_state state;
+       struct usb_function func;
+       struct uvc_video video;
++      struct completion *vdev_release_done;
++      struct mutex lock;      /* protects func_unbound and func_connected */
++      bool func_unbound;
+       bool func_connected;
+       wait_queue_head_t func_connected_queue;
+--- a/drivers/usb/gadget/function/uvc_v4l2.c
++++ b/drivers/usb/gadget/function/uvc_v4l2.c
+@@ -491,6 +491,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh
+       if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
+               return -EINVAL;
++      guard(mutex)(&uvc->lock);
++
+       if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
+               return -EBUSY;
+@@ -512,7 +514,8 @@ static void uvc_v4l2_disable(struct uvc_
+       uvc_function_disconnect(uvc);
+       uvcg_video_enable(&uvc->video, 0);
+       uvcg_free_buffers(&uvc->video.queue);
+-      uvc->func_connected = false;
++      scoped_guard(mutex, &uvc->lock)
++              uvc->func_connected = false;
+       wake_up_interruptible(&uvc->func_connected_queue);
+ }